Pasando argumentos a un callback de página de menú en el admin de WordPress
Situación: Estoy trabajando en un plugin y lo estoy desarrollando como una clase. Todo funcionaba bien hasta que me encontré con esta situación. Quería hacer las cosas un poco más limpias e intenté esto...
class MyPlugin {
function __construct() {
add_action('admin_menu', array(&$this, 'myplugin_create_menus');
}
// No quiero escribir una función para cada página de opciones que cree
// prefiero cargar el contenido desde un archivo externo.
function load_view($filename) {
$view = require(dirname(__FILE__).'/views/'.$filename.'.php');
return $view;
}
// Aquí es donde surge el problema
function myplugin_create_menus() {
add_menu_page( 'Nombre del plugin',
'Nombre del plugin',
'manage_options',
'my-plugin-settings',
array(&$this, 'load_view') // ¿Dónde especifico el valor de $filename??
);
}
}#fin de la clase
He probado varias opciones diferentes pero nada funciona, quizás lo tengo delante pero no lo veo.
Por supuesto esto es una recreación, he añadido prefijos a todas mis funciones y no son exactamente como las escribí aquí, pero espero que entiendan la idea de lo que pregunto.
Gracias de antemano.
P.D.: Si quieren ver el código fuente original estaré encantado de pegarlo y darles el enlace.
No puedes pasar un argumento a la función de callback. add_menu_page()
lo añade como un manejador de acción, y admin.php
dispara la acción, sin ningún argumento.
Veo dos soluciones simples para este problema. Una es almacenar todos los nombres de archivo en un array en tu clase, indexados por el nombre del hook. Luego puedes usar esto para buscar qué archivo necesitas cargar (también puedes almacenar datos adicionales en este array).
class WPSE16415_Plugin
{
protected $views = array();
function load_view() {
// current_filter() también devuelve la acción actual
$current_views = $this->views[current_filter()];
include(dirname(__FILE__).'/views/'.$current_views.'.php');
}
function myplugin_create_menus() {
$view_hook_name = add_menu_page( 'Nombre del Plugin',
'Nombre del Plugin',
'manage_options',
'my-plugin-settings',
array(&$this, 'load_view'),
);
$this->views[$view_hook_name] = 'options';
}
}
La otra es omitir el argumento del callback, para que WordPress incluya el archivo indicado por el nombre del slug mismo, como Brady sugiere en su respuesta.

Esto no funciona para mí. Estoy usando WordPress 4.1 (y al día de hoy 4.1.1)

¡Ingenioso! Y sí funciona. Aquí hay un ejemplo más completo: http://hastebin.com/segibugice que generaría una URL como http://example.com/wp-admin/admin.php?page=my-slug

Debería haber mencionado que cuando pasas variables al alcance de una función anónima, debes usar la palabra clave "use".
function() use ($my_var) { // ahora puedes usar $my_var }

Puedo confirmar que esto funciona perfectamente en WordPress v5.5

Resolví este problema simplemente añadiendo el ID (o cualquier dato que necesites) al slug del menú.
Por ejemplo:
add_menu_page( 'Nombre del plugin',
'Nombre del plugin',
'manage_options',
'mi-plugin-config-' . $identificador,
'funcion-callback-config'
);
Esto creará una URL con 'mi-plugin-config-archivo' (como ejemplo), y puedo analizar esa parte de la URL (con $_GET o filter_input).

También podrías usar un parámetro de URL, pero tienes que crear un ítem de menú (y luego puedes ocultarlo si lo deseas).

Gracias por poner mi código en un bloque de código toscho. Cuando hago una pregunta hay un botón para eso, pero no estoy familiarizado con el marcado para hacerlo en una respuesta.

¿La función load_view debería ser así?:
function load_view($filename) {
include(dirname(__FILE__).'/views/'.$filename.'.php');
}
y en tu archivo include debería imprimir cualquier contenido para la página que se está mostrando.
EDITADO:
Esto es lo que dice el Codex al respecto:
$menu_slug
(string) (requerido)
El nombre del slug para referirse a este menú (debe ser único para este menú). Antes de la Versión 3.0 esto se llamaba parámetro file (o handle). Si se omite el parámetro function, menu_slug debe ser el archivo PHP que maneja la visualización del contenido de la página del menú.
Por defecto: Ninguno
$function
La función que muestra el contenido de la página para la página del menú. Técnicamente, el parámetro function es opcional, pero si no se proporciona, WordPress asumirá básicamente que incluir el archivo PHP generará la pantalla de administración, sin llamar a una función. La mayoría de los autores de plugins eligen poner el código generador de página en una función dentro de su archivo principal del plugin.:En caso de que se especifique el parámetro function, es posible usar cualquier cadena para el parámetro file. Esto permite el uso de páginas como ?page=my_super_plugin_page en lugar de ?page=my-super-plugin/admin-options.php.
Entonces, lo que puedo deducir de esto es que si dejas function vacío intenta incluir un archivo php basado en lo que hayas establecido en menu_slug
.
EDITADO 2
function load_view() {
include(dirname(__FILE__).'/views/'.$this->filename.'.php');
}
function myplugin_create_menus() {
$this->filename = "something";
add_menu_page( 'Nombre del plugin',
'Nombre del plugin',
'manage_options',
'my-plugin-settings',
array(&$this, 'load_view')
);
$this->filename = "somethingelse";
add_menu_page( 'Nombre del plugin',
'Nombre del plugin',
'manage_options',
'my-plugin-settings',
array(&$this, 'load_view')
);
}

@Brady Lo sé, y la función "load_view" está funcionando bien y mostrando el contenido correctamente cuando se usa un valor estático. Por ejemplo: incl..../views/my-panel.php');

@Brady array(&$this, 'load_view') // ¿Dónde especifico el valor de $filename??. No puedo hacer algo como array(&$this, 'load_view("my-value")') Quiero encontrar una manera de pasar parámetros a la función que estoy llamando

Ah, ya entiendo. Quieres pasar una función de clase pero con un parámetro. Bueno, he buscado y buscado y no encuentro cómo hacerlo. Pero ya que estás pasando la clase, ¿no podrías hacer lo que he puesto en EDIT 2?

@Brady: Tu segunda edición no ayudará mucho, solo sobrescribes la variable filename
por lo que siempre será "somethingelse"
. Tu primera edición podría ser la clave aquí: si load_view
no hace nada excepto incluir el archivo, realmente no deberías pasar una función callback y WordPress intentará cargar la página que pasaste como slug.

Basado en la respuesta de user35752, incluso puedes usar un método de objeto con parámetros como callback.
$args = [ [new Foo(), 'bar'], [$param1, $param2, ...] ];
$callback = function () use ($args){
call_user_func_array($args[0], $args[1]);
};
add_menu_page( $page, $menu, $capability, $slug, $callback , $icon, $position)
