Как получить пользовательский HTML для списка дочерних страниц в WordPress (с вторичной относительной ссылкой)? В настоящее время используется регулярное выражение для обработки вывода wp_list_pages().

Вопрос или проблема

Я использую wp_list_pages(), чтобы вывести все дочерние страницы текущей просматриваемой страницы. Это работает достаточно хорошо, но я хотел бы добавить URL потока комментариев с иконкой рядом с каждой дочерней страницей.

Мое решение заключалось в том, чтобы грубо использовать регулярное выражение и хитрое страивание. Это работает. Но мне кажется неправильным, что я не смог найти более «вордпрессный» способ сделать то, что я сделал. Например, использовать какой-то базовый шаблон для вывода.

Вот мое текущее решение.

$string = '';
$meta_id = get_post_meta($post->ID,'meta',true);
$parent_meta_id = get_post_meta($post->post_parent,'meta',true);
if ( is_page() ){
    $childpages = wp_list_pages( 'sort_column=post_name&title_li=&child_of=" . $post->ID . "&echo=0&depth=1&exclude=".$meta_id.",'.$parent_meta_id );
}
if ( $childpages ) {
    $rows = explode('</li>',$childpages);
    $string = '<div class="subtopics container"><ul class="om_page_list list-unstyled card-columns" style="column-count: 3;">';
    foreach($rows as $row){
        $match = array();
        if(''!=trim($row)){
            preg_match_all('#\bhttps?://[^,\s()<>]+(?:\([\w\d]+\)|([^,[:punct:]\s]|/))#', $row, $match);
            if(0<count($match)){
                $feedlink = '<a href="'.$match[0][0].'feed/"><img src="https://openmentions.com/wp-content/themes/openmention/Feed-icon.svg" style="width:1em;" title="Тема по RSS Feed"/></a>';
                $row = str_replace('<a href', $feedlink.' <a href', $row);
            }
            $string .= $row;   
        }
    }
    $string.='</ul></div>';
} 

Вы можете видеть, что я вставил хак preg в середину, чтобы добавить дополнительную ссылку перед ссылкой. Я заметил, что есть параметр link_before, но я не мог понять, как сделать так, чтобы он также содержал URL страницы (URL потока — это тот же самый URL плюс “feed/”). Я мог бы использовать его в качестве цели для str_replace, но строка "<a href" хорошо справляется с этой задачей, если вы помните вернуть ее обратно после.

Существует ли нативный способ WordPress получить тот же результат?

wp_list_pages() передает найденный массив страниц в walk_page_tree(), который использует Walker_Page для отрисовки списка, если не передан пользовательский экземпляр walker в wp_list_pages() с аргументом array( 'walker' => $instance ).

Walker_Page является просто расширением класса Walker WordPress, который используется “для отображения различных древовидных структур“. У этого класса есть переопределяемые публичные методы для рендеринга уровней и элементов дерева – их начала и конца.

Это означает, что мы можем создать свой собственный класс расширения от Walker_Page и переопределить и изменить метод start_el(), чтобы соответствовать нашим требованиям.

Вот пример, где я сначала использую метод родителя, чтобы отобразить элемент списка во временной вспомогательной переменной, затем делаю простой str_replace(), чтобы добавить ссылку на поток перед ссылкой на страницу, и наконец добавляю полностью завершенный HTML-элемент к фактическому выводу, который передается в метод по ссылке.

class WPSE_421764_Custom_Page_Walker extends Walker_Page
{
    public function start_el( &$output, $data_object, $depth = 0, $args = array(), $current_object_id = 0 )
    {
        $element="";
        parent::start_el( $element, $data_object, $depth, $args, $current_object_id );

        $page = $data_object;

        $feed_link = sprintf(
            '><a href="%1$s/feed">
                <img src="https://openmentions.com/wp-content/themes/openmention/Feed-icon.svg" style="width:1em;" title="Тема по RSS Feed"/>
            </a><a',
            esc_attr( get_permalink( $page->ID ) )
        );

        $output .= str_replace( '><a', $feed_link, $element );
    }
}

Walker_Page использует формат <li%s><a%s>%s%s%s</a> с sprintf() для создания начала элемента, и именно оттуда берется ><a для вышеупомянутого str_replace().

Я использую замену строки здесь, чтобы избежать переписывания всего метода. Если вы хотите настроить метод от начала до конца, то я бы рекомендовал использовать исходный код Walker_Page::start_el() в качестве справочного материала и вдохновения.

А чтобы сделать дело немного чище, вы можете создать вспомогательную функцию, которая обернет рендеринг списка в красивую упаковку.

function wpse_421764_child_pages_list( int $post_id, int $parent_id = 0 ): string {
    $meta_id = get_post_meta( $post_id, 'meta', true );
    $parent_meta_id = get_post_meta( $parent_id, 'meta', true );

    $childpages = is_page( $post_id ) ? wp_list_pages( array(
        'sort_column' => 'post_name',
        'title_li' => '',
        'child_of' => $post_id,
        'echo' => 0,
        'depth' => 1,
        'walker' => new WPSE_421764_Custom_Page_Walker(),
        'exclude' => implode( ',', array_filter( array( $meta_id, $parent_meta_id ) ) ),
    ) ) : '';

    return $childpages ? sprintf(
            '<div class="subtopics container">
                <ul class="om_page_list list-unstyled card-columns" style="column-count: 3;">%s</ul>
            </div>',
            $childpages
        ) : '';
}

Использование,

echo wpse_421764_child_pages_list( $post->ID, $post->post_parent );

Ответ или решение

Для создания кастомного HTML-вывода списка дочерних страниц в WordPress с добавлением ссылки на ленту комментариев и иконкой, вместо использования регулярных выражений для обработки вывода wp_list_pages(), можно использовать встроенный класс Walker_Page. Это позволит избежать неэстетичных и ненадежных обходных решений, таких как preg_replace или str_replace.

Вот пошаговое руководство по созданию кастомного Walker_Page, который будет выводить нужные элементы в формате, который вы хотите.

Шаг 1: Создание кастомного класса Walker

Сначала создадим новый класс, расширяющий Walker_Page. Мы переопределим метод start_el(), чтобы добавить иконку ленты перед ссылкой на страницу.

class WPSE_421764_Custom_Page_Walker extends Walker_Page {
    public function start_el(&$output, $data_object, $depth = 0, $args = array(), $current_object_id = 0) {
        $element = "";
        parent::start_el($element, $data_object, $depth, $args, $current_object_id);

        $page = $data_object;
        $feed_link = sprintf(
            '<a href="%1$s/feed"><img src="https://openmentions.com/wp-content/themes/openmention/Feed-icon.svg" style="width:1em;" title="Тема по RSS Feed"/></a><a',
            esc_url(get_permalink($page->ID))
        );

        $output .= str_replace('<a', $feed_link, $element);
    }
}

Шаг 2: Создание функции для вывода списка дочерних страниц

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

function wpse_421764_child_pages_list(int $post_id, int $parent_id = 0): string {
    $meta_id = get_post_meta($post_id, 'meta', true);
    $parent_meta_id = get_post_meta($parent_id, 'meta', true);

    $childpages = is_page($post_id) ? wp_list_pages(array(
        'sort_column' => 'post_name',
        'title_li' => '',
        'child_of' => $post_id,
        'echo' => 0,
        'depth' => 1,
        'walker' => new WPSE_421764_Custom_Page_Walker(),
        'exclude' => implode(',', array_filter(array($meta_id, $parent_meta_id))),
    )) : '';

    return $childpages ? sprintf(
            '<div class="subtopics container">
                <ul class="om_page_list list-unstyled card-columns" style="column-count: 3;">%s</ul>
            </div>',
            $childpages
        ) : '';
}

Шаг 3: Использование функции

Теперь, когда функция и класс готовы, вы можете использовать их в своём шаблоне:

echo wpse_421764_child_pages_list($post->ID, $post->post_parent);

Заключение

Таким образом, вместо использования регулярных выражений для обработки вывода функции wp_list_pages(), мы создали кастомный вариант Walker_Page, который позволяет нам добавлять дополнительные элементы в HTML-вывод. Это более идиоматичный и надежный способ решения задачи в WordPress, что делает ваш код более чистым и поддерживаемым.

Оцените материал
Добавить комментарий

Капча загружается...