Вопрос или проблема
Я использую 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, что делает ваш код более чистым и поддерживаемым.