Как загрузить шаблон страницы из плагина?
Я хочу добавить шаблоны страниц в тему напрямую из плагина. Идея в том, чтобы шаблон отображался в выпадающем списке в разделе "Атрибуты страницы" (Page Attributes), и весь код должен находиться в плагине.
Есть ли какие-нибудь советы, как это реализовать?

Вы можете использовать фильтр theme_page_templates
для добавления шаблонов в выпадающий список шаблонов страниц следующим образом:
function wpse255804_add_page_template ($templates) {
$templates['my-custom-template.php'] = 'Мой шаблон';
return $templates;
}
add_filter ('theme_page_templates', 'wpse255804_add_page_template');
Теперь WordPress будет искать файл my-custom-template.php
в директории темы, поэтому вам нужно перенаправить его в директорию вашего плагина с помощью фильтра page_template
вот так:
function wpse255804_redirect_page_template ($template) {
if ('my-custom-template.php' == basename ($template))
$template = WP_PLUGIN_DIR . '/mypluginname/my-custom-template.php';
return $template;
}
add_filter ('page_template', 'wpse255804_redirect_page_template');
Подробнее об этом можно прочитать здесь: Добавление пользовательского шаблона страницы программно

начиная с версии 4.7 WordPress, приведенный код не будет работать. Измените эту строку с 'if ('my-custom-template.php' == basename ($template))' на $post = get_post(); $page_template = get_post_meta( $post->ID, '_wp_page_template', true ); if ('my-custom-template.php' == basename ($page_template ))

Это комбинация приведенного выше ответа и комментариев, которая в итоге сработала для меня.
Функция для добавления плагина в список доступных шаблонов:
function wpse255804_add_page_template ($templates) {
$templates['my-custom-template.php'] = 'Мой шаблон';
return $templates;
}
add_filter ('theme_page_templates', 'wpse255804_add_page_template');
Функция для указания пути к шаблону внутри плагина:
function wpse255804_redirect_page_template ($template) {
$post = get_post();
$page_template = get_post_meta( $post->ID, '_wp_page_template', true );
if ('my-custom-template.php' == basename ($page_template))
$template = WP_PLUGIN_DIR . '/mypluginname/my-custom-template.php';
return $template;
}
add_filter ('page_template', 'wpse255804_redirect_page_template');

Спасибо за помощь. Я немного изменил код, чтобы он работал с текущей версией WordPress. Также изменил его для поддержки более чем одного пользовательского шаблона.
Уверен, есть способ сделать это лучше, но у меня сработало.
/**
* Загрузка шаблона через плагин
*/
function yourname_add_page_template ($templates) {
$templates['page-one.php'] = 'Название здесь Один';
$templates['page-two.php'] = 'Название здесь Два';
$templates['page-three.php'] = 'Название здесь Три';
return $templates;
}
add_filter ('theme_page_templates', 'yourname_add_page_template');
function yourname_redirect_page_template ($template) {
$post = get_post();
$page_template = get_post_meta( $post->ID, '_wp_page_template', true );
if ('page-one.php' == basename ($page_template)) {
$template = WP_PLUGIN_DIR . '/pluginname/templates/page-one.php';
return $template;
}
elseif ('page-two.php' == basename ($page_template)) {
$template = WP_PLUGIN_DIR . '/pluginname/templates/page-two.php';
return $template;
}
elseif ('page-three.php' == basename ($page_template)) {
$template = WP_PLUGIN_DIR . '/pluginname/templates/page-three.php';
return $template;
}
}
add_filter ('page_template', 'yourname_redirect_page_template');

The below code snippet is very well thought out, it will try to look for a template in the plugin and if it can't find it there it will try to get it from the theme.
define( 'MY_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
define( 'MY_PLUGIN_TEMPLATE_DIR', MY_PLUGIN_DIR . '/templates/' );
add_filter( 'template_include', 'ibenic_include_from_plugin', 99 );
function ibenic_include_from_plugin( $template ) {
$new_template = '';
$provided_template_array = explode( '/', $template );
// This will give us archive.php
$new_template = end( $provided_template_array );
// Define single and archive template for custom post type 'portfolio'
if( is_singular('portfolio') ) {
$new_template = 'single-portfolio.php';
}
if( is_post_type_archive( 'portfolio' ) ) {
$new_template = 'archive-portfolio.php';
}
$plugin_template = MY_PLUGIN_TEMPLATE_DIR . $new_template;
if( file_exists( $plugin_template ) ) {
return $plugin_template;
}
return $template;
}
Source: https://www.ibenic.com/include-or-override-wordpress-templates/

Обновление за 2022 год: Приведенный ниже код добавит шаблон в список шаблонов страниц и зарегистрирует его в файлах вашего плагина.
// ------------добавление шаблона в список шаблонов страниц------------
function register_custom_template_list ($templates) {
// ------------регистрация отображаемого имени шаблона с именем файла------------
$templates['my-template.php'] = 'Название моего шаблона';
// ------------возврат списка шаблонов страниц с новым добавлением------------
return $templates;
}
// ------------привязка шаблона страницы к списку шаблонов------------
add_filter('theme_page_templates', 'register_custom_template_list');
// ------------регистрация шаблона страницы в файле------------
function render_custom_template_archive ($template) {
// ------------получение атрибутов текущей редактируемой страницы------------
$post = get_post();
$page_template = get_post_meta( $post->ID, '_wp_page_template', true );
// ------------проверка, выбран ли шаблон для пользовательского шаблона------------
if (basename ($page_template) == 'my-template.php'){
// ------------регистрация файла шаблона страницы для страницы------------
$template = dirname(__DIR__).'/path_to/my-template.php';
// ------------отображение содержимого пользовательского шаблона------------
return $template;
} else {
// ------------возврат выбранного шаблона, если пользовательский шаблон не выбран------------
return $template;
}
}
// ------------привязка шаблона страницы к файлу------------
add_filter('page_template', 'render_custom_template_archive');

@Rup Я добавил комментарии к коду, чтобы разница стала понятнее. Основное отличие в последней части "render_custom_template_archive". Оператор else гарантирует, что другие шаблоны страниц также могут загружаться. Принятый ответ/ответ TKEz сломал мой сайт на WP и заставил все страницы использовать мой кастомный шаблон. Мне пришлось полностью сбросить сайт, чтобы он заработал (к счастью, это был тестовый сайт).

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

Теперь, когда WordPress поддерживает использование так называемых шаблонов страниц (Page Templates) и для других типов записей, обновлённый пример, который работает с постами, пользовательскими типами записей, а также страницами, может быть полезен.
/*
Plugin Name: Example Plugin Templates
Plugin URI:
Description: Загрузка шаблонов страниц (или типов записей) из плагина
Version: 0.1
Requires at least: 6.0
Requires PHP: 7
Author: t31os
Author URI:
License: GPL v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/
namespace t31os\Plugin;
if( !defined('ABSPATH') )
exit;
class Plugin_Templates {
private $types;
private $args;
private $folder;
private $templates = [
'template-name-1.php' => 'Шаблон плагина 1'
,'template-name-2.php' => 'Шаблон плагина 2'
,'template-name-3.php' => 'Шаблон плагина 3'
];
public function __construct() {
// Хук с поздним приоритетом, чтобы плагины/темы успели зарегистрировать типы записей
add_action( 'init', [$this, 'on_init'], 5000 );
}
public function on_init() {
// Фильтры, потому что почему бы и нет!
$this->args = apply_filters( 'my_plugin_template_type_args', [ 'public' => true ] );
$this->folder = apply_filters( 'my_plugin_template_directory', __DIR__ . '/templates/' );
$this->templates = apply_filters( 'my_plugin_template_file_list', $this->templates );
// Устанавливаем типы записей
$this->types = get_post_types( $this->args );
// Мы не используем шаблоны страниц для вложений
unset( $this->types['attachment'] );
// Добавляем пользовательские файлы в список шаблонов
add_filter( 'theme_templates', [$this,'add_plugin_templates'], 1000, 4 );
// Если страница — один из типов
if( isset( $this->types['page'] ) )
add_filter( 'page_template', [$this,'set_plugin_page_template'], 1000, 3 );
// И обрабатываем другие типы записей
add_filter( 'single_template', [$this,'set_plugin_type_template'], 1000, 3 );
}
public function add_plugin_templates( $post_templates, $obj, $post, $post_type ) {
if( !isset( $this->types[$post_type] ) )
return $post_templates;
foreach( $this->templates as $file => $name )
$post_templates[$file] = $name;
return $post_templates;
}
private function is_plugin_template( $file ) {
return (bool) isset( $this->templates[$file] ) && file_exists( $this->folder . $file );
}
public function set_plugin_page_template( $template, $type, $templates ) {
if( !isset( $templates[0] ) )
return $template;
if( $this->is_plugin_template( $templates[0] ) )
return $this->folder . $templates[0];
return $template;
}
public function set_plugin_type_template( $template, $type, $templates ) {
if( !isset( $templates[0] ) || 'single' !== $type )
return $template;
if( $this->is_plugin_template( $templates[0] ) )
return $this->folder . $templates[0];
return $template;
}
// Простая функция для отладки / проверки значений
private function pre( $s ) {
printf( '<pre>%s</pre>', print_r( $s, true ) );
}
}
new Plugin_Templates;
Я постарался не быть слишком абстрактным и сохранить логику достаточно простой. Если возникнут проблемы — оставьте комментарий.

<?php load_template( $_template_file, $require_once ) ?>
Это из первых результатов поиска Google по запросу "load template from plugin". Пожалуйста, попытайтесь найти ответы самостоятельно, прежде чем спрашивать здесь.

Если вы не хотите, чтобы тема или дочерняя тема могли переопределять шаблон плагина, просто используйте require($template)
