Laravel Eloquent - Where Clause ignored - php

I have a filter method in my controller that is very simple. It correctly filters by project and customer but the status part is just ignored. Doing it straight on the database works.
I suspect it has to do with status being a string. Accepted statuses are 'C' and 'O'.
Never ran into this before. Thanks in advance!
Edit: I want to chain where calls as "AND", I need the results to satisfy all 3 criteria. However, it returns all models event if i strip down the query to: Job::with(['users', 'project', 'files'])
->where('status', $status);
How can that fail? I dont't get that.
Edit 2: Hell, even Job::where('status', $status)->get(); fails to bring records where status = 'C' or 'O', while it works if i query straight on the database.
public function doFilter($customer_id, $project_id, $status)
{
$jobs = collect();
if ($customer_id || $project_id || $status) {
$query = Job::query()->with(['users', 'project', 'files']);
$query->when($project_id, function ($query, $project_id) {
return $query->where('project_id', $project_id);
});
$query->when($customer_id, function ($query, $customer_id) {
return $query->where('customer_id', $customer_id);
});
$query->when($status, function ($query, $status) {
return $query->where('status', $status);
});
// dd($status);
$jobs = $query->get();
}
return $jobs;
}

The example above will produce the following SQL
"select * from `jobs` where (`status` = ?) and (`customer_id` = ?) (`project_id` = ?)"
Which mean the matching records from jobs table should satisfy all where clauses
You may fix it using
public function doFilter($customer_id, $project_id, $status)
{
$jobs = collect();
$query = Job::query()->with(['users', 'project', 'files'])->where(function($query) use ($status) {
$query->when($status, function ($query, $status) {
// dump('here 1');
return $query->where('status', $status);
});
})->orWhere(function ($query) use ($customer_id) {
$query->when($customer_id, function ($query, $customer_id) {
// dump('here 2');
return $query->where('customer_id', $customer_id);
});
})->orWhere(function ($query) use ($project_id) {
$query->when($project_id, function ($query, $project_id) {
// dump('here 3');
return $query->where('customer_id', $project_id);
});
});
$jobs = $query->get();
return $jobs;
}
The new code i provided will produce the following SQL
"select * from `jobs` where (`status` = ?) or (`customer_id` = ?) or (`project_id` = ?)" // Only one where clause must be satisfied

Related

How to simplify Laravel Eloquent where clause?

The following example will give me all users that are older than $age and have the gender $gender:
public function get_users_older_than(int $age = null, string $gender = null) {
$users = User::query()
->where('age', '>', $age)
->where('gender', $gender)
->get();
return $users;
}
Is there a way to use the where clause only if age or gender is present in a more eloquent and shorter way then:
public function get_users_older_than(int $age = null, string $gender = null) {
$users = User::query();
if ($age) {
$users = $users->where('age', '>', $age):
}
if ($gender) {
$users = $users->where('gender', $gender):
}
$users = $users->get();
return $users;
}
With that, I could avoid to create and use the extra function and just have one single arrow connected line of code.
A shorter way to do that would be this:
$users = User::where(function ($q) use($age, $gender) {
if ($age) {
$q->where('age', '>', $age);
}
if ($gender) {
$q->where('gender', $gender);
}
})->get();
But just so you know, shorter code does not guarantee more efficiency.
There is also a conditional clauses function when.
Based on what you have, that could look like:
public function get_users_older_than(int $age = null, string $gender = null) {
$users = User::query();
$users->when($age, function ($query, $age) {
$query->where('age', '>', $age);
})->when($gender, function ($query, $gender) {
$query->where('gender', $gender);
})->get();
return $users;
}

How to make a whereIn query builder in Laravel Eloquent with empty parameter

I have made a query in Laravel Eloquent to search in table.
public function searchContest($search){
$category = [];
$area = [];
if(isset($search['area'])){
$area = $search['area'];
}
if(isset($search['category'])){
$category = $search['category'];
}
$qry = self::whereIn('area',$area)
->whereIn('category', $category)
->get();
var_dump($query);
return;
}
But sometimes, area or category is empty and whereIn does not work with it. I'm not able to find any working solutions in the net. How can I make such a query?
Or you can take advantage of the conditional clauses as here
DB::table('table_name')
->when(!empty($category), function ($query) use ($category) {
return $query->whereIn('category', $category);
})
->when(!empty($area), function ($query) use ($area) {
return $query->whereIn('area', $area);
})
->get();
$q = self::query();
if (isset($search['area'])) {
$q->whereIn('area', $search['area']);
}
if (isset($search['category'])) {
$q->whereIn('category', $search['category']);
}
$qry = $q->get();
You can use query scope inside the entity
public function scopeArea($query, $ids)
{
if (! $ids) {
return ;
}
return $query->whereIn('area', $ids);
}
public function scopeCategory($query, $ids)
{
if (! $ids) {
return ;
}
return $query->whereIn('category', $ids);
}
Now you can build the query
$entity
->area($area)
->category($category)
->get();
https://laravel.com/docs/5.7/eloquent#query-scopes

Building complex where statement in Laravel Eloquent using

I have an Eloquent\Builder $query that I want to use additional where() calls on, where the amount of the calls is indefinite and is taken from an array $filter, example below:
$filter = [
'or:email:=:ivantalanov#tfwno.gf',
[
'or:api_token:=:abcdefghijklmnopqrstuvwxyz',
'and:login:!=:administrator',
],
];
The strings, when parsed, produce valid SQL conditions, but the problem lies into sticking them into closures where a group is present (like strings 2 and 3 in the example - the array is their 'group').
I know of Laravel's functionality that allows sticking a Closure function into $query->where() to achieve what I want, but the problem I'm facing is actually building those complex closures. I have to iterate through every string in the group and pass it into the closure generated like so (where $item is the result of parsing a condition string):
$closure = function ($query) use ($item)
{
call_user_func_array(
[$query, $item['function']], [$item['field'], $item['operator'], $item['values']]
);
};
Now the obvious problem with this is while it makes simple closures easily, passing more than one condition is plain impossible.
My question is, what could I use to prepare a complex statement to be executed on a query inside a closure?
Okay, I think I figured it out.
This is the method that will return the end result.
public function parse_filter(Builder &$query, array $filter)
{
$groups = $this->_prepare_groups($filter);
return $this->_parse_groups($query, $groups);
}
These methods will parse the initial array into something more usable.
private function _prepare_groups(array $filter)
{
foreach ($filter as $key => $item) {
if (is_array($item)) {
$groups[] = $this->_prepare_groups($item);
}
if (is_string($item)) {
$simple_filter = $this->_parse_simple_filter($item);
$groups[] = $simple_filter;
$simple_filter = null;
}
}
return $groups;
}
private function _parse_simple_filter(string $filter)
{
$filter_data = explode(':', $filter);
$simple_filter['function'] = $filter_data[0] === 'and' ? 'where' : 'orWhere';
$simple_filter['field'] = $filter_data[1];
$simple_filter['operator'] = $filter_data[2];
$simple_filter['values'] = $filter_data[3];
return $simple_filter;
}
And here is where the most of the magic happens. Closures are recursive calls to this method, as you can see.
private function _parse_groups(Builder &$query, array $groups)
{
foreach ($groups as $operator => $group) {
if (!array_key_exists('function', $group)) {
$closure = function ($query) use ($group)
{
$this->_parse_groups($query, $group);
};
$query->where($closure);
} else {
$query->{$group['function']}($group['field'], $group['operator'], $group['values']);
}
}
return $query;
}
Using this, you can modify an Eloquent\Builder object however you like with deeply nested filters that are declared dynamically (for example, received within a GET/POST request).
I guess this will help for you:
In model create a scope:
public static function scopeGetResultList($query) {
return $query->where(function ($query) use ($item) {
$query->where('group_user_holder_type', '=', 1)
->orWhere('group_user_holder_type', '=', 0);
});
}
OR
Example:
public static function getSearchedUserAuto($search_key, $user_id)
{
$users = DB::table((new User)->getTable().' as U')
->select('U.*', 'CT.city_name', 'C.nicename')
->leftJoin((new Country)->getTable().' as C', 'C.country_id', '=', 'U.user_country')
->leftJoin((new City)->getTable().' as CT', 'CT.city_id', '=', 'U.user_city')
->where(function($query) use ($search_key){
$query->where('U.user_full_name', 'like', '%'.$search_key.'%')
->orWhere('U.user_email', 'like', '%'.$search_key.'%');
})
->where(function($query) use ($search_key){
$query->where('U.user_full_name', 'like', '%'.$search_key.'%')
->orWhere('U.user_email', 'like', '%'.$search_key.'%');
})
->where('U.status', '=', 1)
->where('U.user_id', '!=', $user_id)
->get();
return $users;
}
See if it is work for you.

Is whereHas right way to go?

$provider is optional, is it correct to use whereHas in this scenario even when I have already used with() Eager Loading?
public function findByStatus($status, $provider = null)
{
$result = $this->hosts->with('logins')->where('status', $status);
if ($provider) {
$result->whereHas('logins', function ($query) use ($provider) {
$query->where('provider', $provider);
});
}
return $result->get();
}
You can you it in the way you are using. But I'd do like this.
$result = $this->hosts->whereHas('logins', function ($query) use ($status, $provider) {
$query->where('status', $status);
if ($provider) {
$query->where('provider', $provider);
}
return $query;
})->get();
Since both the quires about logins you can group them in one query.

Laravel array whereIn()

I have a form to filter items:
and I'm looking for something similar to this in Laravel 5.3:
// some variables get from request()->input('...')
$mode = ['A'];
$type = ['a', 'b'];
$group = [0, 1];
// desirable query
$results = Item::whereIn([
['mode_id', $mode],
['type_id', $type],
['group_id', $group]
])->paginate(10);
I can do this
$results = Item::whereIn('mode_id', $mode)
->whereIn('type_id', $type)
->whereIn('group_id', $group)
->paginate(10);
but it's not a dynamic way. For example, if a user select nothing in mode, the query returns an empty array.
We can use conditional clauses:
$results = Item::
when(!empty($mode), function ($query) use ($mode) {
return $query->where('mode_id', $mode);
})
->when(!empty($type), function ($query) use ($type) {
return $query->where('type_id', $type);
})
->when(!empty($group), function ($query) use ($group) {
return $query->where('group_id', $group);
})
->paginate(10);
You can do:
$qb = Item::newQuery();
if (!empty($mode))
{
$qb->whereIn('mode_id', $mode);
}
if (!empty($type))
{
$qb->whereIn('type_id', $type);
}
if (!empty($group))
{
$qb->whereIn('group_id', $group);
}
$results = $qb->paginate(10);
Or build your whereIn associative array w/o empty where's before passing it on.

Categories