Laravel 使用子表值在父表中高级搜索

问题描述 投票:0回答:1

所以我的数据库中有一个

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 函数来完成这项工作。

php laravel eloquent
1个回答
0
投票

我能够通过使用 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());
}
© www.soinside.com 2019 - 2024. All rights reserved.