Come validare i campi personalizzati in un custom post type?
Ho sviluppato un plugin che crea un custom post type con campi personalizzati. Per evitare che gli utenti inseriscano informazioni errate, come posso validare i dati?
Pensavo che la funzione dell'hook save_post potesse gestire la validazione dei campi, ma non riesco a trovare un metodo semplice per mostrare gli errori all'utente.
Esiste una funzione integrata in WordPress per questo? Qual è la tecnica generalmente usata per implementare la validazione dei campi personalizzati?

Stai procedendo nella giusta direzione. Testo i campi nel callback save_post
e poi uso avvisi di amministrazione per mostrare gli errori all'utente quando un campo non supera la validazione. Appaiono in una casella evidenziata nella parte superiore della pagina, proprio come qualsiasi errore/messaggio generato da WordPress stesso.
Ecco un semplice esempio di creazione di un avviso di amministrazione:
function my_admin_notice()
{
?>
<div class="updated">
<p>Aenean eros ante, porta commodo lacinia.</p>
</div>
<?php
}
add_action( 'admin_notices', 'my_admin_notice' );
Non è molto pratico, però. In una situazione come questa, vorresti semplicemente una funzione a cui puoi passare un messaggio. Qualcosa tipo,
if( $pizza != 'warm' )
$notices->enqueue( 'La pizza non è calda', 'error' );
Quindi, puoi scrivere tu stesso la funzione enqueue()
(insieme a una funzione per stampare gli avvisi), oppure puoi includere una libreria come IDAdminNotices.
Ecco un esempio tratto da un plugin che ho scritto. Questo utilizza funzioni di accodamento/stampa degli avvisi integrate nella classe stessa, anziché includere una libreria esterna.
public function saveCustomFields( $postID )
{
// ...
if( filter_var( $_POST[ self::PREFIX . 'zIndex'], FILTER_VALIDATE_INT ) === FALSE )
{
update_post_meta( $post->ID, self::PREFIX . 'zIndex', 0 );
$this->enqueueMessage( 'L\'ordine di sovrapposizione deve essere un numero intero.', 'error' );
}
else
update_post_meta( $post->ID, self::PREFIX . 'zIndex', $_POST[ self::PREFIX . 'zIndex'] );
// ...
}
add_action( 'save_post', array( $this, 'saveCustomFields' );

Non sono del tutto chiaro su questo... il tuo codice di esempio richiede codice di terze parti, o funzionerà in WordPress così com'è?

Sembra funzionare solo quando la pagina viene caricata per la prima volta. Se si clicca il pulsante "Aggiorna/Pubblica", non vengono visualizzati avvisi.

Entrambi gli approcci funzionano per me. Puoi pubblicare un link al codice completo?

Codice di esempio: http://pastebin.com/vTxv9cw1

Penso che il problema sia che stai usando una variabile globale invece di salvare gli avvisi nel database. I dati nelle variabili non persistono tra le richieste. Quindi, l'avviso viene aggiunto a $bh_errorMessages durante la richiesta di salvataggio, poi WordPress reindirizza nuovamente alla schermata di modifica dell'articolo (che è una nuova richiesta), e tutte le variabili vengono reimpostate. Vedi http://en.wikipedia.org/wiki/Post/Redirect/Get. Dai un'occhiata più da vicino a IDAdminNotices e assicurati di fare tutto ciò che fa.

Non sono del tutto chiaro su cosa stai usando per permettere alle variabili di persistere tra le richieste. È l'inclusione della variabile $instance
? O è uno degli statement add_action
? O entrambi? Qualcos'altro?

ok, penso di aver capito. La magia sta nelle funzioni WordPress add_option
e get_option
per memorizzare e recuperare la variabile, e l'hook shutdown
è utile per chiamare la funzione add_option
solo una volta per sessione dopo che tutto è stato fatto.

Ho scritto un piccolo plugin che non solo valida i campi di input sui custom post type, ma rimuove anche il messaggio predefinito di amministrazione, senza l'uso di Javascript.
ecco parte del codice
/ Filtri di validazione
$title = $album->post_title;
if ( ! $title ) {
$errors['title'] = "Il titolo è obbligatorio";
}
// se abbiamo errori impostiamo alcuni messaggi
if (! empty($errors)) {
// dobbiamo rimuovere questa azione altrimenti entrerà in loop infinito
remove_action('save_post', 'album_save_post');
// salviamo gli errori come opzione
update_option('album_errors', $errors);
// Cambiamo lo stato del post da pubblicato a bozza
$album->post_status = 'draft';
// aggiorniamo il post
wp_update_post( $album );
// dobbiamo riaggiungere questa azione
add_action('save_post', 'album_save_post');
// admin_notice viene creato da un $_GET['message'] con un numero che WordPress usa per
// mostrare il messaggio di amministrazione, quindi aggiungeremo un filtro per sostituire il messaggio predefinito con un reindirizzamento
add_filter( 'redirect_post_location', 'album_post_redirect_filter' );
}
Puoi vedere il tutorial completo qui

Quando save_post
viene eseguito, il post è già stato salvato nel database.
Se stai utilizzando ACF, ha una validazione integrata.
Tuttavia, se hai bisogno di validare elementi al di fuori di ACF, come post_title, le cose diventano un po' più complicate.
Analizzando il codice core di WordPress, più precisamente la funzione update_post()
in wp-includes/post.php
, non esiste un modo integrato per intercettare una richiesta prima che venga salvata nel database.
Tuttavia, possiamo agganciarci a pre_post_update
e utilizzare header()
e get_post_edit_link()
per impedire il salvataggio del post.
<?php
/**
* Esegue una validazione personalizzata sul custom post type "Site"
*/
function custom_post_site_save($post_id, $post_data) {
# Se si tratta solo di una revisione, non fare nulla.
if (wp_is_post_revision($post_id))
return;
if ($post_data['post_type'] == 'site') {
# In questo esempio, genereremo un errore per i titoli dei post con meno di 5 caratteri
if (strlen($post_data['post_title']) < 5) {
# Aggiungi una notifica
update_option('my_notifications', json_encode(array('error', 'Il titolo del post non può essere inferiore a 5 caratteri.')));
# E reindirizza
header('Location: '.get_edit_post_link($post_id, 'redirect'));
exit;
}
}
}
add_action( 'pre_post_update', 'custom_post_site_save', 10, 2);
/**
* Mostra notifiche personalizzate nel pannello di amministrazione di WordPress
*/
function my_notification() {
$notifications = get_option('my_notifications');
if (!empty($notifications)) {
$notifications = json_decode($notifications);
#notifications[0] = (string) Tipo di notifica: error, updated o update-nag
#notifications[1] = (string) Messaggio
#notifications[2] = (boolean) è dismissibile?
switch ($notifications[0]) {
case 'error': # rosso
case 'updated': # verde
case 'update-nag': # ?
$class = $notifications[0];
break;
default:
# Di default imposta error per sicurezza
$class = 'error';
break;
}
$is_dismissable = '';
if (isset($notifications[2]) && $notifications[2] == true)
$is_dismissable = 'is_dismissable';
echo '<div class="'.$class.' notice '.$is_dismissable.'">';
echo '<p>'.$notifications[1].'</p>';
echo '</div>';
# Resetta la notifica
update_option('my_notifications', false);
}
}
add_action( 'admin_notices', 'my_notification' );

Vorrei solo aggiungere all'eccellente risposta di @Lucas Bustamante che il valore dei campi personalizzati può essere accessibile tramite la variabile globale $_POST
.
Quindi, la risposta di @Lucas Bustamante è valida anche se la validazione si riferisce a un campo personalizzato. Ad esempio:
<?php
/**
* Esegue una validazione personalizzata sul custom post type "Site"
*/
function custom_post_site_save($post_id, $post_data) {
# Se si tratta solo di una revisione, non fare nulla.
if (wp_is_post_revision($post_id))
return;
if ($post_data['post_type'] == 'site') {
# In questo esempio, genereremo un errore per i titoli dei post con meno di 5 caratteri
if (strlen($_POST['zip_code']) < 5) {
# Aggiungi una notifica
update_option('my_notifications', json_encode(array('error', 'Il codice postale non può essere inferiore a 5 caratteri.')));
# E reindirizza
header('Location: '.get_edit_post_link($post_id, 'redirect'));
exit;
}
}
}
add_action( 'pre_post_update', 'custom_post_site_save', 10, 2);
/**
* Mostra notifiche personalizzate nel pannello di amministrazione di WordPress
*/
function my_notification() {
$notifications = get_option('my_notifications');
if (!empty($notifications)) {
$notifications = json_decode($notifications);
#notifications[0] = (string) Tipo di notifica: error, updated o update-nag
#notifications[1] = (string) Messaggio
#notifications[2] = (boolean) è chiudibile?
switch ($notifications[0]) {
case 'error': # rosso
case 'updated': # verde
case 'update-nag': # ?
$class = $notifications[0];
break;
default:
# Imposta error come default per sicurezza
$class = 'error';
break;
}
$is_dismissable = '';
if (isset($notifications[2]) && $notifications[2] == true)
$is_dismissable = 'is_dismissable';
echo '<div class="'.$class.' notice '.$is_dismissable.'">';
echo '<p>'.$notifications[1].'</p>';
echo '</div>';
# Resetta la notifica
update_option('my_notifications', false);
}
}
add_action( 'admin_notices', 'my_notification' );

puoi utilizzare il filtro "wp_insert_post_data" per modificare o verificare i dati prima che vengano inseriti nel database
add_filter( 'wp_insert_post_data', 'validate_posttypes' );
function wpb_custom_excerpt($data) {
str_starts_with($data['post_title'],'wp')?$data['post_title']='wp'.$data['post_title']:null;
return $data;
}
