Cómo Pasar Variables Externas a Filtros/Acciones

11 abr 2012, 22:37:25
Vistas: 25.3K
Votos: 12

Me encuentro necesitando pasar datos personalizados a un filtro proporcionado por un plugin de terceros. Todas las formas que he visto para hacer esto son realmente complicadas y difíciles de entender.

Toma este ejemplo:

$score = 42; //Algún cálculo loco que no quiero repetir.

function add_score_to_title($title) {
    return 'Resultados del Cuestionario (' . $score . '/') - ' . $title;
}

add_filter( 'aioseop_title_single', 'add_score_to_title');

¿Cómo puedo pasar la variable $score a add_score_to_title()?

Lo que terminé haciendo fue agregar mi variable al objeto global $wp. Así que terminas con esto:

global $wp;
$score = 42; //Algún cálculo loco que no quiero repetir.
$wp->some_random_name_for_score = $score;

function add_score_to_title($title) {
    global $wp;
    $score = $wp->some_random_name_for_score;
    return 'Resultados del Cuestionario (' . $score . '/') - ' . $title;
}

add_filter( 'aioseop_title_single', 'add_score_to_title');

¿Sucio? Tal vez. ¿Simple? ¡Sí! ¿Hay desventajas en esta técnica? Por favor, discutamos.

ACTUALIZACIÓN Aquí está el código completo en cuestión -> http://pastebin.com/fkSXY04m

5
Comentarios

Simplemente haz tu "cálculo loco" dentro de esa función...

onetrickpony onetrickpony
11 abr 2012 22:45:42

Probablemente sea difícil de entender con mi ejemplo simplificado. La variable $score se utiliza en toda esa plantilla. Quiero calcularla una vez y pasar ese valor a la función que es un callback para un filtro que cambia el título de la página. Este filtro se usa en el All In One SEO Pack. Ejecutar el cálculo múltiples veces no es deseable.

kingkool68 kingkool68
11 abr 2012 22:54:33

Entonces deberías publicar más código si quieres obtener una solución que no implique introducir estado global (que nunca debería ser necesario).

onetrickpony onetrickpony
11 abr 2012 23:00:03

Aquí está el código completo en un archivo de plantilla. $score se calcula iterando sobre un array de elementos e incrementando una variable.

http://pastebin.com/fkSXY04m

kingkool68 kingkool68
11 abr 2012 23:07:43

posible duplicado de Pasar un parámetro a funciones de filtro y acción

T.Todua T.Todua
14 may 2015 12:20:32
Todas las respuestas a la pregunta 3
14
12

Tienes al menos dos opciones:

  1. Globalizar la variable deseada y luego hacer referencia a ella dentro del callback
  2. Encapsular la lógica de cálculo de puntuación en una función, luego hacer referencia a ella dentro del callback

Globalizar la Variable

<?php
global $score;
$score = 42; // Algún cálculo loco que no quiero repetir.

function add_score_to_title($title) {
    global $score;
    return 'Resultados del Quiz (' . $score . '/') - ' . $title;
}

add_filter( 'aioseop_title_single', 'add_score_to_title');
?>

Encapsular el Cálculo de Puntuación

Si solo necesitas el cálculo de puntuación dentro del filtro, lleva la lógica al callback mismo:

<?php
function add_score_to_title($title) {
    $score = 0;
    $questions = get_quiz_result_questions();
    $total_questions = 0;
    foreach( $questions as $question ) {
        $order = $question->order;

        if( $order >= 100 ) {
            break;
    }

    if( $question->correct == $_POST['Q'][$order] ) {
        $score++;
    }
    $total_questions++;

    return 'Resultados del Quiz (' . $score . '/') - ' . $title;
}

add_filter( 'aioseop_title_single', 'add_score_to_title');
?>

Mejor aún, podrías encapsular tu cálculo de puntuación en una función propia, y luego llamar a esa función dentro de tu callback:

<?php
function wpse48677_get_score() {
    $score = 0;
    $questions = get_quiz_result_questions();
    $total_questions = 0;
    foreach( $questions as $question ) {
    $order = $question->order;

    if( $order >= 100 ) {
        break;
    }

    if( $question->correct == $_POST['Q'][$order] ) {
        $score++;
    }
    $total_questions++;
    $output['score'] = $score;
    $output['total_questions'] = $total_questions;

    return $output;
}

function add_score_to_title($title) {

    $score_results = wpse48677_get_score();

    $score = $score_results['score'];

    return 'Resultados del Quiz (' . $score . '/') - ' . $title;
}

add_filter( 'aioseop_title_single', 'add_score_to_title');
?>

Si tienes problemas al hacer referencia al objeto $_POST, también puedes registrar tu variable de consulta y luego usar get_query_var() internamente para obtener los datos:

function add_score_query_vars( $query_vars ) {
    $query_vars[] = 'Q';

    return $query_vars;
}
add_filter( 'query_vars', 'add_score_query_vars' );

Con esto implementado, $_POST['Q'] puede reemplazarse con get_query_var('Q').

11 abr 2012 22:44:34
Comentarios

Esto no tiene nada que ver con el número de argumentos pasados a la función por apply_filters...

onetrickpony onetrickpony
11 abr 2012 22:46:40

Probé el método #1 que mencionas de globalizar la variable. No funciona. El parámetro accepted_args tampoco me ayuda ya que no tengo control sobre qué variables se pasan a la función de callback.

kingkool68 kingkool68
11 abr 2012 22:55:58

Perdón, tenías razón. Calculé $score primero y luego lo globalicé. No es de extrañar que no funcionara. ¡Gracias!

kingkool68 kingkool68
11 abr 2012 23:11:35

-1. La primera opción expone una variable en el estado global, la segunda opción no funciona...

onetrickpony onetrickpony
11 abr 2012 23:28:23

Actualizado para reflejar que estaba equivocado sobre la segunda opción. Pero ¿qué tiene de malo exponer una variable global? Ciertamente es mejor que usar una función anónima en un callback. :)

Chip Bennett Chip Bennett
11 abr 2012 23:29:57

No, no lo es. Y aquí hay algunas razones por las que deberías evitar esta práctica. Tal vez esté bien para su caso específico, pero ¿y si alguien realmente construye un plugin público usando tu solución?

onetrickpony onetrickpony
11 abr 2012 23:34:10

"¿Las variables globales son malvadas?"? ¿En serio? Entonces mejor que todo el código de WordPress esté reescrito, ya que depende de bastantes variables globales.

Chip Bennett Chip Bennett
11 abr 2012 23:37:14

Exactamente. Esta es una de las razones por las que el código base de WP es uno de los peores en la web. De todos modos, WP tiene una excusa - la compatibilidad requerida con plugins diseñados para versiones antiguas. Tú no :)

onetrickpony onetrickpony
11 abr 2012 23:38:44

Sí... voy a dejar ese tema aparte. Gracias por señalar el problema con la segunda opción; la he eliminado de la pregunta. Mantengo que es aceptable usar variables globales. Si supiera cómo se genera o devuelve la salida del Plugin, probablemente podría ofrecer una mejor solución.

Chip Bennett Chip Bennett
11 abr 2012 23:44:32

Consulta la respuesta actualizada; después de revisar el Pastebin, he ofrecido dos métodos que preferiría en lugar de usar una variable global.

Chip Bennett Chip Bennett
11 abr 2012 23:52:06

Mejor, pero él quiere que el "score" sea accesible más adelante en el script, sin volver a calcularlo, por lo que el uso de una variable "static" tiene sentido aquí.

onetrickpony onetrickpony
11 abr 2012 23:57:55

Usando una función para calcular el score: ¿no se podría almacenar el cálculo en la caché de objetos?

Stephen Harris Stephen Harris
12 abr 2012 00:48:47

a finales de 2015 y WP todavía depende de variables globales malignas. La excusa ya está muy pasada. Y +1 por la sugerencia de globalizar. En mi caso, el callback necesitaba acceder al $user que tuve que globalizar ya que mi llamada apply_filters se iniciaba desde dentro de una función.

Ejaz Ejaz
7 oct 2015 20:56:50

Estos Globales son malignos, la gente necesita relajarse. Me gusta bastante esta respuesta. El OP preguntó cómo se podían pasar variables entre funciones. Esta respuesta explica varias formas de hacerlo. Comienza con globales, que es mejor evitar, pero aún así son algo que existe y resuelve el problema en cuestión. Luego, la respuesta procede a explicar varias opciones mejores que podrían funcionar en algunas circunstancias.

squarecandy squarecandy
21 sept 2017 18:25:43
Mostrar los 9 comentarios restantes
3
function add_score_to_title($title = false) {
  static $score = false;

  if($score === false){
    // hacer cálculo
  }

  // llamada del plugin (filtro)   
  if($title !== false)
    return 'Resultados del Quiz (' . $score . ') - ' . $title;

  // tu llamada
  return $score;
}

Llama a la función en cualquier parte de tu script para obtener el puntaje, solo se calculará una vez.

Otra forma, usando funciones anónimas:

// hacer el cálculo
$score = 'xxx';

add_filter('aioseop_title_single', function($title) use($score){
  return 'Resultados del Quiz (' . $score . ') - ' . $title;  
});
11 abr 2012 23:02:10
Comentarios

Las funciones anónimas no deben usarse en llamadas a add_filter() o add_action(). No se pueden eliminar mediante remove_function().

Chip Bennett Chip Bennett
11 abr 2012 23:31:07

Te refieres a remove_filter, que se usa principalmente para eliminar filtros integrados, no filtros agregados por plugins/temas...

onetrickpony onetrickpony
11 abr 2012 23:36:03

Sí; perdón: remove_filter() y remove_action(). Y no hay razón por la que los filtros de Plugin/Tema no deban poder eliminarse razonablemente: anular colas de hojas de estilo de Plugin, Temas hijos, etc.

Chip Bennett Chip Bennett
11 abr 2012 23:38:23
6

El siguiente ejemplo muestra la variable $my_calculation en el ámbito global, sin embargo, desde nuestra función local necesitamos declarar global $my_calculation para poder acceder a la variable en el ámbito global.

<?php 

    $my_calculation = 'resultado!';

    function my_function() {

        global $my_calculation;
        return $my_calculation;

    }

    add_filter( 'function_something_here', 'my_function');   

?>

Esta es solo una forma de hacerlo y parece ser ordenada. ¿Funcionaría para ti?

11 abr 2012 22:57:06
Comentarios

¿En qué se diferencia esto de lo que ya tiene?

onetrickpony onetrickpony
11 abr 2012 23:00:22

Una declaración menos de "global". Mira su segundo ejemplo, ¡declara global $wp dos veces!

Adam Adam
11 abr 2012 23:02:47

Tampoco funciona. Esto es lo primero que intenté.

kingkool68 kingkool68
11 abr 2012 23:02:54

¿Puedes usar print o echo para mostrar tu resultado y asegurarte de que tu función está funcionando correctamente antes de pasarla al filtro?

Adam Adam
11 abr 2012 23:04:27

¡Ups! Globalizar $score sí funciona. Me equivoqué al asignar primero un valor a $score y luego globalizarlo, lo cual claramente no funciona. Hacerlo correctamente, globalizando primero $score y luego asignándole un valor, funciona como se esperaba. Gracias a todos.

kingkool68 kingkool68
11 abr 2012 23:12:57

Eso era lo que iba a decir después de mirar tu script, estaba un poco al revés ;) ¡Que te diviertas!

Adam Adam
11 abr 2012 23:14:01
Mostrar los 1 comentarios restantes