У меня много таблиц (с учетом пользователя, а не SQL) с фильтрами на столбе. Столбцы имеют не только поля таблиц, но и поля связанных моделей.
Я пытаюсь добавить функцию, в которой при фильтрации данных можно использовать запятую как разделитель ИЛИ. Мне нужно сделать это вообще, поскольку у меня довольно большое количество таблиц, полей и отношений. Кроме того, некоторые таблицы построены динамически.
Код, который добавляет предложения where для собственных полей моделей, таков:
foreach ($filters as $column => $filter) { $q->where(function ($q) use ($filter, $model, $column) { $first = array_pop($filter); $q->where("$model.$column", 'LIKE', "%$first%"); foreach ($filter as $or) { $q->orWhere("$model.$column", 'LIKE', "%$or%"); } }); }
Это работает так, как ожидалось. При попытке сделать то же самое с полями отношений единственное различие заключается в использовании whereHas и в том числе модели:
foreach ($filters as $column => $filter) { $q->whereHas($model, function ($q) use ($filter, $model, $column) { $first = array_pop($filter); $q->where("$model.$column", 'LIKE', "%$first%"); foreach ($filter as $or) { $q->orWhere("$model.$column", 'LIKE', "%$or%"); } }); }
Проблема в том, что если я фильтрую поля отношений, это не работает должным образом. Я подозреваю, что это связано с whereHas с sub, где / илиWhere клаузулы. Это результат, который я получаю
No filters: |------------+-----------------------------------+--------------------------------------| | Contact ID | Type (Field in contact SQL table) | Ecozone (Field in Ecozone SQL table) | |------------+-----------------------------------+--------------------------------------| | Filter -> | | | |------------+-----------------------------------+--------------------------------------| | 1 | Manager | Bush | | 2 | | Forest | | 3 | Worker | | |------------+-----------------------------------+--------------------------------------| Single filter on current model (correct): |------------+-----------------------------------+--------------------------------------| | Contact ID | Type (Field in contact SQL table) | Ecozone (Field in Ecozone SQL table) | |------------+-----------------------------------+--------------------------------------| | Filter -> | man | | |------------+-----------------------------------+--------------------------------------| | 1 | Manager | Bush | |------------+-----------------------------------+--------------------------------------| Multiple filter on current model (correct): |------------+-----------------------------------+--------------------------------------| | Contact ID | Type (Field in contact SQL table) | Ecozone (Field in Ecozone SQL table) | |------------+-----------------------------------+--------------------------------------| | Filter -> | man, wor | | |------------+-----------------------------------+--------------------------------------| | 1 | Manager | Bush | | 3 | Worker | | |------------+-----------------------------------+--------------------------------------| Single filter on related model field (correct): |------------+-----------------------------------+--------------------------------------| | Contact ID | Type (Field in contact SQL table) | Ecozone (Field in Ecozone SQL table) | |------------+-----------------------------------+--------------------------------------| | Filter -> | | bus | |------------+-----------------------------------+--------------------------------------| | 1 | Manager | Bush | |------------+-----------------------------------+--------------------------------------| Multiple filter on related model field (incorrect): |------------+-----------------------------------+--------------------------------------| | Contact ID | Type (Field in contact SQL table) | Ecozone (Field in Ecozone SQL table) | |------------+-----------------------------------+--------------------------------------| | Filter -> | | bus,for | |------------+-----------------------------------+--------------------------------------| | 1 | Manager | Bush | | 2 | | Forest | | 3 | Worker | | |------------+-----------------------------------+--------------------------------------|
Целый путь кода ниже:
В контроллере:
return Contacts::with( 'ecozone' )->where( function ($q) { $this->set_filters($q, 'contact'); })->paginate($count);
В BaseController:
protected function set_filters($q, $current_model) { $filters_array = $this->parse_filters(); if ($filters_array) { foreach($filters_array as $model => $filters) { if ($model == $current_model) { foreach($filters as $column => $filter) { $q->where(function ($q) use ($filter, $model, $column) { $first = array_pop($filter); $q->where("$model.$column", 'LIKE', "%$first%"); foreach($filter as $or) { $q->orWhere("$model.$column", 'LIKE', "%$or%"); } }); } } else { foreach($filters as $column => $filter) { $q->whereHas($model, function ($q) use ($filter, $model, $column) { $first = array_pop($filter); $q->where("$model.$column", 'LIKE', "%$first%"); foreach($filter as $or) { $q->orWhere("$model.$column", 'LIKE', "%$or%"); } }); } } } } }
$ this-> фильтры:
protected function parse_filters() { $filters = Input::get('filters'); $filters = json_decode($filters); $filters = array_where($filters, function($key, $value) { return !empty($value); }); $filters = (Array) $filters; $has_filters = !empty($filters); if ($has_filters) { $filters_array = []; foreach ($filters as $key => $value) { $value = explode(',', $value); array_set($filters_array, $key, $value); } } else { $filters_array = false; } return $filters_array; }
И возвращает, что я считаю правильным, следующий массив. Первый уровень – это модель, вторая – поле, третье – разделенные запятой ИЛИ предложения
Array ( [ecozone] => Array ( [ecozone] => Array ( [0] => bush [1] => forest ) ) [contact] => Array ( [contact_type] => Array ( [0] => manager [1] => worker ) ) )
Дамп SQL, используемый при фильтрации по текущим полям модели. Это работает так, как ожидалось:
array (size=4) 0 => array (size=3) 'query' => string 'select count(*) as aggregate from `contact` where `contact`.`deleted_at` is null and ((`contact`.`physical_address` LIKE ? or `contact`.`physical_address` LIKE ?))' (length=163) 'bindings' => array (size=2) 0 => string '%add%' (length=5) 1 => string '%nana%' (length=6) 'time' => float 1.67 1 => array (size=3) 'query' => string 'select * from `contact` where `contact`.`deleted_at` is null and ((`contact`.`physical_address` LIKE ? or `contact`.`physical_address` LIKE ?)) limit 10 offset 0' (length=161) 'bindings' => array (size=2) 0 => string '%add%' (length=5) 1 => string '%nana%' (length=6) 'time' => float 0.91 2 => array (size=3) 'query' => string 'select * from `ecozone` where `ecozone`.`deleted_at` is null and `ecozone`.`id` in (?, ?)' (length=89) 'bindings' => array (size=2) 0 => string '1' (length=1) 1 => string '2' (length=1) 'time' => float 0.8
Дамп SQL-запроса для фильтрации по связанным моделям. Это не работает должным образом:
array (size=4) 0 => array (size=3) 'query' => string 'select count(*) as aggregate from `contact` where `contact`.`deleted_at` is null and ((select count(*) from `ecozone` where `ecozone`.`deleted_at` is null and `contact`.`ecozone_id` = `ecozone`.`id` and `ecozone`.`ecozone` LIKE ? or `ecozone`.`ecozone` LIKE ? and `ecozone`.`deleted_at` is null) >= 1)' (length=301) 'bindings' => array (size=2) 0 => string '%grass%' (length=7) 1 => string '%bush%' (length=6) 'time' => float 1.18 1 => array (size=3) 'query' => string 'select * from `contact` where `contact`.`deleted_at` is null and ((select count(*) from `ecozone` where `ecozone`.`deleted_at` is null and `contact`.`ecozone_id` = `ecozone`.`id` and `ecozone`.`ecozone` LIKE ? or `ecozone`.`ecozone` LIKE ? and `ecozone`.`deleted_at` is null) >= 1) limit 10 offset 0' (length=299) 'bindings' => array (size=2) 0 => string '%grass%' (length=7) 1 => string '%bush%' (length=6) 'time' => float 0.89 2 => array (size=3) 'query' => string 'select * from `ecozone` where `ecozone`.`deleted_at` is null and `ecozone`.`id` in (?, ?)' (length=89) 'bindings' => array (size=2) 0 => string '1' (length=1) 1 => string '2' (length=1) 'time' => float 1.38