WP_Query - фильтровать или напрямую?

2 янв. 2016 г., 05:01:18
Просмотры: 13.5K
Голосов: 5

Количество моих файлов, шаблонов, скриптов, запросов и т.д. растёт, и мне нужна хорошая система для поддержки всего этого.

Я старался держать всё организованным:

  • Нет тегов <script> в шаблонах
  • Встроенный CSS только когда это PHP переменная, которая может динамически меняться из админки
  • Одиночные большие файлы JS и CSS для минимизации запросов

Теперь пришло время организовать мои запросы, потому что их уже как минимум 10 и количество продолжает расти.

  1. Первый вариант: использовать кастомный add_filter() для каждого запроса

    • Мне не нужно искать запросы, потому что они все в одном файле или директории
    • Если нужно изменить запрос, изменения делаются в одном месте, а не во всех шаблонах
  2. Второй вариант: писать все запросы прямо в шаблонах как обычно делают

    • По сути, все пункты противоположны первому варианту

Вопрос:

Есть ли у использования фильтров для аргументов запроса какие-либо недостатки? Производительность? Что-то ещё?


Пример:

  1. Обычный способ:

    $args = array(
            'post_type'         => 'my-post',
            'posts_per_page'    => 8,
            'orderby'           => 'rand', 
        );
    }
    
    $results = new WP_Query( $args );
    
  2. С фильтром:

    //В одном файле -> легко найти и изменить
    add_filter( 'some_args', 'some_search_args' );
    
    function some_search_args( $search_args ) {
        $search_args['post_type'] = 'property';
        $search_args['posts_per_page'] = 8;
        $search_args['orderby'] = 'rand';
    
        //Любая логика и условный код может быть здесь
    
        return $search_args;
    }
    
    
    //И
    
    
    //Просто включаем так в любой шаблон сколько угодно раз
    //Для изменения запроса нужно будет поменять только код выше
    $search_args = array();
    $search_args = apply_filters( 'some_args', $search_args );
    
    $results = new WP_Query( $search_args );
    
0
Все ответы на вопрос 1
4

Здесь необходимо учесть довольно много моментов, и похоже, что вы стремитесь повысить производительность запроса. Первый и самый важный вопрос, который вы должны задать себе:

Нужен ли мне кастомный запрос

Я уже делал подробный пост на эту тему некоторое время назад, который вам стоит прочитать. Если после прочтения моего поста по ссылке вы ответили "да" на вопрос выше, то при создании кастомных запросов нужно учесть следующее:

  • Избегайте (где это возможно) сложных операций orderby, таких как сортировка по мета-значениям. SQL не лучший инструмент для сортировки, и иногда PHP справляется быстрее. Я предпочитаю использовать usort() для сложной сортировки, чтобы сэкономить ресурсы. Случайная сортировка также очень ресурсоемка.

  • Избегайте (где это возможно) построения сложных запросов с вложенными мета- и таксономическими запросами, особенно с большим количеством операторов OR. Они сильно нагружают ресурсы.

  • Избегайте (где это возможно) использования операторов LIKE в генерируемом SQL. Они тоже дорогостоящие.

  • Используйте транзиенты (и кеши) для хранения ресурсоемких запросов. Для случайных запросов это не сработает, поэтому нужно искать другие методы решения этой проблемы.

  • Всегда избегайте типичного цикла foreach, где вы получаете список терминов и затем запускаете кастомный запрос для каждого из них — это очень ресурсоемко. Лучше запросить все записи сразу и затем использовать usort() для сортировки результатов.

  • Создавайте запрос в соответствии с вашими потребностями. Чаще всего нам нужно запросить записи только для получения их ID, чтобы передать их в другую функцию. В таких случаях запрашивайте только ID записей. Это значительно экономит ресурсы. Просто добавьте 'fields'=>'ids', в аргументы вашего запроса.

  • Для ускорения запросов без пагинации используйте get_posts() или просто передайте 'no_found_rows'=>true в WP_Query (это именно то, что делает `get_posts`). Это пропускает процесс пагинации и экономит много ресурсов на больших базах данных.

Это всего лишь руководство для ускорения запроса. Есть и другие способы оптимизации.

Есть ли недостатки у использования фильтров для аргументов запроса? Производительность? Что-то еще?

Я не вижу причин для проблем. Если вы создаете коммерческую тему, то делаете все правильно. Возможность фильтрации значительно облегчает жизнь авторам дочерних тем. Это может занять миллисекунду или даже меньше, но это время потрачено не зря. Это как санация. Санация требует времени и ресурсов (хотя и очень мало), но потраченная миллисекунда может спасти ваш сайт от взлома и уничтожения.

По моему мнению, стоит искать другие способы ускорения запроса, не жертвуя удобством и поддерживаемостью. Вариант 2 определенно стоит реализовать для коммерческих тем.

ИДЕЯ (Возможно, немного перебор ;-))

Вы также можете использовать pre_get_posts для фильтрации вашего кастомного запроса и сделать его настраиваемым. Это так же просто, как установить собственный параметр в запросе и затем использовать его для таргетинга.

В следующем примере мы используем кастомный параметр query_no, которому присваиваем числовые значения.

Запросы

$q1 = new WP_Query( ['query_no' => 1] );    
$q2 = new WP_Query( ['query_no' => 2] );    
$q3 = new WP_Query( ['query_no' => 3] );    

pre_get_posts

add_action( 'pre_get_posts', function( $q ) 
{
    if ( $q->get( 'query_no' ) == 1 ) {
        $q->set( 'posts_per_page', -1 );
        // Добавьте любые другие дополнительные аргументы
    }

    if ( $q->get( 'query_no' ) == 2 ) {
        $q->set( 'post_type', ['post', 'page'] );
        // Добавьте любые другие дополнительные аргументы
    }

    if ( $q->get( 'query_no' ) == 3 ) {
        $q->set( 'post_status', 'trash' );
        // Добавьте любые другие дополнительные аргументы
    }
} );

Теперь пользователь может добавлять дополнительные аргументы или изменять переданные.

add_action( 'pre_get_posts', function( $q ) 
{
    if ( $q->get( 'query_no' ) == 2 ) {
        // Добавим еще один тип записи
        $post_types = $q->get( 'post_type' );
        $post_types = array_merge( $post_types, ['my_post_type'] );

        $q->set( 'post_type'     , $post_types );
        $q->set( 'posts_per_page', -1          );
        // Добавьте любые другие дополнительные аргументы
    }

}, 
11 // Убедитесь, что это выполняется после стандартного действия
); 
2 янв. 2016 г. 09:02:11
Комментарии

Что ты думаешь о транзиентах, когда результаты запроса одинаковые? Глупый пример, но все же: запрос выполняется через ajax -> устанавливается транзиент -> и когда пользователь спамит кнопку без изменения полей ввода (я поставил защиту от спама на ajax-кнопки, но все же) -> получаем результаты из транзиентов? Я спрашиваю, потому что мой огромный запрос связан с картой, и перемещение/масштабирование вызывает точно такой же запрос, так как отображение например 100 000 маркеров убивает любой браузер.

N00b N00b
2 янв. 2016 г. 12:56:22

Просто посмотри связанные посты под usort(). Я создаю уникальные имена транзиентов, беря аргументы, затем использую md5() для создания уникального ключа, который добавляю к имени транзиента. Таким образом, если аргументы запроса меняются, создается уникальный транзиент. Любой последующий вызов с теми же аргументами просто загружает результаты из транзиента.

Pieter Goosen Pieter Goosen
2 янв. 2016 г. 13:46:27

Извини, наверное я был слеп, потому что раньше этого не видел.. Спасибо за очень подробный ответ.

N00b N00b
2 янв. 2016 г. 13:49:47

Без проблем, мы все ещё ощущаем последствия Нового года, хахаха. Наслаждайтесь ;-)

Pieter Goosen Pieter Goosen
2 янв. 2016 г. 13:50:47