Crea una pagina di archivio personalizzata per un custom post type in un plugin
Sto scrivendo un plugin che crea un custom post type chiamato "my_plugin_lesson":
$args = array (
'public' => true,
'has_archive' => true,
'rewrite' => array('slug' => 'lessons', 'with_front' => false)
);
register_post_type ('my_plugin_lesson', $args);
Il custom post type ha un archivio, e l'URL dell'archivio è:
http://example.com/lessons
Voglio personalizzare l'aspetto di questo archivio; voglio elencare i post in formato tabella, invece del classico archivio blog di WordPress. Capisco che si potrebbe creare un template di archivio personalizzato nel tema creando il file archive-my_plugin_lesson.php
; tuttavia, vorrei che il plugin funzioni con qualsiasi tema.
Come posso modificare il contenuto della pagina di archivio senza aggiungere o modificare file del tema?
Modifica:
Capisco che potrei usare l'hook filter archive_template
. Tuttavia, tutto ciò che fa è sostituire il template del tema, che deve comunque essere specifico per il tema. Ad esempio, quasi tutti i template del tema avranno bisogno delle funzioni get_header
, get_sidebar
, e get_footer
, ma quale dovrebbe essere l'id del <div>
del contenuto? Questo è diverso in ogni tema.
Quello che vorrei fare è sostituire il contenuto stesso con il mio contenuto, e usarlo al posto della pagina di archivio per il mio custom post type.

Quello di cui hai bisogno è agganciare il filtro template_include
e caricare selettivamente il tuo template all'interno del plugin.
Come buona pratica, se intendi distribuire il tuo plugin, dovresti verificare se archive-my_plugin_lesson.php
(o magari myplugin/archive-lesson.php
) esiste nel tema, altrimenti usa la versione del plugin.
In questo modo è facile per gli utenti sostituire il template tramite il tema (o child theme) senza modificare il codice del plugin.
Questo è il metodo utilizzato da plugin popolari, ad esempio WooCommerce, giusto per citare un nome.
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;
}
Maggiori informazioni sul Codex per

Questo sostituisce ancora solo il file template del tema, giusto? Cosa devo inserire nel file archive-lesson.php del mio plugin? Dovrebbe essere diverso per funzionare con ogni tema. Anche i temi predefiniti "Twenty" non sono d'accordo su quali siano i contenitori div/section che circondano il contenuto.

Puoi utilizzare l'hook archive_template
per elaborare il contenuto del template di archivio di un tema, utilizzando lo schema seguente. Tuttavia, riuscirai a elaborare solo una frazione dei temi esistenti, dato che un template può contenere praticamente qualsiasi cosa.
Lo schema consiste nel caricare il template in una stringa ($tpl_str
) nel filtro archive_template
, sostituire il tuo contenuto, includere la stringa (utilizzando il trucco eval( '?>' . $tpl_str );
) e poi restituire un file vuoto in modo che l'include
in "wp-includes/template-loader.php" diventi un'operazione nulla.
Di seguito trovi una versione modificata del codice che utilizzo in un plugin, che si rivolge ai template "classici" che usano get_template_part
ed è più orientato all'elaborazione di template singoli piuttosto che di archivio, ma può aiutarti a iniziare. L'impostazione prevede che il plugin abbia una sottodirectory chiamata "templates" che contiene un file vuoto ("null.php") e template di contenuto (ad esempio "content-single-posttype1.php", "content-archive-postype1.php"), oltre a un template di fallback "single.php" per il caso singolo, e utilizza una versione personalizzata di get_template_part
che cerca in questa directory.
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();
// Modifica con i tuoi custom post type.
$post_types = array( 'posttype1', );
if ( is_single() || is_archive() ) {
$template_basename = basename( $template );
// Questo controllo può essere rimosso.
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 ) ) {
// Permetti all'utente di sovrascrivere.
if ( $single_template = myplugin_get_template( $slug, $post_type ) ) {
$template = $single_template;
} else {
// Se non hai già passato tutto questo...
if ( empty( $using_null[$slug][$post_type] ) ) {
if ( $template && ( $content_template = myplugin_get_template( 'content-' . $slug, $post_type ) ) ) {
$tpl_str = file_get_contents( $template );
// Dovrai adattare queste regex al tuo caso - buona fortuna!
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] ) );
// Questo trucco include la $tpl_str.
eval( '?>' . $tpl_str );
}
}
}
if ( empty( $using_null[$slug][$post_type] ) ) {
// Parsing fallito - cerca un template di fallback.
if ( file_exists( MYPLUGIN_FOLDER . 'templates/' . $slug . '.php' ) ) {
$template = MYPLUGIN_FOLDER . 'templates/' . $slug . '.php';
}
} else {
// Successo! "null.php" è semplicemente un file vuoto di zero byte.
$template = MYPLUGIN_FOLDER . 'templates/null.php';
}
}
}
}
}
return $template;
}
function myplugin_archive_template( $template ) {
return myplugin_single_template( $template );
}
La versione personalizzata di get_template_part
:
/*
* Versione di WP get_template_part() che cerca nel tema, poi nel tema genitore e infine nella directory dei template del plugin (sottodirectory "templates").
* Cerca inizialmente anche nella sottodirectory "myplugin" se presente nelle directory del tema e del tema genitore, in modo che i template del plugin possano essere mantenuti separati.
*/
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;
}
Per completezza, ecco il template di fallback "single.php", che utilizza la versione personalizzata di 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
// Se i commenti sono aperti o abbiamo almeno un commento, carica il template dei commenti
if ( comments_open() || '0' != get_comments_number() ) :
comments_template();
endif;
?>
<?php endwhile; ?>
</div><!-- #content -->
</div><!-- #primary -->
<?php get_sidebar(); ?>
<?php get_footer(); ?>

Mi sono posto la stessa domanda e questa è la soluzione ipotetica a cui sono arrivato:
- All'interno del plugin, crea uno shortcode che generi il tuo loop di archivio nel modo che desideri.
- Quando crei il custom post type, non abilitare l'opzione 'archive'.
- Aggiungi un foglio di stile che controlli tutti gli stili dei contenuti del tuo loop.
All'attivazione del plugin, crea una pagina usando wp_insert_post con il nome corrispondente al post type e il contenuto che sarà lo shortcode.
Puoi fornire opzioni nello shortcode per considerazioni stilistiche aggiuntive, oppure aggiungere classi al contenitore del post per adattarsi a stili specifici del tema o personalizzati. L'utente può anche aggiungere contenuti aggiuntivi prima/dopo il loop modificando la pagina.

Anche se non sono l'autore del post originale, stavo cercando una soluzione allo stesso problema. Ho seguito la tua soluzione ipotetica e posso confermare che funziona anche nella pratica.

Puoi utilizzare il filtro single_template
. Un esempio base preso dal Codex:
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" );
