¿Cómo usar do_action y obtener un valor de retorno?

21 abr 2016, 19:47:49
Vistas: 21.2K
Votos: 12

Tenemos el siguiente escenario.

Agrego una acción para limpiar logs de la base de datos:

add_action( 'myplugin_clean_logs', array( 'MyPlugin_Logs', 'clean_logs' ) );

Ahora quiero ejecutar esta acción periódicamente:

wp_schedule_event( current_time( 'timestamp' ), 'daily', 'myplugin_clean_logs' );

y ejecutarla manualmente:

do_action( 'myplugin_clean_logs' );

El método MyPlugin_Logs::clean_logs devuelve el conteo de filas afectadas o false si algo salió mal.

Ahora quiero mostrar el número de filas que han sido eliminadas. Me imagino algo como esto:

$affected_rows = do_action( 'myplugin_clean_logs' );
echo $affected_rows . ' entradas han sido eliminadas.';

Pero como do_action no devolverá ningún valor, no tengo idea de cómo obtener el valor de retorno.

¿Debería ejecutar el método directamente en una ejecución manual, pero usar la acción en eventos programados?

1
Comentarios

No quieres estar haciendo echo de nada en un evento programado, así que sí, ejecutaría el método directamente en una ejecución manual (asumo que el administrador lo activaría y quieres mostrarles la salida).

Tim Malone Tim Malone
22 abr 2016 01:55:28
Todas las respuestas a la pregunta 3
10
16

Lo interesante es que un filtro es lo mismo que una acción, solo que devuelve un valor, así que simplemente configúralo como un filtro:

add_filter( 'myplugin_clean_logs', array( 'MyPlugin_Logs', 'clean_logs' ) );

Entonces algo como:

$affected_rows = '';
$affected_rows = apply_filters( 'myplugin_clean_logs', $affected_rows );

debería pasar $affected_rows a clean_logs() (y cualquier otra función que hayas enganchado a myplugin_clean_logs) y asignar el valor de retorno de nuevo a $affected_rows.

21 abr 2016 21:18:22
Comentarios

votado negativo porque esto es hackear código en lugar de desarrollar software. Si las acciones fueran solo un subconjunto de filtros no habría necesidad de ellas. Cron no puede pasar valores, por lo tanto no debería engancharse como un filtro incluso si el código problemático del núcleo te permite hacer tales trucos :)

Mark Kaplun Mark Kaplun
22 abr 2016 00:27:11

Entiendo el punto. Comprendo la intención de las dos cosas diferentes, pero al mirar el código del núcleo aquí, todo el asunto de do_action() no es más que un hack elaborado de apply_filters() :)

Caspar Caspar
22 abr 2016 01:00:13

no es el único mal diseño en el núcleo, lo cual en parte es lo que lleva a la confusión que genera preguntas como esta

Mark Kaplun Mark Kaplun
22 abr 2016 01:05:58

Tenemos que trabajar con lo que tenemos, así que aunque entiendo el punto de vista de Mark, sigo pensando que esta es una respuesta legítima - a menos que, por supuesto, el núcleo cambie este enfoque en el futuro, pero creo que eso es poco probable debido a los enormes problemas de compatibilidad hacia atrás que introduciría.

Tim Malone Tim Malone
22 abr 2016 01:57:48

Gracias, @TimMalone. Realmente aprecio la objeción de @mark-kaplun. Mi respuesta describe cómo sortear que do_action() no devuelva un valor en lugar de cómo diseñar una solución acorde con la intención de do_action(). Si alguien es capaz de hacer lo que él pide, esa respuesta merecería ser la aceptada. Mi primer pensamiento sería que el método enganchado (asumiendo que el OP está usando un diseño OOP para este plugin) guarde su resultado en una propiedad protegida de la clase del plugin y luego escribir un rápido getter para extraerlo en algún punto posterior. ¡Pero eso es solo una idea al azar!

Caspar Caspar
22 abr 2016 05:05:51

@Casper la documentación de WordPress distingue explícitamente entre acciones y filtros. Según lo que entendí, un filtro está ahí para filtrar cosas realmente y como tal es más una herramienta que un ejecutor. Tal vez esto es solo teoría y más una diferencia de diseño de código que un caso del mundo real. Al principio tuve una idea similar de pasar una variable por referencia (aunque no lo probé, si funciona), pero lo evité para no estropear el código.

Aley Aley
22 abr 2016 10:22:12

@TimMalone, a veces cuando tienes un problema de codificación confuso, deberías detenerte y preguntarte si es un problema de codificación o un problema de diseño

Mark Kaplun Mark Kaplun
22 abr 2016 10:47:16

@Aley, lo siento, en realidad escribí un comentario a la pregunta pero aparentemente olvidé enviarlo, pero puedes captar la idea de lo que pienso (necesitas manejarlo diferente en ambos casos) de los comentarios aquí

Mark Kaplun Mark Kaplun
22 abr 2016 10:48:28

¿Por qué no simplemente usar una llamada a función PHP en su lugar? De esa manera no necesitas do_action o apply_filters. Coloca la función dentro del archivo functions.php.

Solomon Closson Solomon Closson
18 oct 2016 21:39:25

Para lo que valga, así es como WooCommerce lo hace en sus plantillas. Teorías o mejores prácticas aparte, esta es esencialmente una forma aceptada de hacer las cosas ya que WooCommerce es ahora oficialmente parte de WordPress/Automattic.

Michael Thompson Michael Thompson
16 nov 2016 22:16:59
Mostrar los 5 comentarios restantes
0

Esta es una pregunta muy antigua pero para responder a la pregunta original "¿Cómo usar do_action y obtener un valor de retorno?" para cualquiera que esté buscando, puedes hacerlo usando el buffer de salida.

ob_start();
    do_action( 'myplugin_clean_logs' );
$action_data = ob_get_clean();

De esta manera puedes almacenar el contenido de do_action en una variable.

20 jul 2020 00:41:57
2

Nunca he usado esta función y no la he probado, pero ¿podría funcionar? do_action_ref_array().

function myplugin_clean_logs_fn() {
    $args = array(
        'param1'        => 'val1',
        'param2'        => 'val2',
        'affected_rows' => 0,
    );
    do_action_ref_array( 'myplugin_clean_logs', &$args );
    return $args['affected_rows'];
}

// LLAMAR LA FUNCIÓN
$affected_rows = my_plugin_clean_logs();
echo $affected_rows .' registr'. ($args['affected_rows']*1===1?'o':'os') .' eliminados.';

// PROGRAMARLA
add_action('myplugin_clean_logs_call_fn', 'myplugin_clean_logs_fn');
wp_schedule_event( current_time( 'timestamp' ), 'daily', 'myplugin_clean_logs_call_fn' );

// UN FILTRO DE EJEMPLO
add_action('myplugin_clean_logs', function($args) {
    // Proceso de limpieza
    // Por cada registro afectado, incrementar $args['affected_rows'] según corresponda
}, 10, 3);

Si eso no funciona, ¿por qué no simplemente filtrar como sugirió Caspar? Al fin y al cabo, ese es el propósito de un filtro, y en este caso el número de registros afectados es lo que se está filtrando. (Extraño el viejo MortCore. ¿Alguien recuerda cómo manejaba los valores de retorno, paso por referencia y argumentos con solo una función de tres parámetros?).

8 ago 2019 22:54:27
Comentarios

Esta es una respuesta horrible, ya que pasar y modificar valores por referencia es una muy mala práctica. Honestamente, esta respuesta realmente no aporta valor en el contexto de la pregunta y probablemente debería eliminarse o cambiarse a un comentario. Además, usar funciones anónimas con hooks también es mala práctica, ya que las hace imposibles de desenganchar.

Hybrid Web Dev Hybrid Web Dev
30 oct 2019 23:12:07

Estoy de acuerdo por las mismas razones mencionadas anteriormente, que este no es un camino recomendado. Si por alguna razón necesitas obtener un valor de retorno de una acción, y necesitas algo rápido y sucio, preferiría la solución de Caspars. Si estás desarrollando algo con un ciclo de vida por delante, buscaría una forma más robusta. Ahora que lo pienso, ¿qué tal los admin notices? https://developer.wordpress.org/reference/hooks/admin_notices/

jgangso jgangso
21 ene 2020 11:55:50