Esempio di Settings API con array
Sto utilizzando il libro Wrox WordPress plugin development come riferimento principale per iniziare con un nuovo plugin e capisco che tutte le impostazioni possono essere salvate come un unico array, ma il libro non fornisce un esempio di questo e tutti gli esempi che trovo sul web sembrano così diversi l'uno dall'altro. La seconda metà di un post di Konstantin mi avvicina alla soluzione, ma mi piacerebbe vedere un esempio più completo con campi multipli.

Risposta breve: i valori dell'attributo name
devono seguire lo schema option_name[array_key]
. Quindi, quando usi...
<input name="option_name[key1]">
<input name="option_name[key2]">
...ottieni un array come valore dell'opzione nella tua funzione di validazione:
array (
'key1' => 'some value',
'key2' => 'some other value'
)
PHP fa questo per te, non è una funzionalità di WordPress. :)
Come farlo funzionare con l'API delle impostazioni?
Supponiamo di volere questa pagina delle opzioni, e che tutti i valori siano salvati in una singola opzione e validati in una singola funzione.
La pagina delle opzioni
Abbiamo bisogno dell'hook admin_menu
e due funzioni: una per registrare la pagina, una per renderizzare l'output.
add_action( 'admin_menu', 't5_sae_add_options_page' );
function t5_sae_add_options_page()
{
add_options_page(
'Esempio API Impostazioni T5', // $page_title,
'T5 SAE', // $menu_title,
'manage_options', // $capability,
't5_sae_slug', // $menu_slug
't5_sae_render_page' // Callback
);
}
function t5_sae_render_page()
{
?>
<div class="wrap">
<h2><?php print $GLOBALS['title']; ?></h2>
<form action="options.php" method="POST">
<?php
settings_fields( 'plugin:t5_sae_option_group' );
do_settings_sections( 't5_sae_slug' );
submit_button();
?>
</form>
</div>
<?php
}
L'attributo action
del form deve essere options.php
, altrimenti la validazione non verrà chiamata. Guarda il sorgente PHP di wp-admin/options-permalink.php
– c'è una trappola nascosta do_settings_sections('permalink');
– ma non può funzionare perché l'attributo action
del form è sbagliato.
Ora, torniamo alla nostra pagina personalizzata. La facciamo meglio di WordPress.
Registrare impostazioni, sezioni e campi
Agganciamo admin_init
quando ne abbiamo bisogno e chiamiamo una funzione di registrazione.
if ( ! empty ( $GLOBALS['pagenow'] )
and ( 'options-general.php' === $GLOBALS['pagenow']
or 'options.php' === $GLOBALS['pagenow']
)
)
{
add_action( 'admin_init', 't5_sae_register_settings' );
}
La parte importante qui è: $GLOBALS['pagenow']
deve essere options-general.php
(per l'output) o options.php
(per la validazione). Non chiamare tutto il codice seguente ad ogni richiesta. La maggior parte dei tutorial e quasi tutti i plugin sbagliano questo punto.
Ok, registriamo come pazzi:
Recuperiamo i valori delle opzioni per la nostra pagina e li confrontiamo con alcuni valori predefiniti. Abbastanza semplice.
Registriamo un gruppo di impostazioni con il nome
plugin:t5_sae_option_group
. Mi piacciono i nomi con prefisso, sono più facili da ordinare e da capire in questo modo.Poi registriamo due sezioni, 1 e 2.
E aggiungiamo tre campi, due per la prima sezione, uno per la seconda. Passiamo il nome dell'opzione e il valore escapato alle funzioni di callback per ogni campo. I gestori dell'output non dovrebbero modificare i dati, ma solo aggiungere HTML.
function t5_sae_register_settings()
{
$option_name = 'plugin:t5_sae_option_name';
// Recupera le opzioni esistenti.
$option_values = get_option( $option_name );
$default_values = array (
'number' => 500,
'color' => 'blue',
'long' => ''
);
// Analizza i valori delle opzioni nelle chiavi predefinite, scarta il resto.
$data = shortcode_atts( $default_values, $option_values );
register_setting(
'plugin:t5_sae_option_group', // gruppo, usato per settings_fields()
$option_name, // nome opzione, usato come chiave nel database
't5_sae_validate_option' // callback di validazione
);
/* Nessun argomento ha relazione con il precedente register_setting(). */
add_settings_section(
'section_1', // ID
'Alcuni campi di testo', // Titolo
't5_sae_render_section_1', // stampa output
't5_sae_slug' // menu slug, vedi t5_sae_add_options_page()
);
add_settings_field(
'section_1_field_1',
'Un Numero',
't5_sae_render_section_1_field_1',
't5_sae_slug', // menu slug, vedi t5_sae_add_options_page()
'section_1',
array (
'label_for' => 'label1', // rende il nome del campo cliccabile,
'name' => 'number', // valore per l'attributo 'name'
'value' => esc_attr( $data['number'] ),
'option_name' => $option_name
)
);
add_settings_field(
'section_1_field_2',
'Seleziona',
't5_sae_render_section_1_field_2',
't5_sae_slug', // menu slug, vedi t5_sae_add_options_page()
'section_1',
array (
'label_for' => 'label2', // rende il nome del campo cliccabile,
'name' => 'color', // valore per l'attributo 'name'
'value' => esc_attr( $data['color'] ),
'options' => array (
'blue' => 'Blu',
'red' => 'Rosso',
'black' => 'Nero'
),
'option_name' => $option_name
)
);
add_settings_section(
'section_2', // ID
'Area di testo', // Titolo
't5_sae_render_section_2', // stampa output
't5_sae_slug' // menu slug, vedi t5_sae_add_options_page()
);
add_settings_field(
'section_2_field_1',
'Note',
't5_sae_render_section_2_field_1',
't5_sae_slug', // menu slug, vedi t5_sae_add_options_page()
'section_2',
array (
'label_for' => 'label3', // rende il nome del campo cliccabile,
'name' => 'long', // valore per l'attributo 'name'
'value' => esc_textarea( $data['long'] ),
'option_name' => $option_name
)
);
}
Tutti quei gestori di callback per le sezioni e i campi verranno chiamati automaticamente quando chiamiamo do_settings_sections( 't5_sae_slug' );
nella nostra pagina. Lo abbiamo già fatto, quindi dobbiamo solo...
Stampare i campi
Nota come sono costruiti gli attributi name
: il option_name
passato è la prima parte, la chiave dell'array segue tra parentesi quadre []
.
function t5_sae_render_section_1()
{
print '<p>Scegli un numero tra 1 e 1000, e seleziona un colore.</p>';
}
function t5_sae_render_section_1_field_1( $args )
{
/* Crea questo markup:
/* <input name="plugin:t5_sae_option_name[number]"
*/
printf(
'<input name="%1$s[%2$s]" id="%3$s" value="%4$s" class="regular-text">',
$args['option_name'],
$args['name'],
$args['label_for'],
$args['value']
);
// t5_sae_debug_var( func_get_args(), __FUNCTION__ );
}
function t5_sae_render_section_1_field_2( $args )
{
printf(
'<select name="%1$s[%2$s]" id="%3$s">',
$args['option_name'],
$args['name'],
$args['label_for']
);
foreach ( $args['options'] as $val => $title )
printf(
'<option value="%1$s" %2$s>%3$s</option>',
$val,
selected( $val, $args['value'], FALSE ),
$title
);
print '</select>';
// t5_sae_debug_var( func_get_args(), __FUNCTION__ );
}
function t5_sae_render_section_2()
{
print '<p>Prendi qualche nota.</p>';
}
function t5_sae_render_section_2_field_1( $args )
{
printf(
'<textarea name="%1$s[%2$s]" id="%3$s" rows="10" cols="30" class="code">%4$s</textarea>',
$args['option_name'],
$args['name'],
$args['label_for'],
$args['value']
);
}
Oh, ho introdotto una funzione t5_sae_debug_var()
. Eccola:
function t5_sae_debug_var( $var, $before = '' )
{
$export = esc_html( var_export( $var, TRUE ) );
print "<pre>$before = $export</pre>";
}
Utile per vedere se abbiamo ottenuto ciò che ci aspettavamo.
Ora, questo funziona abbastanza bene, ci serve solo una cosa:
Validare l'array delle opzioni
Poiché abbiamo usato la notazione con parentesi, il nostro valore è un array. Dobbiamo solo scorrere ogni elemento e validarlo.
function t5_sae_validate_option( $values )
{
$default_values = array (
'number' => 500,
'color' => 'blue',
'long' => ''
);
if ( ! is_array( $values ) ) // dati non validi
return $default_values;
$out = array ();
foreach ( $default_values as $key => $value )
{
if ( empty ( $values[ $key ] ) )
{
$out[ $key ] = $value;
}
else
{
if ( 'number' === $key )
{
if ( 0 > $values[ $key ] )
add_settings_error(
'plugin:t5_sae_option_group',
'number-too-low',
'Il numero deve essere tra 1 e 1000.'
);
elseif ( 1000 < $values[ $key ] )
add_settings_error(
'plugin:t5_sae_option_group',
'number-too-high',
'Il numero deve essere tra 1 e 1000.'
);
else
$out[ $key ] = $values[ $key ];
}
elseif ( 'long' === $key )
{
$out[ $key ] = trim( $values[ $key ] );
}
else
{
$out[ $key ] = $values[ $key ];
}
}
}
return $out;
}
Questo è piuttosto brutto; non userei un codice del genere in produzione. Ma fa quello che deve: restituisce un array di valori validati. WordPress serializzerà l'array, lo salverà nel database con il nome della nostra opzione e lo restituirà deserializzato quando chiameremo get_option()
.
Tutto questo funziona, ma è inutilmente complicato, otteniamo markup del 1998 (<tr valign="top">
), e molte ridondanze.
Usa l'API delle impostazioni quando devi. Come alternativa usa admin_url( 'admin-post.php' )
come azione del form (guarda il suo sorgente) e crea la pagina delle impostazioni completa con il tuo codice, probabilmente più elegante.
In realtà, devi farlo quando scrivi un plugin per la rete, perché l'API delle impostazioni non funziona lì.
Ci sono anche alcuni casi limite e parti incomplete che non ho menzionato qui – li scoprirai quando ne avrai bisogno. :)

Wow, grazie. Questo è molto utile. Nessuno degli altri post che ho letto menzionava nulla riguardo ai plugin di rete, che è un dettaglio importante che terrò a mente per il futuro.

Solo un'aggiunta a questo. Se stai cercando di visualizzare/memorizzare checkbox ho modificato il codice di callback in: '<input type="checkbox" id="%3$s" name="%1$s[%2$s] value="%4$s" ' . checked('on', $args['value'], false) . '/>'

Rivedendo la risposta sono perplesso dall'uso di plugin:t5_sae_option_group che include un singolo due punti. Ho cercato approfonditamente ma non ho trovato una spiegazione di questa sintassi. Potresti indicarmi una spiegazione di questo nella documentazione PHP per favore? Grazie

@user50909 : sembrano semplici identificatori di stringhe per me. La sintassi PHP non dovrebbe essere un fattore.

@user50909 I due punti sono solo un marcatore nella stringa. Rende più semplice trovare tutte le mie opzioni nel database.

Per il multisito, $GLOBALS['pagenow']
sembra essere sempre vuoto. Cosa dovremmo controllare prima di agganciare admin_init
per registrare le impostazioni?

Grazie! La query string creava problemi, quindi ho dovuto rimuoverla prima che funzionasse, ma per il resto perfetto.

Ciao @fuxia, hai menzionato di evitare di registrare l'impostazione quando non si è nella pagina necessaria e hai anche detto che la maggior parte degli sviluppatori sbaglia registrandola sempre. Potresti approfondire? Evitare l'esecuzione di codice non richiesto nella pagina corrente sembra qualcosa di positivo. Tuttavia, non sarebbe anche questa una cattiva pratica che interrompe la funzionalità di base? La funzione register_option()
aggiunge l'opzione a una variabile globale di WordPress che può essere recuperata con get_registered_settings()
. Perché avrebbero aggiunto una tale funzione se non dovessimo sempre registrare le nostre impostazioni? Grazie :)
