Abbiamo visto che WordPress dispone di 8 diversi tipi di contenuto predefiniti, più uno. Si tratta dei cosiddetti custom post type e strutturalmente possono essere considerati delle pagine statiche, sebbene spesso vengano utilizzati a fini eterogenei, in quanto possono essere visualizzati anche in pagine di archivio, esattamente come i normali post.

Per utilizzare i custom post type bisogna procedere per fasi. Ecco come procederemo:

In questo post non andremo oltre. Nei prossimi articoli vedremo come personalizzare questi tipi di contenuto in modo più spinto creando nella pagina di amministrazione dei custom meta box con custom field avanzati.
Ma ora dedichiamoci ai custom post type.

Creazione di un plugin di sviluppo

La prima fase consiste nel predisporre la sandbox in cui creare i tipi di contenuto personalizzato. In breve, ci occorre un plugin in cui scrivere il nostro codice.

Apriamo la cartella /wp-content/plugins/ della nostra installazione e creiamo al suo interno una nuova cartella, assegnandole un nome univoco in lettere minuscole.

Attenzione: il nome di questa cartella sarà il nome del plugin e dovrà essere univoco sia nello stesso ambiente di sviluppo, sia nell’intera directory dei plugin di WordPress.

Il motivo è che WordPress cerca continuamente gli aggiornamenti del software installato. Se esiste un plugin nella directory con lo stesso nome del vostro plugin di sviluppo, WordPress sovrascriverà i file del vostro plugin con i file del plugin presente nella directory, cancellando definitivamente il vostro codice.

Per questi esempi, ho dato al plugin lo stesso nome del nostro blog: frammenti. Per sicurezza, però, facciamo prima una ricerca nella directory.

Non esiste nessun plugin con lo stesso nome nella directory di WordPress.org

Fatto il dovuto controllo, torniamo alla cartella del plugin /wp-content/plugins/frammenti/ e, al suo interno, creiamo il file frammenti.php. Nel file aggiungiamo il seguente codice:

<?php
/**
 * @package frammenti
 * @version 1.0
 */
/*
Plugin Name: Frammenti
Plugin URI: http://wordpress.org/extend/plugins/#
Description: This is a development plugin 
Author: Carlo Daniele
Version: 1.0
Author URI: https://frammentidicodice.com/
*/

/**
 * Register a custom post type
 */
function frammenti_plugin_init() {

}
add_action( 'init', 'frammenti_plugin_init' );

Noterete che il nome del plugin si ripete identico nella directory, nel file .php e nell’intestazione del plugin stesso. E non solo: utilizziamo il nome del plugin anche come prefisso del nome della funzione PHP, in modo da rendere univoca anche questa e non rischiare conflitti con plugin o temi presenti nella stessa installazione.

Il nostro task è la registrazione di un tipo di post personalizzato (custom post type) che utilizzeremo per creare un sistema di contenuti che memorizzi la knowledgebase di un generico sito. Il task viene eseguito dalla funzione frammenti_plugin_init(). Si tratta di una funzione di callback, ossia una funzione che viene passata come parametro ad un’altra funzione per essere invocata ed eseguita in un preciso momento dell’esecuzione del programma (una definizione più generale è disponibile su Wikipedia). Il momento in cui la funzione sarà eseguita viene stabilito dal parametro init. Nella terminologia di WordPress, questo è definito action hook e si attiva quando WordPress ha completato il caricamento, ma prima che venga inviato qualunque header (maggiori informazioni nel Codex).

Ora sappiamo come e quando viene invocata la funzione di callback che registrerà il nostro custom post type. Passiamo alla fase successiva.

Registrazione di un post type

La registrazione del post type avviene attraverso la funzione register_post_type. Insieme ai post type vanno registrate le tassonomie, come vedremo tra qualche paragrafo.

Il primo passo sarà la scelta del nome del post type. In questo siamo liberi di scegliere il nome che preferiamo, con le eccezioni dei nomi riservati e dei nomi di altri post type già presenti nell’installazione, che potrebbero essere stati registrati da un tema o da un altro plugin.

Fatta la dovuta premessa, passiamo al codice:

/**
 * Register a custom post type
 */
function frammenti_plugin_init() {
	// an array of labels
	$labels = array();

	// an array of arguments
	$args = array();

	// register the post type
	register_post_type( 'knowledgebase', $args );
}
add_action( 'init', 'frammenti_plugin_init' );

L’array $labels contiene le etichette associate al tipo di post visualizzate nel pannello di amministrazione. L’array $args, invece, contiene un elenco di argomenti che definiranno le caratteristiche del tipo di post. La funzione register_post_type registra il tipo di post assegnandogli le proprietà stabilite dall’array $args.

Definizione delle proprietà e delle etichette del custom post type

Definiamo, quindi, l’array $labels:

$labels = array(
	'name'			=> _x( 'KB articles', 'post type general name', 'frammenti' ),
	'singular_name'		=> _x( 'KB article', 'post type singular name', 'frammenti' ),
	'menu_name'		=> _x( 'KBs', 'admin menu', 'frammenti' ),
	'name_admin_bar'	=> _x( 'KB', 'add new on admin bar', 'frammenti' ),
	'add_new'		=> _x( 'Add New', 'KB', 'frammenti' ),
	'add_new_item'		=> __( 'Add New KB', 'frammenti' ),
	'new_item'		=> __( 'New KB', 'frammenti' ),
	'edit_item'		=> __( 'Edit KB', 'frammenti' ),
	'view_item'		=> __( 'View KB', 'frammenti' ),
	'all_items'		=> __( 'All KBs', 'frammenti' ),
	'search_items'		=> __( 'Search KBs', 'frammenti' ),
	'parent_item_colon'	=> __( 'Parent KBs:', 'frammenti' ),
	'not_found'		=> __( 'No KBs found.', 'frammenti' ),
	'not_found_in_trash'	=> __( 'No KBs found in Trash.', 'frammenti' )
);

Poi l’array di argomenti:

$args = array(
	'labels'		=> $labels,
	'description'		=> __( 'Description.', 'frammenti' ),
	'public'		=> true,
	'publicly_queryable'	=> true,
	'show_ui'		=> true,
	'show_in_menu'		=> true,
	'query_var'		=> true,
	'rewrite'		=> array( 'slug' => 'kb' ),
	'capability_type'	=> 'post',
	'has_archive'		=> true,
	'hierarchical'		=> false,
	'menu_position'		=> null,
	'supports'		=> array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'revisions' )
);

Il parametro 'labels' è l’array di etichette definito in precedenza. Per la descrizione delle altre variabili, faccio rinvio alla documentazione del Codex.

Passiamo alla registrazione del tipo di post:

register_post_type( 'knowledgebase', $args );

Ed ecco cosa succede nel nostro sito di sviluppo:

nuovo tipo di post
La dashboard di WordPress dopo la registrazione del nuovo tipo di post
$args = array(
	'labels'		=> $labels,
	'description'		=> __( 'Description.', 'frammenti' ),
	'public'		=> true,
	'publicly_queryable'	=> true,
	'show_ui'		=> true,
	'show_in_menu'		=> true,
	'query_var'		=> true,
	'rewrite'		=> array( 'slug' => 'kb' ),
	'show_in_rest'		=> true,
	'capability_type'	=> 'post',
	'has_archive'		=> true,
	'hierarchical'		=> false,
	'menu_position'		=> null,
	'supports'		=> array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'revisions' )
);

Se andiamo a creare un nuovo articolo della Knowledgebase, noteremo subito che questo mostra l’editor classico. Se volessimo (e vogliamo) abilitare l’editor Gutenberg per il tipo di post, dobbiamo aggiungere un nuovo parametro al nostro array di argomenti: show_in_rest. Questo stabilisce se esporre il tipo di post alla REST API di WordPress, e deve essere impostato a true per abilitare Gutenberg. Aggiorniamo, quindi, il nostro array $args come segue:

Saltiamo nella dashboard di WordPress e creiamo un nuovo KB. Apriamo, quindi, il pannello delle impostazioni per vedere quali elementi sono disponibili nella pagina.

nuovo kb
Un nuovo custom post type con l’editor Gutenberg

Registrazione delle tassonomie del post type

Una volta registrato il post type, noteremo subito che nella dashboard di WordPress la schermata non corrisponde esattamente alla schermata dei post. Mancano, in particolare, categorie e tag. Questo perché non abbiamo ancora associato una o più tassonomie al nostro tipo di post.

Procediamo, quindi, associando al tipo di post appena registrato le tassonomie predefinite, ossia tag e categorie. A questo scopo ci basta aggiungere un nuovo parametro all’array $args:

$args = array(
	'labels'		=> $labels,
	'description'		=> __( 'Description.', 'frammenti' ),
	'public'		=> true,
	'publicly_queryable'	=> true,
	'show_ui'		=> true,
	'show_in_menu'		=> true,
	'query_var'		=> true,
	'rewrite'		=> array( 'slug' => 'kb' ),
	'show_in_rest'		=> true,
	'capability_type'	=> 'post',
	'taxonomies'		=> array( 'category', 'post_tag' ),
	'has_archive'		=> true,
	'hierarchical'		=> false,
	'menu_position'		=> null,
	'supports'		=> array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'revisions' )
);

Le tassonomie che abbiamo aggiunto sono le stesse dei post, quindi non dovremo sorprenderci se nella pagina delle categorie degli articoli della KB troveremo gli stessi termini riportati nella pagina delle categorie dei post.
Salviamo il file e ricarichiamo la pagina.

custom post type con categorie e tag
La pagina di amministrazione del custom post type con categorie e tag

A questo punto la pagina degli articoli della Knowledgebase assomiglia in tutto e per tutto alla pagina di amministrazione degli articoli del blog.

Tassonomie predefinite e personalizzate

A questo punto ci dobbiamo fermare un attimo. Categorie e tag sono le tassonomie predefinite degli articoli del blog. È corretto utilizzarle per altri tipi di post? La risposta non è univoca. In linea di principio non ci sono problemi ad utilizzare le stesse tassonomie dei articoli del blog, a meno che questo non provochi delle incongruenze nell’architettura dei contenuti.

Il problema maggiore riguarda la visualizzazione dei contenuti nel front-end del sito. Gli archivi delle categorie e dei tag, infatti, visualizzano normalmente solo gli articoli del blog, e non eventuali altri contenuti aventi gli stessi termini delle tassonomie. In un altro articolo vedremo come creare pagine di archivio specifiche per un dato custom post type.

Supponiamo ora che i nostri articoli della KB necessitino di un terzo sistema di classificazione rispetto al sistema gerarchico delle categorie e semantico dei tag. In pura linea teorica, si potrebbe pensare ad una suddivisione dei contenuti per tipologia di pubblico: potremmo avere, ad esempio, un pubblico alle prime armi, un pubblico di utenti più o meno avanzati e un pubblico di sviluppatori e professionisti. Definiamo, quindi, una nuova tassonomia, cui assegniamo il nome di Audience.

La registrazione delle custom taxonomy avviene in modo molto simile alla registrazione dei tipi di post. Cambia il nome della funzione, ma la logica e i parametri sono gli stessi:

taxonomy_labels = array(
	'name'	=> _x( 'Audience', 'taxonomy general name', 'frammenti' )
);
$taxonomy_args = array(
	'hierarchical'      => false,
	'labels'            => $labels,
	'show_ui'           => true,
	'show_admin_column' => true,
	'query_var'         => true,
	'show_in_rest'      => true,
	'rewrite'           => array( 'slug' => 'audience' ),
);
register_taxonomy( 'audience', 'knowledgebase', $args );

Questo codice va inserito nella stessa funzione di callback agganciata all’action hook init. L’assegnazione del valore true al parametro show_in_rest è indispensabile affinché la tassonomia venga visualizzata all’interno della barra degli strumenti dell’editor a blocchi.

tassonomia in gutenberg
La tassonomia Audience nell’editor a blocchi di WordPress

La nuova tassonomia viene quindi associata al custom post type knowledgebase e l’associazione deve avvenire anche in senso contrario. Il codice completo diventa:

function frammenti_plugin_init() {

	$labels = array(
		'name'			=> _x( 'KB articles', 'post type general name', 'frammenti' ),
		'singular_name'		=> _x( 'KB article', 'post type singular name', 'frammenti' ),
		'menu_name'		=> _x( 'KB articles', 'admin menu', 'frammenti' ),
		'name_admin_bar'	=> _x( 'KB article', 'add new on admin bar', 'frammenti' ),
		'add_new'		=> _x( 'Add New KB', 'KB', 'frammenti' ),
		'add_new_item'		=> __( 'Add New KB', 'frammenti' ),
		'new_item'		=> __( 'New KB', 'frammenti' ),
		'edit_item'		=> __( 'Edit KB', 'frammenti' ),
		'view_item'		=> __( 'View KB', 'frammenti' ),
		'all_items'		=> __( 'All KB articles', 'frammenti' ),
		'search_items'		=> __( 'Search KB articles', 'frammenti' ),
		'parent_item_colon'	=> __( 'Parent KB:', 'frammenti' ),
		'not_found'		=> __( 'No KB found.', 'frammenti' ),
		'not_found_in_trash'	=> __( 'No KB found in Trash.', 'frammenti' )
	);

	$args = array(
		'labels'		=> $labels,
		'description'		=> __( 'Description.', 'frammenti' ),
		'public'		=> true,
		'publicly_queryable'	=> true,
		'show_ui'		=> true,
		'show_in_menu'		=> true,
		'query_var'		=> true,
		'rewrite'		=> array( 'slug' => 'kb' ),
		'show_in_rest'		=> true,
		'capability_type'	=> 'post',
		'taxonomies'		=> array( 'category', 'post_tag', 'audience' ),
		'has_archive'		=> true,
		'hierarchical'		=> false,
		'menu_position'		=> null,
		'supports'		=> array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'revisions' )
	);

	register_post_type( 'knowledgebase', $args );

	$taxonomy_labels = array(
		'name'	=> _x( 'Audience', 'taxonomy general name', 'frammenti' )
	);
	
	$taxonomy_args = array(
		'hierarchical'      => false,
		'labels'            => $taxonomy_labels,
		'show_ui'           => true,
		'show_admin_column' => true,
		'query_var'         => true,
		'show_in_rest'	    => true,
		'rewrite'           => array( 'slug' => 'audience' ),
	);

	register_taxonomy( 'audience', 'knowledgebase', $taxonomy_args );
	
}
add_action( 'init', 'frammenti_plugin_init' );

Visualizzazione del post type nel front-end del sito

Se creassimo i nostri primi articoli appartenenti al tuovo tipo di post, e subito dopo accedessimo al front-end del sito, non vedremmo traccia dei contenuti creati. Questo perché i contenuti personalizzati non entrano nel Loop degli articoli del blog, né, come abbiamo detto, nel Loop delle pagine di archivio.

Eppure WordPress ci offre da subito la possibilità di visualizzare questi contenuti in vario modo. Prima di procedere, però, dobbiamo assicurarci di aver aggiornato la struttura dei permalink. La registrazione del tipo di post e della tassonomia, infatti, apporta modifiche al sistema dei permalink che potrebbero non essere recepite immediatamente. Per questo basta andare alla pagina di amministrazione Impostazioni → Permalink e verificare che la struttura dei pretty permalink sia quella che desideriamo. In caso contrario, scegliamo la nostra struttura e salviamo i dati.

Impostazioni Permalink di WordPress
Impostazioni Permalink di WordPress

Per avere la prova che tutto funzioni correttamente, basta creare un URL con lo slug che abbiamo definito con l’argomento rewrite:

'rewrite' => array( 'slug' => 'kb' ),

Basterà aggiungere kb all’URL del sito, ad esempio:

https://miosito.com/kb/

Il risultato sarà la pagina di archivio di tutti i post di tipo knowledgebase.

Per la visualizzazione dei custom post type si possono anche sfruttare le funzionalità dei menu di navigazione dall’editor dei menu di WordPress. Andiamo nella dashboard alla pagina Aspetto → Menu. Qui saranno disponibili due nuovi gruppi di link: il nuovo tipo di post e la nuova tassonomia.

Due nuovi gruppi di menu
Due nuovi gruppi di menu

A questo punto possiamo creare link all’archivio generale degli articoli della knowledgebase, link ai singoli articoli e link alle tassonomie associate al tipo di post.

Il codice completo è disponibile su Gist