Создание пользовательской страницы архива для произвольного типа записи в плагине WordPress

27 сент. 2013 г., 22:04:49
Просмотры: 18.3K
Голосов: 16

Я разрабатываю плагин, который создает произвольный тип записи "my_plugin_lesson":

$args = array (
    'public' => true,
    'has_archive' => true,
    'rewrite' => array('slug' => 'lessons', 'with_front' => false)
);
register_post_type ('my_plugin_lesson', $args);

У этого типа записи есть архив, и его URL выглядит так:

http://example.com/lessons

Я хочу кастомизировать внешний вид этого архива - выводить записи в табличном формате вместо стандартного архива WordPress. Я знаю, что можно создать кастомный шаблон архива в теме через файл archive-my_plugin_lesson.php, но мне нужно, чтобы плагин работал с любой темой.

Как я могу изменить содержимое архивной страницы без добавления или модификации файлов темы?

Дополнение: Я понимаю, что можно использовать хук archive_template. Однако это просто заменяет шаблон темы, который все равно должен быть специфичным для каждой темы. Например, практически каждому шаблону темы нужны функции get_header, get_sidebar и get_footer, но каким должен быть id контентного <div>? В каждой теме он разный.

Что я хочу сделать - это заменить само содержимое своим собственным контентом и использовать его вместо стандартной архивной страницы для моего типа записи.

0
Все ответы на вопрос 4
1
17

Вам необходимо подключить фильтр template_include и выборочно загружать ваш шаблон внутри плагина.

Как хорошая практика, если вы планируете распространять ваш плагин, следует проверять существует ли archive-my_plugin_lesson.php (или, возможно, myplugin/archive-lesson.php) в теме, и если нет - использовать версию из плагина.

Таким образом пользователи смогут легко заменить шаблон через тему (или дочернюю тему), не редактируя код плагина.

Этот метод используется популярными плагинами, например WooCommerce, если называть один из них.

add_filter('template_include', 'lessons_template');

function lessons_template( $template ) {
  if ( is_post_type_archive('my_plugin_lesson') ) {
    $theme_files = array('archive-my_plugin_lesson.php', 'myplugin/archive-lesson.php');
    $exists_in_theme = locate_template($theme_files, false);
    if ( $exists_in_theme != '' ) {
      return $exists_in_theme;
    } else {
      return plugin_dir_path(__FILE__) . 'archive-lesson.php';
    }
  }
  return $template;
}

Больше информации в Кодексе:

28 сент. 2013 г. 14:38:19
Комментарии

Это всё ещё просто заменяет файл шаблона темы, верно? Что мне нужно поместить в файл archive-lesson.php моего плагина? Он должен быть разным, чтобы работать с каждой темой. Даже стандартные темы "Twenty" не согласованы в том, какие div/section контейнеры окружают контент.

Ben Miller Ben Miller
29 сент. 2013 г. 07:35:37
0

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

Схема заключается в загрузке шаблона в строку ($tpl_str) в фильтре archive_template, замене вашего контента, включении строки (с использованием трюка eval( '?>' . $tpl_str );), а затем возврате пустого файла, чтобы include в "wp-includes/template-loader.php" стал пустой операцией.

Ниже представлена модифицированная версия кода, который я использую в плагине. Он ориентирован на "классические" шаблоны, использующие get_template_part, и больше предназначен для обработки одиночных шаблонов, чем архивов, но поможет вам начать. В настройке плагин имеет подкаталог "templates", который содержит пустой файл ("null.php") и шаблоны контента (например, "content-single-posttype1.php", "content-archive-postype1.php"), а также резервный шаблон "single.php" для одиночного случая. Используется пользовательская версия get_template_part, которая ищет файлы в этом каталоге.

define( 'MYPLUGIN_FOLDER', dirname( __FILE__ ) . '/' );
define( 'MYPLUGIN_BASENAME', basename( MYPLUGIN_FOLDER ) );

add_filter( 'single_template', 'myplugin_single_template' );
add_filter( 'archive_template', 'myplugin_archive_template' );

function myplugin_single_template( $template ) {
    static $using_null = array();

    // Укажите свои пользовательские типы записей.
    $post_types = array( 'posttype1', );

    if ( is_single() || is_archive() ) {
        $template_basename = basename( $template );
        // Эту проверку можно убрать.
        if ( $template == '' || substr( $template_basename, 0, 4 ) == 'sing' || substr( $template_basename, 0, 4 ) == 'arch' ) {
            $post_type = get_post_type();
            $slug = is_archive() ? 'archive' : 'single';
            if ( in_array( $post_type, $post_types ) ) {
                // Позволяет пользователю переопределить.
                if ( $single_template = myplugin_get_template( $slug, $post_type ) ) {
                    $template = $single_template;
                } else {
                    // Если ещё не проходили через всё это...
                    if ( empty( $using_null[$slug][$post_type] ) ) {
                        if ( $template && ( $content_template = myplugin_get_template( 'content-' . $slug, $post_type ) ) ) {
                            $tpl_str = file_get_contents( $template );
                            // Вам придётся адаптировать эти регулярные выражения под свой случай — удачи!
                            if ( preg_match( '/get_template_part\s*\(\s*\'content\'\s*,\s*\'' . $slug . '\'\s*\)/', $tpl_str, $matches, PREG_OFFSET_CAPTURE )
                            || preg_match( '/get_template_part\s*\(\s*\'content\'\s*,\s*get_post_format\s*\(\s*\)\s*\)/', $tpl_str, $matches, PREG_OFFSET_CAPTURE )
                            || preg_match( '/get_template_part\s*\(\s*\'content\'\s*\)/', $tpl_str, $matches, PREG_OFFSET_CAPTURE )
                            || preg_match( '/get_template_part\s*\(\s*\'[^\']+\'\s*,\s*\'' . $slug . '\'\s*\)/', $tpl_str, $matches, PREG_OFFSET_CAPTURE ) ) {
                                $using_null[$slug][$post_type] = true;
                                $tpl_str = substr( $tpl_str, 0, $matches[0][1] ) . 'include \'' . $content_template . '\'' . substr( $tpl_str, $matches[0][1] + strlen( $matches[0][0] ) );
                                // Этот трюк включает строку $tpl_str.
                                eval( '?>' . $tpl_str );
                            }
                        }
                    }
                    if ( empty( $using_null[$slug][$post_type] ) ) {
                        // Не удалось разобрать — ищем резервный шаблон.
                        if ( file_exists( MYPLUGIN_FOLDER . 'templates/' . $slug . '.php' ) ) {
                            $template = MYPLUGIN_FOLDER . 'templates/' . $slug . '.php';
                        }
                    } else {
                        // Успех! "null.php" — это просто пустой файл без содержимого.
                        $template = MYPLUGIN_FOLDER . 'templates/null.php';
                    }
                }
            }
        }
    }
    return $template;
}

function myplugin_archive_template( $template ) {
    return myplugin_single_template( $template );
}

Пользовательская версия get_template_part:

/*
 * Версия WP get_template_part(), которая ищет в теме, затем в родительской теме и, наконец, в каталоге шаблонов плагина (подкаталог "templates").
 * Также изначально ищет в подкаталоге "myplugin" в темах и родительских темах, чтобы шаблоны плагина можно было хранить отдельно.
 */
function myplugin_get_template( $slug, $part = '' ) {
    $template = $slug . ( $part ? '-' . $part : '' ) . '.php';

    $dirs = array();

    if ( is_child_theme() ) {
        $child_dir = get_stylesheet_directory() . '/';
        $dirs[] = $child_dir . MYPLUGIN_BASENAME . '/';
        $dirs[] = $child_dir;
    }

    $template_dir = get_template_directory() . '/';
    $dirs[] = $template_dir . MYPLUGIN_BASENAME . '/';
    $dirs[] = $template_dir;
    $dirs[] = MYPLUGIN_FOLDER . 'templates/';

    foreach ( $dirs as $dir ) {
        if ( file_exists( $dir . $template ) ) {
            return $dir . $template;
        }
    }
    return false;
}

Для полноты приведён резервный "single.php", который использует пользовательскую версию get_template_part:

<?php
get_header(); ?>

    <div id="primary" class="content-area">
        <div id="content" class="clearfix">
            <?php while ( have_posts() ) : the_post(); ?>

            <?php if ( $template = myplugin_get_template( 'content-single', get_post_type() ) ) include $template; else get_template_part( 'content', 'single' ); ?>

                <?php
                    // Если комментарии открыты или их хотя бы один, загружаем шаблон комментариев
                    if ( comments_open() || '0' != get_comments_number() ) :
                        comments_template();
                    endif;
                ?>

            <?php endwhile; ?>

        </div><!-- #content -->
    </div><!-- #primary -->

<?php get_sidebar(); ?>
<?php get_footer(); ?>
11 сент. 2014 г. 19:35:11
2

Я размышлял над тем же вопросом и придумал следующее гипотетическое решение:

  • Внутри плагина создать шорткод, который выводит архивный цикл так, как вам нужно.
  • При создании пользовательского типа записи не включать опцию 'archive'.
  • Добавить таблицу стилей, которая управляет всеми стилями содержимого вашего цикла.

При активации плагина создать страницу с помощью wp_insert_post, где имя будет соответствовать типу записи, а содержимое - шорткоду.

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

30 мая 2015 г. 22:32:43
Комментарии

Хотя я не автор исходного вопроса, я искал решение той же проблемы. Я попробовал ваше гипотетическое решение и теперь могу подтвердить, что оно работает и на практике.

Lucio Crusca Lucio Crusca
6 янв. 2016 г. 16:21:01

Отлично! Рад, что это кому-то пригодилось. Я уже совсем забыл об этом.

SkyShab SkyShab
6 янв. 2016 г. 17:50:20
1

Вы можете использовать фильтр single_template. Базовый пример, взятый из Кодекса:

function get_custom_post_type_template($single_template) {
     global $post;

     if ($post->post_type == 'my_post_type') {
          $single_template = dirname( __FILE__ ) . '/post-type-template.php';
     }
     return $single_template;
}

add_filter( "single_template", "get_custom_post_type_template" );
27 сент. 2013 г. 22:18:41
Комментарии

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

Ben Miller Ben Miller
28 сент. 2013 г. 07:49:09