Home » Php » php – Using multiple Laravel scopes in OR context

php – Using multiple Laravel scopes in OR context

Posted by: admin July 12, 2020 Leave a comment

Questions:

I have a Subscription model which contain start_date and end_date attributes in my Laravel app. I have created two query scopes, scopeActive and scopeFuture to find active and future subscriptions (respectively).

I would like to know how I can build a query using both scopes in OR context, so that I can find any active or future subscriptions.

Subscription Model

/**
 * Scope a query to only include active subscriptions.
 *
 * @param \Illuminate\Database\Eloquent\Builder $query
 * @return \Illuminate\Database\Eloquent\Builder
 */
public function scopeActive($query)
{
    return $query->where('start_date', '<', Carbon::now())
                 ->where('end_date', '>', Carbon::now());
}

/**
 * Scope a query to only include future subscriptions.
 *
 * @param \Illuminate\Database\Eloquent\Builder $query
 * @return \Illuminate\Database\Eloquent\Builder
 */
public function scopeFuture($query)
{
    return $query->where('start_date', '>', Carbon::now());
}


public function business()
{
    return $this->belongsTo('App\Business');
}

Business Model

.....
public function subscriptions()
{
    return $this->hasMany('App\Subscription');
}

User Model

public function business()
{
    return $this->hasOne('App\Business');
}

Controller

Subscription::active()->get();
How to&Answers:

Try this.

$subscriptions = Subscription::where(function ($query) {
    $query->active();
})->orWhere(function ($query) {
    $query->future();
})->get();

Edit : Example as requested in the comment

$subscriptions = auth()->user()
    ->business()
    ->subscriptions()
    ->where(function ($query) {
        $query->active();
    })->orWhere(function ($query) {
        $query->future();
    })->get();

Answer:

I’m not trying to answer, just wanted to lay down an example.

public function scopeVisable(Builder $builder){

    $builder->where(function($builder){
            $builder->orWhere(function($builder){
            $builder->isPublic();
        });
        $builder->orWhere(function($builder){
            $builder->isMine();
        });
    });

    return $builder;
}

public function scopeIsPublic(Builder $builder){

    $builder->whereIsPublic(true);

    return $builder;
}

public function scopeIsMine(Builder $builder){

    $user = app(CurrentUser::class);
    $builder->whereAddedByUserId($user->getKey());

    return $builder;
}

The builder’s ->toSql() reveals:

select * from channels where (('is_public' = ?) or ('added_by_user_id' = ?))

Answer:

Take advantage of params. You could try something like…

public function scopeCanceled($query, $scope = 'where')
{
    return $query->$scope('canceled_at');
}

then you can do for example $query->canceled('orWhere')

you can then de-uglify with:

public function scopeOrCanceled($query)
{
    return $this->scopeCanceled($query, 'orWhere');
}

example $query->orCanceled()

in general with scopes, i find it handy to always have some sort of optional value, to make things flexible

public function scopeApproved($query, bool $value = true)
{
    return $query->where('is_approved', $value);
}

adding a third param for this use case can then make sense

public function scopeApproved($query, string $scope = 'where', bool $value = true) {
    return $query->$scope('is_approved', $value);
}

Answer:

Subscription::active()->orWhere( function($q){
            return $q->future() ;
        } )->get() ;