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.
Related
I would need something like this:
$user_ids = [1,2,3]; // 1 as logged user
$chats = Chat::whereHas('users', function ($q) { $q->where('users.id','=',1); })->get();
$c = null;
foreach ($chats as $chat) {
if ($chats->users->pluck('id')->diff($user_ids)->count() === 0) {
$c = $chat;
break;
}
}
return $c;
Is there some one-liner or simpler solution in laravel to get chat based on relation collection that equals to array of ids without same order within array?
If I understand you correct, you want the first chat that has exactly these user IDs right? then try this:
$user_ids = [1, 2, 3];
$chat = Chat::has('users', '=', count($user_ids))
->where(function ($query) use ($user_ids) {
foreach ($user_ids as $user_id) {
$query->whereHas('users', function ($q) use ($user_id) {
$q->where('id', $user_id);
});
}
})
->first();
$chat = Chat::whereHas('users', fn ($q) => $q->whereIn('users.id', $user_ids), '=', count($user_ids))
->whereDoesntHave('users', fn ($q) => $q->whereNotIn('users.id', $user_ids))
->first();
This works
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
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
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.
I have query for filters:
public function filter(Request $request)
{
$category_id = $request->category;
$brand_id = $request->brand;
$filters = $request->filters;
if($brand_id == null){
$products = Products::with('brand')->whereHas('category',function($q) use($category_id){
$q->where('category_id', $category_id);
})->whereHas('filters',function($q) use($filters){
$q->where('filter_id', $filters);
})->paginate(9);
}else{
$products = Products::with('brand')->where('brand_id',$brand_id)->whereHas('category',function($q) use($category_id){
$q->where('category_id', $category_id);
})->whereHas('filters',function($qw) use($filters){
$qw->where('filter_id', $filters);
})->paginate(9);
}
//Брэнды всех товаров
$Brands = array();
foreach ($products as $product) {
if(!in_array($product->brand, $Brands)){
array_push($Brands, $product->brand);
}
}
return response()->json(['products' => $products,'brands' => $Brands]);
}
I get response only for first product, but i need to get all products which contain at least one filter from the list.How can I do it?
Just a small refactor + i'm not sure if you receive an array of filters or just a single id. If you need more than 1 id, use whereIn.
If you want to make this even cleaner, you can create Eloquent scope for filters and brands.
public function filter(Request $request)
{
$categoryId = $request->category;
$brandId = $request->brand;
$filters = $request->filters;
$query = Products::with('brand');
if ($brandId) {
$query = $query->where('brand_id', $brandId);
}
if ($filters) {
$query = $query->whereHas('filters', function($q) use ($filters) {
// If you have more than one filter id, use whereIn
// $q->where('filter_id', $filters);
$q->whereIn('filter_id', (array) $filters);
});
}
if ($categoryId) {
$query = $query->whereHas('category', function($q) use ($categoryId) {
$q->where('category_id', $categoryId);
});
}
$products = $query->paginate(9);
$brands = $products->total() > 0 ? $products->items()->pluck('brand')->all() : [];
return response()->json(compact('products', 'brands'));
}
use whereIn() function instead of where() when query products