Laravel Entity Attribute Value Фильтр множественных атрибутов

#php #mysql #laravel #eloquent #entity-attribute-value

#php #mysql #laravel #красноречивый #сущность-атрибут-значение

Вопрос:

Я пытаюсь разработать модель post, которая имеет отношение «один ко многим» с моделью postmeta (включая id столбцы , post_id , meta_key , meta_value ), а также имеет отношение «многие ко многим» с моделью term (включая term_id и некоторые другие столбцы, а также таблицу post_term, включая id post_postId term_term_term_id столбцы, ,).

Я создал форму, которая содержит некоторые post_metas и термины для поиска и фильтрации моих сообщений.

В этом примере PHP-кода показано, что я использовал:

 $posts_query = Post::where('post_type', 'add')->where('author', Auth::id())->where(function ($q0) use($filter_metas){
            foreach ($filter_metas as $index => $meta) {
                $q0->whereHas('postmetas', function ($q0) use ($index,$meta) {
                    $q0->where('meta_key', $index)->where('meta_value', $meta);
                });
            }
        });
 

и это выполняемый запрос для получения результатов.

 select * from `posts` where `post_type` = ? and `author` = ? and (exists (select * from `postmeta` where `posts`.`postId` = `postmeta`.`post_id` and `meta_key` = ?) and exists (select * from `postmeta` where `posts`.`postId` = `postmeta`.`post_id` and `meta_key` = ? and `meta_value` in (?, ?, ?, ?)) and exists (select * from `postmeta` where `posts`.`postId` = `postmeta`.`post_id` and `meta_key` = ? and `meta_value` in (?, ?, ?)) and exists (select * from `postmeta` where `posts`.`postId` = `postmeta`.`post_id` and `meta_key` = ? and `meta_value` > ?) and exists (select * from `postmeta` where `posts`.`postId` = `postmeta`.`post_id` and `meta_key` = ? and `meta_value` = ?) and exists (select * from `postmeta` where `posts`.`postId` = `postmeta`.`post_id` and `meta_key` = ? and `meta_value` = ?) and exists (select * from `postmeta` where `posts`.`postId` = `postmeta`.`post_id` and `meta_key` = ? and `meta_value` = ?) and exists (select * from `postmeta` where `posts`.`postId` = `postmeta`.`post_id` and `meta_key` = ? and `meta_value` = ?) and exists (select * from `postmeta` where `posts`.`postId` = `postmeta`.`post_id` and `meta_key` = ? and `meta_value` = ?) and exists (select * from `postmeta` where `posts`.`postId` = `postmeta`.`post_id` and `meta_key` = ? and `meta_value` = ?)
 

Я могу получить правильные результаты из этого запроса, но запрос выполняется слишком медленно, и это моя главная проблема. Существуют ли какие-либо альтернативные подходы?

Комментарии:

1. Старайтесь не использовать whereHas, если у вас больше записей с номерами. Добавьте индексы, если вы действительно хотите использовать whereHas. Я столкнулся с той же проблемой ранее, и я использовал объединения.

2. Привет и спасибо за ваш комментарий, не могли бы вы объяснить это немного подробнее? Или просто поделитесь примером того, как это сделать со мной?

Ответ №1:

Вы можете использовать единый объединенный запрос с агрегацией для фильтрации по мета-атрибутам каждого сообщения

 $posts = Post::select(['posts.postId','posts.name'])
            ->where('post_type', 'add')
            ->where('author', Auth::id())
            ->join('postmeta', 'posts.postId', '=', 'postmeta.post_id')
            ->groupBy('posts.postId','posts.name')
            ->where(function ($query) use ($filter_metas) {
                foreach ($filter_metas as $index => $meta) {
                    $query->havingRaw('sum(case when meta_key = ? and meta_value = ? then 1 else 0 end) > 0', [$index, $meta]);
                }
            });
 

В приведенной выше havingRaw части запроса это ключ, он будет условно отфильтровывать сообщения, которые не соответствуют метаинформации,

Ответ №2:

Попробуйте этот код, не уверен, работает ли он, надеюсь, вы сможете понять идею.

         $posts_query = Post::where('post_type', 'add')
        ->where('author', Auth::id())
        ->leftJoin('postmeta', function($join){
            $join->on('posts.postId', 'postmeta.post_id');
        })
        ->where(function ($query) use ($filter_metas) {
            foreach ($filter_metas as $index => $meta) {
                $query->where('postmeta.meta_key', $index)
                    ->where('postmeta.meta_value', $meta);
            }
        });
 

Комментарии:

1. Спасибо за ваш ответ, но этот запрос не решает мою проблему. Когда я объединяю таблицы, каждая строка имеет один meta_key, поэтому, когда я использую ‘и’ для исследования разных meta_keys, нет строки, которая имела бы, например, meta_key=a и meta_key=b .