Поиск только записей блога (стандартный виджет поиска WP)

21 февр. 2016 г., 22:23:17
Просмотры: 18.3K
Голосов: 4

Я использовал этот код:

add_filter( 'pre_get_posts','search_only_blog_posts' );

function search_only_blog_posts( $query ) {

    if ( $query->is_search ) {

        $query->set( 'post_type', 'post' );
    }
    return $query;
}

...пока не понял, что это применяется практически ко всем стандартным поискам в WordPress (включая поиск на странице списка записей в админке и т.д.).

Как сделать, чтобы виджет поиска искал только записи блога (не пользовательские типы записей, таксономии, изображения и т.д.) и не применялся к другим стандартным поискам WP (только к поиску через виджет) ?

Или проще создать собственный виджет поиска?

Я бы предпочел использовать встроенные возможности WordPress, а не изобретать велосипед.

0
Все ответы на вопрос 5
6

@PieterGoosen хорошо описал, почему ваш callback pre_get_posts вызывает проблему.

Вот альтернативное решение для ограничения нативного виджета поиска только типом записи post:

/**
 * Ограничиваем нативные виджеты поиска типом записи 'post'
 */
add_filter( 'widget_title', function( $title, $instance, $id_base )
{
    // Нацеливаемся на базовый поиск
    if( 'search' === $id_base )
        add_filter( 'get_search_form', 'wpse_post_type_restriction' );
    return $title;
}, 10, 3 );

function wpse_post_type_restriction( $html )
{
    // Запускаем только один раз
    remove_filter( current_filter(), __FUNCTION__ );

    // Добавляем скрытое поле post_type
    return str_replace( 
        '</form>', 
        '<input type="hidden" name="post_type" value="post" /></form>',
        $html 
    );
}  

где мы корректируем вывод функции get_search_form(), но только для виджетов поиска.

22 февр. 2016 г. 10:48:30
Комментарии

Довольно нестандартный способ использовать фильтр widget_title ;-)

Pieter Goosen Pieter Goosen
22 февр. 2016 г. 10:59:26

да, здесь он очень удобен, фильтр widget_display_callback был другой возможностью ;-) @PieterGoosen

birgire birgire
22 февр. 2016 г. 11:12:33

Спасибо за информацию, никогда не поздно и не стыдно научиться чему-то новому ;-)

Pieter Goosen Pieter Goosen
22 февр. 2016 г. 11:15:01

Оба ответа очень хорошие, но я склоняюсь к более короткому, который использует сам поиск WordPress. @PieterGoosen, разве WP_Widget не вызывает deprecated ошибку? Я не нашел альтернативы в интернете (сделал лишь несколько быстрых поисков, сейчас это не в приоритете).

N00b N00b
22 февр. 2016 г. 18:59:06

@N00b Нет, думаю, ты путаешь с устаревшими конструкторами PHP4. Как я сказал, мой код взят из ядра, просто имена изменены соответствующим образом ;-)

Pieter Goosen Pieter Goosen
22 февр. 2016 г. 19:02:32

@PieterGoosen А, это многое объясняет, видимо я не до конца понял deprecated ошибку.. В любом случае, спасибо. Могу использовать твой ответ, чтобы починить свои виджеты. Двух зайцев одним выстрелом, бах!

N00b N00b
22 февр. 2016 г. 19:05:35
Показать остальные 1 комментариев
1

Ваше использование pre_get_posts совершенно неверно.

  • pre_get_posts — это действие (action), а не фильтр. Проверьте исходный код

    do_action_ref_array( 'pre_get_posts', array( &$this ) );
    

    Да, add_filter работает, потому что add_action вызывает add_filter, поэтому ваш код будет работать. Но с точки зрения правильного использования — это просто неверно. Если что-то является действием, используйте add_action(). Это логично.

  • WP_Query::is_search (а также WP_Query::is_search()) возвращает true для любого запроса, в котором параметр s передан в WP_Query. Помните, что условные теги внутри WP_Query определяются не по URL, а по переданным в него query vars. Для основного запроса query vars передаются в WP_Query после разбора URL.

  • pre_get_posts изменяет все экземпляры WP_Query, query_posts и get_posts, как на фронтенде, так и в админке. Поэтому, если вы хотите воздействовать только на основной запрос, вам нужно его явно указать. Также, скорее всего, вам нужно обрабатывать только фронтенд, особенно для архивов и поисковых запросов.

Вот пример правильного использования pre_get_posts для основного запроса: (Вы можете заменить замыкание на обычную функцию, просто учтите, что анонимные функции нельзя удалить позже)

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin()         // Только фронтенд
         && $q->is_main_query() // Только основной запрос
         && $q->is_search()     // Только страница поиска
    ) {
        $q->set( 'post_type', ['my_custom_post_type', 'post'] );
    }
});

Что касается вашего вопроса о виджете поиска, вот что я выяснил:

  • Виджет поиска просто вызывает get_search_form()

  • Нет полезных фильтров для точечного изменения именно виджета поиска. Фильтры в get_search_form() влияют на все формы, использующие эту функцию.

Таким образом, вам нужно создать собственный виджет поиска с собственной формой.

Попробуйте следующий вариант: (Изменено на основе стандартного виджета поиска, не тестировалось)

class My_Custom_Search extends WP_Widget {
    /**
     * Настраивает новый экземпляр виджета поиска.
     *
     * @since 1.0.0
     * @access public
     */
    public function __construct() {
        $widget_ops = [
            'classname'   => 'widget_custom_search', 
            'description' => __( "Пользовательская форма поиска для вашего сайта.")
        ];
        parent::__construct( 'custom-search', _x( 'Пользовательский поиск', 'Мой пользовательский виджет поиска' ), $widget_ops );
    }

    /**
     * Выводит содержимое текущего виджета поиска.
     *
     * @since 1.0.0
     * @access public
     *
     * @param array $args     Аргументы вывода, включая 'before_title', 'after_title',
     *                        'before_widget', и 'after_widget'.
     * @param array $instance Настройки текущего виджета поиска.
     */
    public function widget( $args, $instance ) {
        /** Этот фильтр описан в wp-includes/widgets/class-wp-widget-pages.php */
        $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
        echo $args['before_widget'];

        if ( $title ) {
            echo $args['before_title'] . $title . $args['after_title'];
        }

        $form = '<form role="search" method="get" class="search-form" action="' . esc_url( home_url( '/' ) ) . '">
            <label>
                <span class="screen-reader-text">' . _x( 'Искать:', 'label' ) . '</span>
                <input type="search" class="search-field" placeholder="' . esc_attr_x( 'Поиск &hellip;', 'placeholder' ) . '" value="' . get_search_query() . '" name="s" title="' . esc_attr_x( 'Искать:', 'label' ) . '" />
            </label>
            <input type="hidden" value="post" name="post_type" id="post_type" />
            <input type="submit" class="search-submit" value="'. esc_attr_x( 'Поиск', 'кнопка отправки' ) .'" />
        </form>';

        echo $form;

        echo $args['after_widget'];
    }

    /**
     * Выводит форму настроек виджета поиска.
     *
     * @since 1.0.0
     * @access public
     *
     * @param array $instance Текущие настройки.
     */
    public function form( $instance ) {
        $instance = wp_parse_args( (array) $instance, ['title' => '')];
        $title = $instance['title'];
        ?>
        <p><label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Заголовок:'); ?> <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>" /></label></p>
        <?php
    }

    /**
     * Обрабатывает обновление настроек текущего виджета поиска.
     *
     * @since 1.0.0
     * @access public
     *
     * @param array $new_instance Новые настройки, введенные пользователем через
     *                            WP_Widget::form().
     * @param array $old_instance Старые настройки.
     * @return array Обновленные настройки.
     */
    public function update( $new_instance, $old_instance ) {
        $instance = $old_instance;
        $new_instance = wp_parse_args((array) $new_instance, ['title' => '')];
        $instance['title'] = sanitize_text_field( $new_instance['title'] );
        return $instance;
    }
}
22 февр. 2016 г. 09:08:16
Комментарии

Пожалуйста, ознакомьтесь с моим обновлением

Pieter Goosen Pieter Goosen
22 февр. 2016 г. 10:21:33
1

Вы можете просто добавить этот код в файл functions.php.

function SearchFilter($query) 
{
    // Фильтр поиска: устанавливаем тип записи только для постов (не страниц)
    if (($query->is_search)&&(!is_admin())) {
        $query->set('post_type', 'post');
    }
    return $query;
}

// Добавляем фильтр к запросам перед их выполнением
add_filter('pre_get_posts','SearchFilter');
12 апр. 2016 г. 15:53:09
Комментарии

Это сработало идеально — отлично!

manifestor manifestor
19 сент. 2018 г. 17:43:32
1

Просто вставьте этот код в файл functions.php вашей темы WordPress.

function wpdocs_my_search_form( $form ) {
$form = '<form role="search" method="get" id="searchform" class="searchform" action="' . home_url( '/' ) . '" >
<div><label class="screen-reader-text" for="s">' . __( 'Поиск по:' ) . '</label>
<input type="text" value="' . get_search_query() . '" name="s" id="s" />
<input type="hidden" value="post" name="post_type" id="post_type" />
<input type="submit" id="searchsubmit" value="'. esc_attr__( 'Поиск' ) .'" />
</div>
</form>';

return $form;
} add_filter( 'get_search_form', 'wpdocs_my_search_form' );
16 сент. 2017 г. 03:38:07
Комментарии

Пожалуйста, объясните, почему это помогает… в правке. Спасибо.

kaiser kaiser
16 сент. 2017 г. 12:22:12
2
-3

Пожалуйста, ознакомьтесь с этим кодом и настройками по следующей ссылке

22 февр. 2016 г. 08:30:44
Комментарии

Пожалуйста, опубликуйте реальный ответ, а не ссылку на какую-то страницу, где можно найти решение

Pieter Goosen Pieter Goosen
22 февр. 2016 г. 08:54:27

Более того, это то, что у автора уже есть, и это ничем ему не поможет...

flomei flomei
22 февр. 2016 г. 09:54:18