How do you wrap Laravel Eloquent ORM query scopes in parentheses when chaining?

14,056

Solution 1

Did you test whereRaw()?

so your function should look like:

function scopeIsFeaturedOrIsBlogPost($query) {
    return $query->whereRaw('( isFeatured = "true" OR type = "blogPost" )');
}

ps. this is untested

Solution 2

You can generate parentheses by passing a callback function to where().

Model::where('a',1)->where(function($query) {
    $query->where('b', 2)->orWhere('c',3);
})->get();

Solution 3

SEE EDIT FOR FINAL ANSWER

Better than raw, use orWhere with a closure:

$model = Model::whereAIsOne()->orWhere(function($query) {
    $query->where('b', 1)->where('c', 1);
})->get();

The really unfortunate thing (and the way I ended up at this post) is that the second $query used in the closure is an instance of \Illuminate\Database\Query\Builder rather than \Illuminate\Database\Eloquent\Builder - this means that you can't use the model scopes within that closure, because Query\Builder instances don't have a reference to a model.

That said, I still think this is much more ORM-like and useful as a developer.

EDIT

Took a while to figure out, but this will work if you want to use your scopes as well:

$model = Model::whereAIsOne()->orWhere(function($query) {
    $this->newEloquentBuilder($query)->setModel($this)->whereBIsOneAndCIsOne();
})->get();

I'm actually creating a fourth scope in my code that encompasses all of this, so the $this might not work in this context - however, I find that scoping for my models is one of the most important parts of building a good system.

This definitely should be built into Eloquent - the engine has a good start, but so many features are missing.

Solution 4

I'm a little late to the party, but wouldn't the most logical way be to wrap the where in a Closure?

    Model::where('a', '=', 1)
            ->where(function($query) {
                    $query->where('b', '=', 2)
                    ->orWhere('c', '>', 3);
    })
    ->get();

Solution 5

To wrap queries, you can do something like this:

$builder->where(function ($query) {
    $query->where('gender', 'Male')
        ->where('age', '>=', 18);
})->orWhere(function($query) {
    $query->where('gender', 'Female')
        ->where('age', '>=', 65);   
})

It will output the following query:

WHERE (gender = 'Male' and age >= 18) or (gender = 'Female' and age >= 65)
Share:
14,056

Related videos on Youtube

crowsfan85
Author by

crowsfan85

Updated on September 15, 2022

Comments

  • crowsfan85
    crowsfan85 over 1 year

    In Eloquent, I'd like to generate this query:

    SELECT * FROM table WHERE a=1 AND ( b=2 OR c=3 );
    

    But I seem to be generating this query instead:

    SELECT * FROM table WHERE a=1 AND b=2 OR c=3;
    

    Here is my implementation and code:

    $result = Model::aIsOne()->bIsTwoOrCIsThree()->get();
    

    Model contains:

    function scopeAIsOne($query) {
        return $query->where('a', 1);
    }
    
    function scopeBIsTwoOrCIsThree($query) {
        return $query->where('b', 2)->orWhere('c', 3);
    }
    

    Thanks for any help. I've searched through the docs including Advanced Wheres to no avail.

  • crowsfan85
    crowsfan85 over 10 years
    Good call. I'll give that a shot, though I'd hope there'd be a more elegant way to do it. I'd tend to think that every chained scope should automatically get wrapped in parentheses. (ps. wish I had the rep to upvote you)
  • STWilson
    STWilson over 7 years
    for those needing to pass a variable into the nest, remember to USE e.g.: ->where(function($query) use ($options) { $query...
  • Luciano Bargmann
    Luciano Bargmann almost 5 years
    Although this fixes the issue, it us not using Eloquent. And ps. this is untested... thanks for honesty, but don't do this.