mercredi 25 octobre 2017

Scope-like function to return Model

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?



from Newest questions tagged laravel-5 - Stack Overflow http://ift.tt/2gI4VZ5
via IFTTT

Aucun commentaire:

Enregistrer un commentaire