Skip to content

Instantly share code, notes, and snippets.

@SlyDave
Last active April 29, 2020 20:51
Show Gist options
  • Save SlyDave/7b4cba791b09e5144dfce3d17614392b to your computer and use it in GitHub Desktop.
Save SlyDave/7b4cba791b09e5144dfce3d17614392b to your computer and use it in GitHub Desktop.
Example of using dynamic relationships in Laravel based on parent row condition with ordering and default on null match
<?php
$assignments = UserVesselAssignment::withDetails()->get();
<?php
class UserVesselAssignment extends Model
{
use Compoships;
protected $fillable = [
'starts_at',
'ends_at',
];
protected $dates = [
'starts_at',
'ends_at',
];
/**
* Select the correct details for the assignment, that is, the latest details within the assignment period or ... TODO
* @param Builder $query
*/
public function scopeWithDetails(Builder $query)
{
$select = UserVesselDetails::select('id')->whereColumn('user_vessel_assignments.ends_at', '>=', 'user_vessel_details.created_at')
->whereColumn('user_vessel_assignments.user_id', 'user_vessel_details.user_id')
->whereColumn('user_vessel_assignments.vessel_id', 'user_vessel_details.vessel_id')
->orderBy('user_vessel_details.created_at', 'DESC')->limit(1)->toSql();
$null = UserVesselDetails::select('id')->whereColumn('user_vessel_assignments.starts_at', '<=', 'user_vessel_details.created_at')
->whereColumn('user_vessel_assignments.user_id', 'user_vessel_details.user_id')
->whereColumn('user_vessel_assignments.vessel_id', 'user_vessel_details.vessel_id')
->orderBy('user_vessel_details.created_at', 'ASC')->limit(1)->toSql();
$query->select()
->addSelect(DB::raw("COALESCE(({$select}), ({$null})) AS user_vessel_details_id"))
->with('details');
}
/**
* @return BelongsTo
*
* Only use this in conjunction with @scopeWithDetails as there isn't actually a user_vessel_detail_id on the Assignment to make the BelongsTo work
*/
public function details(): BelongsTo
{
return $this->belongsTo(UserVesselDetails::class, ['vessel_id', 'user_id'], ['vessel_id', 'user_id']);
}
}
<?php
class UserVesselDetails extends Model
{
protected $fillable = [
'user_id',
'vessel_id',
];
}
@SlyDave
Copy link
Author

SlyDave commented Apr 29, 2020

If you don't need a default value, you can remove the second sub select and update ->addSelect(DB::raw(... to not use COALESCE

You could also belongsTo relation to have withDefault(), but this may result in N+1 queries for any row that doesn't find a match on the initial scopeWithDetails call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment