所以我的数据库中有一个
products
表和 custom_details
表。
products
可以有 custom_details
并且它们的 custom_details 类型可以是 number
、date
或 text
类型。
每个产品可以有许多自定义详细信息,因此它们之间的关系是一对多。
现在我想允许客户根据我的数据库中可用的产品详细信息进行高级搜索,并根据自定义详细信息执行一些排序
products table
-------------------------------
|id | name | description |
| 1 | product1 | |
| 2 | product2 | |
| 3 | product3 | |
| 4 | product4 | |
-------------------------------
custom_details table
-------------------------------------------------------------------------------------
| id | detailable_id | detailable_type | data_type | name | value |
| 1 | 1 | App\Models\Product | number | length | 10 |
| 2 | 1 | App\Models\Product | number | height | 19 |
| 3 | 1 | App\Models\Product | text | factory | usa |
| 4 | 1 | App\Models\Product | date | release_date | 2024-01-01 |
| 5 | 2 | App\Models\Product | number | length | 20 |
| 6 | 2 | App\Models\Product | number | height | 50 |
| 7 | 2 | App\Models\Product | text | factory | india |
| 8 | 2 | App\Models\Product | date | release_date | 2024-02-01 |
| 9 | 3 | App\Models\Product | number | length | 30 |
| 10 | 3 | App\Models\Product | number | height | 33 |
| 11 | 3 | App\Models\Product | text | factory | philippines |
| 12 | 3 | App\Models\Product | date | release_date | 2024-03-01 |
| 13 | 4 | App\Models\Product | number | length | 22 |
| 14 | 4 | App\Models\Product | number | height | 68 |
| 15 | 4 | App\Models\Product | text | factory | hawai |
| 16 | 4 | App\Models\Product | date | release_date | 2024-04-01 |
-------------------------------------------------------------------------------------
我的高级搜索的端点接受过滤器的有效负载及其相应的值,这些值将用于过滤产品。 我的 custom_detail 的每个 data_type 都有自己的过滤器实现。请参阅下面的我从客户收到的过滤器示例
'filters' => [
[
'name' => 'length',
'type' => 'number',
'min' => 15,
'max' => 30,
'order' => 'asc',
],
[
'name' => 'factory',
'type' => 'text',
'values' => ['usa', 'hawai'],
],
[
'name' => 'release_date',
'type' => 'date',
'min' => '2024-02-01',
'max' => '2024-05-06',
],
],
这是我使用有效负载中的过滤器实现的高级搜索
$query
->select("products.*")
->with(['customDetails', 'category'])
->join('custom_details', function ($query) use ($filters) {
$query->on('products.id', '=', 'custom_details.detailable_id')
->where('custom_details.detailable_type', '=', 'App\Models\Product');
if (count($filters) > 0) {
$query->where(function ($query) use ($filters) {
foreach ($filters as $key => $filter) {
$query->orWhere(function ($query) use ($filter) {
$query->where('custom_details.name', $filter['name'])
->where('custom_details.type', $filter['type']);
$query->where(function ($query) use ($filter) {
switch ($filter['type']) {
case 'text':
if (isset($filter['values']) && $filter['values']) {
$query->whereIn('custom_details.value', $filter['values']);
}
break;
case 'date':
if (isset($filter['min']) && $filter['min']) {
$query->whereDate('custom_details.value', '>=' ,$filter['min']);
}
if (isset($filter['max']) && $filter['max']) {
$query->whereDate('custom_details.value', '<=' ,$filter['min']);
}
break;
case 'number':
if (isset($filter['min']) && $filter['min']) {
$query->where('custom_details.value', '>=' ,$filter['min']);
}
if (isset($filter['max']) && $filter['max']) {
$query->where('custom_details.value', '<=' ,$filter['max']);
}
break;
}
});
});
}
});
}
});
foreach ($filters as $key => $filter) {
if (isset($filter['order'])) {
$query->orderBy('custom_details.value', $filter['order']);
}
}
上面的实现为我提供了通过所有过滤标准的产品,但它也为我提供了至少通过一个过滤标准的产品。
我只想获取通过所有过滤条件的产品。
我尝试用
orWhere
替换 where
但它没有给我任何结果。
请帮助我指出正确的方向。我不知道我应该使用什么 mysql 函数来完成这项工作。
我能够通过使用 laravel 的 leftJoinLateral 使其工作。虽然我不确定这是否是最好的方法。请随时提供更好的答案。
请参阅下面的 leftJoinLateral 实现。
public function advanceSearch($filters, $totalItems) {
$baseQuery = $this->model
->select($this->model->getTable().".*");
foreach ($filters as $key => $filter) {
$baseQuery->leftJoinLateral(
DB::table('custom_details')
->select('value')
->where('detailable_type', $this->model::class)
->whereColumn('detailable_id', $this->model->getTable().".id")
->where('name', $filter['name'])
->where('type', $filter['type']),
$filter['name']."_".$filter['type']
);
}
foreach ($filters as $key => $filter) {
switch ($filter['type']) {
case config('enum.custom_detail_types.text'):
if (isset($filter['values']) && $filter['values']) {
$baseQuery->whereIn($filter['name']."_".$filter['type'].".value", $filter['values']);
}
break;
case config('enum.custom_detail_types.date'):
if (isset($filter['min']) && $filter['min']) {
$baseQuery->whereDate($filter['name']."_".$filter['type'].".value", '>=' ,$filter['min']);
}
if (isset($filter['max']) && $filter['max']) {
$baseQuery->whereDate($filter['name']."_".$filter['type'].".value", '<=' ,$filter['max']);
}
break;
case config('enum.custom_detail_types.number'):
if (isset($filter['min']) && $filter['min']) {
$baseQuery->where($filter['name']."_".$filter['type'].".value", '>=' ,$filter['min']);
}
if (isset($filter['max']) && $filter['max']) {
$baseQuery->where($filter['name']."_".$filter['type'].".value", '<=' ,$filter['max']);
}
break;
case config('enum.custom_detail_types.boolean'):
if (isset($filter['value'])) {
$baseQuery->where($filter['name']."_".$filter['type'].".value", '1');
}
break;
default:
if (isset($filter['values']) && count($filter['values']) > 0) {
$baseQuery->whereIn($filter['name']."_".$filter['type'].".value" ,$filter['values']);
}
break;
}
}
foreach ($filters as $key => $filter) {
if (isset($filter['order'])) {
$baseQuery->orderBy($filter['name']."_".$filter['type'].".value", $filter['order']);
}
}
return $baseQuery->paginate($totalItems ? $totalItems : $this->model->count());
}