I am developing an application in Laravel 5.4
and I want a global scope
that allows me to filter different elements of the application depending on the user that created them.
I have a class BaseEloquentModel.php
who extends Eloquent
and all my models extends from this class. I have a global scope
as follow:
protected static function boot()
{
parent::boot();
static::addGlobalScope('', function(\Illuminate\Database\Eloquent\Builder $builder) use($userId) {
/**
* I get the name of the table with <code>(with(new static))->getTable()</code>
* and then filter the query for the <b>user_id</b> field
*/
$builder->where(
(with(new static))->getTable() . '.user_id',
'=',
$userId
);
});
}
When I have a query like this, with or
operator, the global scope is "neutralized":
$qBuilderCars = Car::whereRaw("name like ? or id = ?", [
'%' . $searchCriteria. '%',
$searchCriteria
]);
If I call the toSql()
method on $qBuilderCars
I see that it "correctly" adds the AND
operator to the end of the query.
select * from `cars` where name like ? or id = ? and user_id = ?
Maybe you've already noticed my problem ... If the element's builder, in this case cars
, has used an OR
operator, then the global scope will not help, since there is no parenthesis between where name like ? or id = ?
. So the resulting query would be something similar to the following:
select * from `cars` where name like ? (or id = ? and user_id = ?)
So this query will return all cars whose name matches or whose ID is the one received and has been created by the user...
When what I need is:
select * from `cars` where (name like ? or id = ?) and user_id = ?
I tried to alter my global scope
to try to make the AND
operator that I add the most restrictive in the query, but without any success.
I can not manually add parentheses to all the application's queries, so ... Is there a way to add global parentheses from the global scope to the builder
?
The solution is to add parentheses to all the raw queries.
β β You can see the @Styx solution which I consider the most successful
I will also leave my answer, which acts directly inside the global scope
, and which I consider interesting to be able to see how an \Illuminate\Database\Eloquent\Builder
object works
Well, it seems that your solution to add parentheses is the best workaround, but I have a suggestion how to do that slightly better way.
Create new class QueryBuilder
. For example, in \App\Models\
namespace (app/Models/
folder):
namespace App\Models;
use Illuminate\Database\Query\Builder as EloquentQueryBuilder;
class QueryBuilder extends EloquentQueryBuilder {
public function whereRaw($sql, $bindings = [], $boolean = 'and')
{
return parent::whereRaw('('.$sql.')', $bindings, $boolean);
}
}
Add this code to your BaseEloquentModel
class:
use Illuminate\Database\Eloquent\Model;
use App\Models\QueryBuilder; // <-- addition
class BaseEloquentModel extends Model {
// ...
protected function newBaseQueryBuilder()
{
$connection = $this->getConnection();
return new QueryBuilder(
$connection,
$connection->getQueryGrammar(),
$connection->getPostProcessor()
);
}
// ...
}
Now, all whereRaw()
calls will automatically have parentheses around query.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With