Respuesta corta: los name
valores de sus atributos deben usar el esquema option_name[array_key]
. Entonces, cuando usas ...
<input name="option_name[key1]">
<input name="option_name[key2]">
... obtienes una matriz como valor de opción en tu función de validación:
array (
'key1' => 'some value',
'key2' => 'some other value'
)
PHP lo hace por usted, esta no es una característica de WordPress. :)
¿Cómo hacer que eso funcione con la API de configuración?
Digamos que queremos esta página de opciones, y todos los valores deben almacenarse en una opción y validarse en una función.
La página de opciones
Necesitamos el gancho admin_menu
y dos funciones: una para registrar la página, otra para representar la salida.
add_action( 'admin_menu', 't5_sae_add_options_page' );
function t5_sae_add_options_page()
{
add_options_page(
'T5 Settings API Example', // $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
}
El formulario action
debe ser options.php
o no se llamará a la validación. Mire la fuente PHP de wp-admin/options-permalink.php
- hay una trampa oculta do_settings_sections('permalink');
- pero no puede funcionar porque el formulario action
es incorrecto.
Ahora, de vuelta a nuestra página personalizada. Lo hacemos mejor que WordPress.
Registrar configuraciones, secciones y campos
Nos conectamos admin_init
cuando lo necesitamos y llamamos a una función de registro.
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 aquí es: $GLOBALS['pagenow']
debe ser options-general.php
(para la salida) o options.php
(para la validación). No llame a todos los siguientes códigos en cada solicitud. La mayoría de los tutoriales y casi todos los complementos se equivocan.
Bien, registremos como locos:
Buscamos los valores de opción para nuestra página y los analizamos en función de algunos valores predeterminados. Bastante básico
Registramos un grupo de configuraciones con el nombre plugin:t5_sae_option_group
. Me gustan los nombres prefijados, son más fáciles de ordenar y entender de esta manera.
Luego registramos dos secciones, 1 y 2.
Y agregamos tres secciones, dos para la primera sección, una para la segunda. Pasamos el nombre de la opción y el valor escapado a las funciones de devolución de llamada para cada campo. Los controladores de salida no deben cambiar los datos, solo agregue algo de HTML.
function t5_sae_register_settings()
{
$option_name = 'plugin:t5_sae_option_name';
// Fetch existing options.
$option_values = get_option( $option_name );
$default_values = array (
'number' => 500,
'color' => 'blue',
'long' => ''
);
// Parse option values into predefined keys, throw the rest away.
$data = shortcode_atts( $default_values, $option_values );
register_setting(
'plugin:t5_sae_option_group', // group, used for settings_fields()
$option_name, // option name, used as key in database
't5_sae_validate_option' // validation callback
);
/* No argument has any relation to the prvious register_setting(). */
add_settings_section(
'section_1', // ID
'Some text fields', // Title
't5_sae_render_section_1', // print output
't5_sae_slug' // menu slug, see t5_sae_add_options_page()
);
add_settings_field(
'section_1_field_1',
'A Number',
't5_sae_render_section_1_field_1',
't5_sae_slug', // menu slug, see t5_sae_add_options_page()
'section_1',
array (
'label_for' => 'label1', // makes the field name clickable,
'name' => 'number', // value for 'name' attribute
'value' => esc_attr( $data['number'] ),
'option_name' => $option_name
)
);
add_settings_field(
'section_1_field_2',
'Select',
't5_sae_render_section_1_field_2',
't5_sae_slug', // menu slug, see t5_sae_add_options_page()
'section_1',
array (
'label_for' => 'label2', // makes the field name clickable,
'name' => 'color', // value for 'name' attribute
'value' => esc_attr( $data['color'] ),
'options' => array (
'blue' => 'Blue',
'red' => 'Red',
'black' => 'Black'
),
'option_name' => $option_name
)
);
add_settings_section(
'section_2', // ID
'Textarea', // Title
't5_sae_render_section_2', // print output
't5_sae_slug' // menu slug, see t5_sae_add_options_page()
);
add_settings_field(
'section_2_field_1',
'Notes',
't5_sae_render_section_2_field_1',
't5_sae_slug', // menu slug, see t5_sae_add_options_page()
'section_2',
array (
'label_for' => 'label3', // makes the field name clickable,
'name' => 'long', // value for 'name' attribute
'value' => esc_textarea( $data['long'] ),
'option_name' => $option_name
)
);
}
Todos esos controladores de devolución de llamada para las secciones y campos se llamarán automáticamente cuando llamemos do_settings_sections( 't5_sae_slug' );
a nuestra página. Ya lo hicimos, así que solo necesitamos ...
Imprimir los campos
Tenga en cuenta cómo name
se crean los atributos: lo aprobado option_name
es la primera parte, la clave de la matriz sigue entre corchetes []
.
function t5_sae_render_section_1()
{
print '<p>Pick a number between 1 and 1000, and choose a color.</p>';
}
function t5_sae_render_section_1_field_1( $args )
{
/* Creates this 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>Makes some notes.</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, introduje una función t5_sae_debug_var()
. Aquí está:
function t5_sae_debug_var( $var, $before = '' )
{
$export = esc_html( var_export( $var, TRUE ) );
print "<pre>$before = $export</pre>";
}
Útil para ver si obtuvimos lo que esperábamos.
Ahora, esto funciona bastante bien, solo necesitamos una cosa:
Validar la matriz de opciones
Debido a que usamos la notación de corchetes, nuestro valor es una matriz. Solo tenemos que recorrer cada elemento y validarlo.
function t5_sae_validate_option( $values )
{
$default_values = array (
'number' => 500,
'color' => 'blue',
'long' => ''
);
if ( ! is_array( $values ) ) // some bogus data
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',
'Number must be between 1 and 1000.'
);
elseif ( 1000 < $values[ $key ] )
add_settings_error(
'plugin:t5_sae_option_group',
'number-too-high',
'Number must be between 1 and 1000.'
);
else
$out[ $key ] = $values[ $key ];
}
elseif ( 'long' === $key )
{
$out[ $key ] = trim( $values[ $key ] );
}
else
{
$out[ $key ] = $values[ $key ];
}
}
}
return $out;
}
Esto es bastante feo; No usaría ese código en producción. Pero hace lo que debería: devuelve una matriz validada de valores. WordPress serializará la matriz, la almacenará con el nombre de nuestra opción en la base de datos y la devolverá sin serializar, cuando llamemos get_option()
.
Todo esto funciona, pero es innecesariamente complicado, obtenemos marcado desde 1998 ( <tr valign="top">
) y muchas redundancias.
Use la API de configuración cuando sea necesario. Como uso alternativo admin_url( 'admin-post.php' )
como acción de formulario (mire su fuente) y cree la página de configuración completa con su propio código, probablemente más elegante.
En realidad, debes hacerlo cuando escribes un complemento de red, porque la API de configuración no funciona allí.
También hay algunos casos extremos y partes incompletas que no mencioné aquí: los encontrará cuando los necesite. :)