Verifica aggiornamento vs nuovo post nell'azione save_post
È possibile all'interno dell'azione save_post determinare se si tratta di un nuovo post in fase di creazione o di un post esistente che viene aggiornato?

Dalla versione 3.7 di WordPress - se non ricordo male - l'hook save_post
- maggiori informazioni sull'hook e il suo utilizzo su Code Reference: save_post
e Codex: save_post
- include un terzo parametro $update
che può essere utilizzato proprio per determinare questo.
@param int $post_ID ID del post.
@param WP_Post $post Oggetto post.
@param bool $update Indica se si tratta di un post esistente in fase di aggiornamento o meno.
Nota:
$update
non è sempre true
– puoi verificarlo e testarlo tu stesso con il codice qui sotto. Tuttavia, non è ben documentato, probabilmente il nome non è ottimale e quindi crea aspettative fuorvianti. Il codice seguente può essere utilizzato per eseguire qualche debug, gioca con i punti in cui interrompere l'esecuzione del codice, altrimenti non vedrai le informazioni/messaggi. Penso che il colpevole del comportamento ingannevole sia la gestione delle revisioni e dei salvataggi automatici – che potrebbero essere disabilitati, ma non lo consiglio e non l'ho testato. Non sono sicuro se questo meriti un Trac Ticket, quindi non ne ho aperto uno, se pensi di sì, segui il link e fallo tu. A parte questo, come detto nei commenti, se hai un problema specifico, apri una nuova domanda.
add_action( 'save_post', 'debug_save_post_update', 10, 3 );
function debug_save_post_update( $ID, $post, $update ) {
echo '<pre>';
print_r( $post ); echo '<br>';
echo '$update == ';
echo $update ? 'true' : 'false';
//condizioni
if( ! $update && $post->post_status == "auto-draft" ) {
// si applica ai nuovi post
echo ' && $post->post_status == "auto-draft"';
//die();
} else if ( ! $update ) {
// si applica fondamentalmente alla revisione (salvata automaticamente)
//die();
} else {
// si applica all'aggiornamento di un post pubblicato
// quando c'è una revisione, che normalmente è il caso,
// comportamento standard di WordPress, allora viene considerato
// un aggiornamento, ed è qui che nasce la confusione
// ci sono altri metodi, come controllare il tempo o lo stato del post
// a seconda del tuo caso d'uso potrebbe essere più appropriato
// utilizzare una di queste alternative
//die();
}
echo '</pre>';
//die();
}

Il parametro $update
è SEMPRE vero anche quando si tratta di un nuovo post. Quindi questo parametro è inutile. Non sono sicuro se abbia mai funzionato, ma di sicuro non funziona come documentato nell'ultima versione di WordPress 4.8.

@SolomonClosson Se guardi wp_publish_post
, allora sì. Ma questo non è vero per il suo utilizzo in wp_insert_post
. Ho scritto una funzione di debug, l'ho aggiunta alla risposta.

@SolomonClosson Se hai un problema concreto, per favore fai una nuova domanda. Dai un'occhiata alle revisioni per la funzione di debug e una spiegazione.

L'hook save_post
ha un terzo parametro che è sempre impostato su TRUE, quindi non sono sicuro di cosa c'entri con altri hook, senza parlare di altri hook. Sto parlando dell'hook nella tua risposta. Questo è incorretto.

@SolomonClosson Come ho detto, l'hook si verifica due volte:
wp_insert_post()
, wp_publish_post()
. Quest'ultimo riguarda solo i post futuri, lì $update
è impostato per essere sempre true
. Altrimenti, per quanto riguarda wp_insert_post()
, $update
non è sempre true
.

Arrivo in ritardo alla festa, ma ehi, perché no, @Nicolai, quello che dici è ciò che ci si aspetterebbe accadesse, ma non riesco a ottenere che $update sia false anche quando mi collego a 'wp_insert_post'. Non importa dove lo collego, è sempre true. Non capisco perché questo non sia stato risolto.

Il modo in cui eseguo questo controllo (all'interno di una funzione agganciata) è confrontare la data di pubblicazione e la data di modifica (in GMT per standardizzazione)
function check_new_vs_update( $post_id ){
$myPost = get_post($post_id);
$created = new DateTime( $myPost->post_date_gmt );
$modified = new DateTime( $myPost->post_modified_gmt );
$diff = $created->diff( $modified );
$seconds_difference = ((($diff->y * 365.25 + $diff->m * 30 + $diff->d) * 24 + $diff->h) * 60 + $diff->i)*60 + $diff->s;
if( $seconds_difference <= 1 ){
// Nuovo post
}else{
// Post aggiornato
}
}
add_action('save_post', 'check_new_vs_update' );
Questo funziona perché anche alla creazione il post ha una data di 'modifica' associata, che è esattamente uguale alla data di 'creazione', ma permettiamo una variazione di 1 secondo in caso il secondo cambi durante la creazione del post.

A volte il post_date_gmt
è 2019-03-12 01:31:30
e il post_modified_gmt
è 2019-03-12 01:31:31
. :(

@HeYifei何一非 ottimo punto, se l'elaborazione inizia alla fine di un determinato secondo, ciò potrebbe accadere. Ho aggiornato la mia risposta, grazie

Ragazzi, solo un'informazione. L'hook viene attivato durante il ripristino e l'eliminazione di un post.

Non posso credere che debba essere così complicato. WordPress non smette mai di stupirmi.

Per essere onesti con loro, hanno letteralmente milioni di utenti ed è un progetto open-source. Purtroppo non possono includere ogni funzionalità e questa probabilmente non è una delle più utilizzate. Negli ultimi anni cose come migliorare l'editor e la sicurezza sono state prioritarie e questo è positivo! Voglio dire, persino jQuery viene aggiornato solo ora! Fino alla versione attuale era rimasto alla 1.x!

Alla fine ho semplicemente verificato l'esistenza di un valore personalizzato prima di impostarlo. In questo modo, se si tratta di un post appena creato, il valore personalizzato non esisterebbe ancora.
function attributes_save_postdata($post_id) {
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if (!wp_verify_nonce($_POST['_attributes_noncename'], plugin_basename(__FILE__))) return;
if ('page' == $_POST['post_type']) {
if (!current_user_can('edit_page', $post_id)) return;
} else {
if (!current_user_can('edit_post', $post_id)) return;
}
$termid = get_post_meta($post_id, '_termid', true);
if ($termid != '') {
// è un nuovo record
$termid = 'update';
} else {
// è un record esistente
}
update_post_meta($post_id, '_termid', $termid);
}
add_action('save_post', 'attributes_save_postdata');

Per farlo funzionare, devi prima creare il campo personalizzato usando add_post_meta?

Secondo il Codex: [update_post_meta] può essere utilizzato al posto della funzione add_post_meta(). http://codex.wordpress.org/Function_Reference/update_post_meta

Questo potrebbe fallire, se i post sono stati creati prima che l'hook del codice venga abilitato tramite l'attivazione di un plugin. I post più vecchi non hanno il meta impostato, quindi il primo aggiornamento per loro sarà considerato come nuovo.

Esempio di risposta a ialocin con il parametro "update":
function save_func($ID, $post,$update) {
if($update == false) {
// fai qualcosa se è la prima pubblicazione
} else {
// Fai qualcosa se è un aggiornamento
}
}
add_action( 'save_post', 'save_func', 10, 3 );

Un modo migliore per strutturare questo codice sarebbe posizionare prima il blocco di aggiornamento, permettendo di usare semplicemente if($update)
, oppure mantenere prima il nuovo blocco ma usando if( ! $update )
. Quest'ultimo approccio porterà OP verso una pratica migliore ed è preferito rispetto al tuo metodo dagli standard di codifica WordPress in casi come l'operatore ternario

Ho appena incontrato l'azione save_post
riguardo la creazione e l'aggiornamento. Dopo aver letto il codice sorgente per comprenderne il flusso, ho scoperto che il seguente metodo potrebbe essere utile. Dato che non è stato ancora menzionato prima, vedo se può essere utile a qualcuno. (Il test è stato fatto su Core 5.3.3)
Il flusso di creazione di un Post è approssimativamente:
- Dopo aver premuto "Aggiungi nuovo" (Post)
- Viene chiamato
$post = get_default_post_to_edit( $post_type, true );
dove get_default_post_to_edit()
riceve l'argomento$create_in_db = true
- Quindi
wp_insert_post()
viene chiamato immediatamente, viene creato un postauto-draft
, anche se non è salvato. Ogni volta che si clicca su "Aggiungi nuovo", viene creato unauto-draft
$update
è sempre true per "Pubblica". Quindi quando si pubblica un nuovo post, è true.
Confrontando l'oggetto $_POST
per un nuovo post e per l'aggiornamento o ripubblicazione di un post, la differenza principale è il valore _wp_http_referer
: per un nuovo post è /wp-admin/post-new.php
Assunzione: si assume che il post sia pubblicato/aggiunto dall'interfaccia utente. Se viene fatto tramite altri meccanismi o codice personalizzato, è necessario adattare i controlli.
add_action( 'save_post', 'test_save_post_check', 0, 3 );
function test_save_post_check( $post_ID, $post, $update ) {
// altri test + questo
// controlla la presenza di 'post-new' nel '_wp_http_referer'
if( strpos( wp_get_raw_referer(), 'post-new' ) > 0 ) {
// nuovo
} else {
// aggiornamento
}
}

Ecco un codice funzionale che ho testato e utilizzato nel mio sito web, che risolve i seguenti due problemi associati all'azione save_post:
Problema nel distinguere tra aggiornamento o inserimento
Problema del doppio inserimento causato dall'azione save_post
function save_annonces_callback($post_ID, $post, $update){ $post_type = get_post_type($post_ID); if ( $post_type === 'annonces' ){ //questo previene il doppio inserimento dall'azione save_post :) if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) { return; } else { //controlla se è un nuovo post per l'inserimento if( strpos( wp_get_raw_referer(), 'post-new' ) > 0 ){ //esegui inserimento }else{ //esegui aggiornamento } } } }add_action('save_post','save_annonces_callback', 10, 3);

L'hook save_post
viene attivato sia quando un articolo viene creato che quando viene aggiornato (dopo che WordPress lo ha salvato nel database). pre_post_update
viene attivato quando un articolo viene aggiornato, ma prima che l'articolo venga effettivamente aggiornato - questo può essere importante.

Come ha suggerito Darshan Thanki (e Stephen Harris ha ulteriormente approfondito), puoi utilizzare pre_post_update
a tuo vantaggio.
global $___new_post;
$___new_post = true;
add_action(
'pre_post_update',
function() {
global $___new_post;
$___new_post = false;
},
0
);
function is_new_post() {
global $___new_post;
return $___new_post;
}
Il motivo per cui ho utilizzato le variabili globali è perché function is_new_post() use ( &$new_post )
non è valido in PHP (sorprendente...) quindi includere quella variabile nello scope della funzione non funziona -- da qui la soluzione con global.
Nota che questo approccio può essere utilizzato in modo affidabile solo all'interno/dopo l'evento save_post
(il che è solitamente sufficiente, almeno per quello che stiamo facendo con esso).

Quando save_post viene attivato, tutte le informazioni su quel post sono già disponibili, quindi in teoria potresti usare
function f4553265_check_post() {
if (!get_posts($post_id)) {
// se questo è un nuovo post, get_posts($post_id) dovrebbe restituire null
} else {
// $post_id esiste già nel database
}
}
add_action('save_post','f4553265_check_post');
però non è testato. =)

Quando arrivi a save_post
il post stesso sarebbe già stato salvato nel database - quindi get_posts
restituirebbe il post corrente.

Un altro approccio che utilizza una funzione integrata senza aggiungere dati al database sarebbe usare get_post_status()
.
$post_status = get_post_status();
if ( $post_status != 'draft' ) {
//bozza
} else {
//non è una bozza: può essere pubblicato, in attesa, ecc.
}
Tuttavia, nota che potrebbe non essere appropriato se hai intenzione di reimpostare successivamente lo stato su "bozza" – le tue istruzioni verrebbero ripetute la prossima volta che aggiornerai il post.
A seconda del contesto, potresti voler considerare le varie stringhe che possono essere restituite da get_post_status()
per costruire uno scenario più adatto.
Vedi il Codex per get_post_status() e Stato del Post
I valori possibili sono:
- 'publish' - Un post o pagina pubblicato
- 'pending' - post in attesa di revisione
- 'draft' - un post in stato di bozza
- 'auto-draft' - un post appena creato, senza contenuto
- 'future' - un post programmato per pubblicazione futura
- 'private' - non visibile agli utenti non loggati
- 'inherit' - una revisione. vedi get_children.
- 'trash' - post nel cestino. aggiunto dalla Versione 2.9.

Non credo che questo faccia ciò che è stato richiesto. Se creo un nuovo articolo e poi premo 'Pubblica', save_post()
viene eseguito per la prima volta, ma durante quella esecuzione get_post_status()
restituisce già 'publish' e non 'draft', anche se è solo in fase di pubblicazione.

Dato che il parametro $update
è inutile, questo è il metodo più veloce che ho testato:
function wpse48678_check_is_post_new($post_id, $post, $update)
{
if (false !== strpos($_POST['_wp_http_referer'], 'post-new.php')) {
return true; // Oppure fai qualcos'altro.
} else {
return false; // Oppure fai qualcos'altro.
}
}
add_action('save_post_{$post_type}', 'wpse48678_check_is_post_new', 10, 3);

Ho passato ore su questo
Sto attualmente sviluppando un plugin e volevo inviare una notifica a tutti gli autori quando una pagina veniva "Creata" sul sito della mia Web Design Company.
Inizialmente ho provato a usare il seguente codice:
add_action('save_post_page', function($post_id, $post, $update) {
if(!$update){
echo("È un nuovo post!");
}else{
echo("È un vecchio post!");
}
}
Continuava a mostrare "È un vecchio post!". Ma perché??? Dopo alcune ricerche ho scoperto che dopo aver cliccato il pulsante "Aggiungi nuova pagina" WordPress crea immediatamente una bozza automatica che imposta la variabile $update a true. Molto utile.
Dopo ore di ricerca ho finalmente trovato la seguente soluzione che funziona:
// Si attiva quando un post cambia "stato" ad esempio da bozza a in sospeso, da pubblicato a programmato.
add_action('transition_post_status', function($new_status, $old_status, $post) {
// Controlla se il post passa da bozza automatica a qualsiasi altro stato (bozza, in sospeso, pubblicato, ecc.)
if ('auto-draft' === $old_status && $new_status !== 'auto-draft'){
echo("È un nuovo post!");
}else{
echo("È un vecchio post!");
}
}
Nota: In WordPress, "bozza" (draft) e "bozza automatica" (auto-draft) sono diversi. Uno stato "bozza" è un post che un utente imposta manualmente come bozza, indicando che è un lavoro in corso. Una "bozza automatica", invece, viene assegnata automaticamente a un post quando viene creato ma non ancora pubblicato. Praticamente un modo diverso per dire salvataggio automatico.
Spero che questo aiuti alcune persone... Ora vado a passare altre 4 ore su un altro problema haha.

Questo dovrebbe funzionare ragazzi
add_action( 'wp_after_insert_post', 'clearCDNCache', 10, 4);
public function clearCDNCache($post_id, $post, $update, $post_before){
if ( ! empty($_POST['_wpnonce']) ) {
// Esegui la funzione personalizzata solo una volta
}
// Aggiungi nuova modalità
if (!empty($_POST['_wp_http_referer']) && $_POST['_wp_http_referer'] == '/wp-admin/post-new.php') {
} else {
// Modalità aggiornamento
}
}
