Comprobar actualización vs nueva entrada en la acción save_post
¿Es posible dentro de la acción save_post determinar si se está creando una nueva entrada o si se está actualizando una entrada existente?

Desde la versión 3.7 de WordPress - si mal no recuerdo - el hook save_post
- más información sobre el hook y su uso en Referencia de Código: save_post
y Codex: save_post
- tiene un tercer parámetro $update
que puede usarse para determinar precisamente eso.
@param int $post_ID ID del Post.
@param WP_Post $post Objeto del Post.
@param bool $update Indica si es una actualización de un post existente o no.
Nota:
$update
no siempre es true
– puedes verlo y probarlo tú mismo con el código de abajo. Sin embargo, no está bien documentado, posiblemente el nombre no es óptimo y por lo tanto crea expectativas engañosas. El siguiente código puede usarse para depurar, juega con cuándo interceptar la ejecución del código, porque de lo contrario no verás los mensajes/información. Creo que el culpable del comportamiento engañoso es el manejo de revisiones y auto guardados – los cuales podrían desactivarse, pero no lo recomiendo y no lo he probado. No estoy seguro si esto amerita un Ticket en Trac, así que no abrí uno, si crees que sí, por favor sigue el enlace y hazlo tú mismo. Aparte de eso, como se menciona en los comentarios, si tienes un problema específico, publica una nueva pregunta.
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';
//condiciones
if( ! $update && $post->post_status == "auto-draft" ) {
// aplica a un post nuevo
echo ' && $post->post_status == "auto-draft"';
//die();
} else if ( ! $update ) {
// aplica básicamente a la revisión (auto guardada)
//die();
} else {
// aplica a actualizar un post publicado
// cuando hay una revisión, que es lo normal,
// comportamiento estándar de WordPress, entonces se considera
// una actualización, que es donde surge la confusión
// hay otros métodos, como verificar el tiempo o el estado del post
// dependiendo de tu caso de uso podría ser más apropiado
// usar alguna de esas alternativas
//die();
}
echo '</pre>';
//die();
}

El parámetro $update
es SIEMPRE verdadero incluso cuando es una publicación nueva. Por lo tanto, este parámetro es inútil. No estoy seguro de si alguna vez funcionó, pero definitivamente no está funcionando como está documentado en la última versión de WordPress 4.8.

@SolomonClosson Si miras wp_publish_post
, entonces sí. Pero eso no es cierto para su uso en wp_insert_post
. He escrito una función de depuración, la agrego a la respuesta.

@SolomonClosson Si tienes un problema concreto real, por favor haz una nueva pregunta. Echa un vistazo a las revisiones de la función de depuración para obtener una explicación.

El hook save_post
tiene un tercer parámetro que siempre está establecido como TRUE, así que no estoy seguro de qué tiene que ver con otros hooks, no estoy hablando de otros hooks. Me refiero al hook en tu respuesta. Esto es incorrecto.

@SolomonClosson Como dije, el hook ocurre dos veces:
wp_insert_post()
, wp_publish_post()
. Este último es solo para posts programados, ahí $update
siempre se establece como true
. Por otro lado, en relación a wp_insert_post()
, $update
no siempre es true
.

Llego tarde a la fiesta, pero bueno, ¿por qué no? @Nicolai, lo que dices es lo que uno esperaría que ocurriera, pero no logro que $update sea false incluso cuando uso el hook 'wp_insert_post'. No importa dónde lo enganche, siempre es true. No entiendo por qué esto no está corregido.

La forma en que realizo esta verificación (dentro de una función enganchada) es comparar la fecha de publicación y la fecha de modificación (en GMT para estandarización)
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 ){
// Nueva publicación
}else{
// Publicación actualizada
}
}
add_action('save_post', 'check_new_vs_update' );
Esto funciona porque incluso al crear la publicación, esta tiene una fecha de 'modificación' adjunta, que es exactamente igual a la fecha de 'creación', pero permitimos una variación de 1 segundo en caso de que pase un segundo durante la creación de la publicación.

A veces el post_date_gmt
es 2019-03-12 01:31:30
y el post_modified_gmt
es 2019-03-12 01:31:31
. :(

@HeYifei何一非 buen punto, si el procesamiento comienza al final de un segundo dado, esto podría pasar. He actualizado mi respuesta, gracias

Chicos, solo una info. El hook se dispara al restaurar y eliminar una entrada.

No puedo creer que tenga que ser tan complicado. WordPress nunca deja de sorprenderme.

Para ser justos con ellos, tienen literalmente millones de usuarios y es un proyecto de código abierto. Desafortunadamente no pueden incluir cada característica y esta probablemente no es una de las más utilizadas. En años recientes cosas como mejorar el editor y la seguridad han sido priorizadas ¡y eso es bueno! ¡Quiero decir, incluso jQuery está siendo actualizado recién ahora! ¡Hasta la versión actual estaba corriendo 1.x!

Terminé simplemente verificando la existencia de un valor personalizado antes de establecerlo. De esa manera, si es una publicación recién creada, el valor personalizado aún no existiría.
function attributes_save_postdata($post_id) {
// Verifica si es un guardado automático y salir si es así
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
// Verifica el nonce de seguridad
if (!wp_verify_nonce($_POST['_attributes_noncename'], plugin_basename(__FILE__))) return;
// Verifica permisos para páginas o posts
if ('page' == $_POST['post_type']) {
if (!current_user_can('edit_page', $post_id)) return;
} else {
if (!current_user_can('edit_post', $post_id)) return;
}
// Obtiene el meta valor existente
$termid = get_post_meta($post_id, '_termid', true);
if ($termid != '') {
// Es un nuevo registro
$termid = 'update';
} else {
// Es un registro existente
}
// Actualiza el meta valor
update_post_meta($post_id, '_termid', $termid);
}
// Añade la acción para guardar los datos
add_action('save_post', 'attributes_save_postdata');

¿Para que esto funcione, primero debes crear el campo personalizado usando add_post_meta?

Según el Codex: [update_post_meta] puede usarse en lugar de la función add_post_meta(). http://codex.wordpress.org/Function_Reference/update_post_meta

Esto podría fallar si los posts fueron creados antes de que el código se active mediante la activación de un plugin. Los posts más antiguos no tienen el meta configurado, por lo que la primera actualización para ellos se considerará como nueva.

Ejemplo de respuesta a ialocin con el parámetro "update":
function save_func($ID, $post,$update) {
if($update == false) {
// hacer algo si es la primera publicación
} else {
// Hacer algo si es una actualización
}
}
add_action( 'save_post', 'save_func', 10, 3 );

Una mejor manera de estructurar esto sería colocando primero el bloque de actualización, permitiendo simplemente hacer if($update)
o mantener primero el bloque nuevo pero usando if( ! $update )
. Este último llevará al OP a mejores prácticas y es preferido sobre tu método según los estándares de codificación de WordPress en casos como el operador ternario

Acabo de encontrarme con el save_post
sobre nuevas publicaciones y actualizaciones. Después de leer el código fuente para entender el flujo, encontré que el siguiente método podría ser útil. Como aún no se ha mencionado antes, vean si les sirve a alguien. (La prueba es con Core 5.3.3)
El flujo de creación de una publicación es aproximadamente:
- Después de presionar "Añadir nueva" (Entrada)
- Se llamará a
$post = get_default_post_to_edit( $post_type, true );
donde get_default_post_to_edit()
recibe el argumento$create_in_db = true
- Así que
wp_insert_post()
se llama inmediatamente, se crea un borrador automático (auto-draft
), incluso si no se guarda. Cada vez que se hace clic en "Añadir nueva", se crea unauto-draft
$update
siempre es verdadero para Publicar. Entonces, al publicar una nueva entrada, es verdadero.
Al comparar el objeto $_POST para una nueva entrada y una entrada actualizada o republicada, la diferencia prominente es el valor de _wp_http_referer
, donde la nueva entrada es /wp-admin/post-new.php
Suposición: se asume que la entrada se publica/añade desde la interfaz de usuario. Si se hace por otro mecanismo o código personalizado, se necesitará ajustar la verificación.
add_action( 'save_post', 'test_save_post_check', 0, 3 );
function test_save_post_check( $post_ID, $post, $update ) {
// otras pruebas + esta
// verificando la posición 'post-new' en '_wp_http_referer'
if( strpos( wp_get_raw_referer(), 'post-new' ) > 0 ) {
// nueva
} else {
// actualización
}
}

aquí hay un código funcional que probé y usé en mi sitio web, que resuelve los siguientes dos problemas asociados con la acción save_post:
problema para verificar entre actualización o inserción
problema de inserción doble por la acción save_post
function save_annonces_callback($post_ID, $post, $update){ $post_type = get_post_type($post_ID); if ( $post_type === 'annonces' ){ //esto previene la inserción doble por la acción save_post :) if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) { return; } else { //verificar si es un nuevo post para insertar if( strpos( wp_get_raw_referer(), 'post-new' ) > 0 ){ //realizar inserción }else{ //realizar actualización } } } }add_action('save_post','save_annonces_callback', 10, 3);

El hook save_post
se activa tanto cuando se crea un post como cuando se actualiza (después de que WordPress lo haya guardado en la base de datos). pre_post_update
se activa cuando un post se actualiza, pero antes de que el post sea actualizado - esto puede ser importante.

Como sugirió Darshan Thanki (y Stephen Harris amplió), puedes aprovecharte del hook pre_post_update
.
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;
}
La razón por la que usé variables globales es porque function is_new_post() use ( &$new_post )
no es válido en PHP (sorprendente...) así que incorporar esa variable al ámbito de la función no funciona -- de ahí lo global.
Ten en cuenta que esto solo puede usarse de manera confiable dentro o después del evento save_post
(lo cual suele ser suficiente, al menos para lo que estamos haciendo con ello).

Cuando se dispara save_post, toda la información sobre esa entrada ya está disponible, así que en teoría podrías usar:
function f4553265_check_post() {
if (!get_posts($post_id)) {
// si es una nueva entrada, get_posts($post_id) debería retornar null
} else {
// $post_id ya existe en la base de datos
}
}
add_action('save_post','f4553265_check_post');
Esto no está probado, sin embargo. =)

Para cuando llegas a save_post
, el post en sí ya se habría guardado en la base de datos - así que get_posts
devolvería el post actual.

Otro enfoque que utiliza una función incorporada y no requiere agregar nada a la base de datos sería usar get_post_status()
.
$post_status = get_post_status();
if ( $post_status != 'draft' ) {
//borrador
} else {
//no es un borrador: puede ser publicado, pendiente, etc.
}
Sin embargo, ten en cuenta que puede no ser apropiado si planeas establecer el estado nuevamente como "borrador" más tarde – tus instrucciones se repetirán la próxima vez que actualices la publicación.
Dependiendo del contexto, podrías considerar las distintas cadenas que puede devolver get_post_status()
para construir un escenario más adecuado.
Consulta el Codex para get_post_status() y Estado de la Publicación
Los valores posibles son:
- 'publish' - Una publicación o página publicada
- 'pending' - la publicación está pendiente de revisión
- 'draft' - una publicación en estado de borrador
- 'auto-draft' - una publicación recién creada, sin contenido
- 'future' - una publicación programada para publicarse en el futuro
- 'private' - no visible para usuarios no registrados
- 'inherit' - una revisión. ver get_children.
- 'trash' - la publicación está en la papelera. añadido en la Versión 2.9.

Dado que el parámetro $update
es inútil, esta es la forma más rápida que probé:
function wpse48678_check_is_post_new($post_id, $post, $update)
{
if (false !== strpos($_POST['_wp_http_referer'], 'post-new.php')) {
return true; // O hacer algo más.
} else {
return false; // O hacer algo más.
}
}
add_action('save_post_{$post_type}', 'wpse48678_check_is_post_new', 10, 3);

He pasado horas en esto
Actualmente estoy en proceso de desarrollar un plugin y quería enviar una notificación a todos los autores cuando una página era "Creada" en el sitio de mi Compañía de Diseño Web.
Originalmente intenté usar lo siguiente:
add_action('save_post_page', function($post_id, $post, $update) {
if(!$update){
echo("¡Es una publicación nueva!");
}else{
echo("¡Es una publicación antigua!");
}
}
Seguía mostrando "¡Es una publicación antigua!". ¿Pero por qué??? Bueno, después de investigar un poco parece que después de hacer clic en el botón "Añadir nueva página" WordPress crea instantáneamente un auto-guardado que establece la variable $update como verdadera. Qué útil.
Después de horas de investigación finalmente encontré lo siguiente que funciona:
// Se activa cuando una publicación cambia de "estado", es decir, de pendiente a borrador, de publicado a programado.
add_action('transition_post_status', function($new_status, $old_status, $post) {
// Comprueba si la publicación pasa de auto-guardado a cualquier otro estado (borrador, pendiente, publicado, etc.)
if 'auto-draft' === $old_status && $new_status !== 'auto-draft'){
echo("¡Es una publicación nueva!");
}else{
echo("¡Es una publicación antigua!");
}
}
Nota: En WordPress, "borrador" y "auto-guardado" son diferentes. Un estado de "borrador" es una publicación que un usuario establece manualmente como borrador, lo que indica que está en progreso. Un "auto-guardado", por otro lado, se asigna automáticamente a una publicación cuando se crea pero aún no se ha publicado. Básicamente es otra forma de decir auto-guardado.
Espero que esto ayude a algunas personas... Ahora a pasar otras 4 horas con otro problema jaja.

Esto debería funcionar chicos
add_action( 'wp_after_insert_post', 'clearCDNCache', 10, 4);
public function clearCDNCache($post_id, $post, $update, $post_before){
if ( ! empty($_POST['_wpnonce']) ) {
// Ejecutar función personalizada solo una vez
}
// Añadir nuevo modo.
if (!empty($_POST['_wp_http_referer']) && $_POST['_wp_http_referer'] == '/wp-admin/post-new.php') {
} else {
// Modo actualización
}
}
