The answers to this question explain that Model Scopes are not meant to return anything but a Query Builder instance and custom getters should be used to return Model instances.
Problem
In my case I have a User and Contract Model, where a User has many Contracts. The Contract periods may overlap, but at any given time, only the Contract with the latest start date should be considered valid (e.g. given Contract 1 from 2017-01-01 to 2017-07-31 and Contract 2 from 2017-06-01 to 2017-12-31, for 2017-07-01 Contract 2 should be returned)
Current solution
Using scopes I always have to call ->first():
public function scopeByDate(Builder $query, $date) {
return $query->whereDate('start', '<=', $date)
->whereDate('end', '>=', $date)
->orderBy('start', 'desc');
}
public function scopeCurrent(Builder $query) {
return $this->scopeByDate($query, date('Y-m-d'));
}
...
$user->contracts()->byDate('some-date')->first();
$user->contracts()->current()->first();
(Worse?) alternative solutions
Otherwise I could make byDate() and current() static, accepting either a Builder (looks bad to me) or a User (even worse?) instance and manually pass the parameters like
public static function byDate(Builder $query, $date) {
return $query->whereDate(...)->whereDate(...)->orderBy(...)->first();
}
...
Contract::byDate($user->contracts(), 'some-date');
or
public static function byUserAndDate(User $user, $date) {
return $user->contracts()->where...->where...->orderBy(...)->first()
}
...
Contract::byUserAndDate($user, 'some-date');
Question
Is there some way I can call byDate() or current() directly on the query builder (relation) like scopes without passing additional parameters and return a model instance instead of a builder and having to call first() every time?
via Chebli Mohamed
Aucun commentaire:
Enregistrer un commentaire