Usar Backbone dentro del área de administración de WordPress: El back end
Spanish (Español) translation by Eva Collados Pascual (you can also view the original English article)
¡Los rumores son ciertos! ¡El panel de administración de WordPress está usando ahora Underscore y Backbone! Esto significa que con el mínimo esfuerzo, podemos comenzar a utilizar estas fantásticas librerías JavaScript en nuestros propios plugins. Este tutorial te mostrará exactamente cómo puedes hacer esto. Crearemos la parte de administrador de un plugin de "quizzes" o cuestionarios. Usaremos un simple tipo de entrada personalizada para guardar preguntas, y luego dentro de cada pregunta añadiremos un "meta box" o cuadro meta que nos permitirá introducir hasta 4 respuestas y seleccionar cuál es la correcta. Vamos a ver paso a paso cómo utilizar plantillas, cómo enganchar en eventos de clic y pulsaciónes de la tecla de flecha hacia arriba, cómo guardar los datos de nuevo en la base de datos de WordPress y lo más importante, cómo 'sacar tu verdad del Dom', como le gusta decir el creador Jeremy Ashkenas.
Voy a mencionar por adelantado, que el plugin que estamos construyendo en este tutorial puede parecer demasiado detallado para lo que logra. Sin embargo, te dará una excelente visión general sobre la utilidad de Backbone y si te encuentras con un proyecto en el futuro que requiera una interfaz de usuario compleja con una gran cantidad de JavaScript, estarás perfectamente preparado y listo para gestionar la muy necesaria organización de la fiesta.
Lo que vamos a hacer
En esta primera parte, configuraremos el back-end de nuestro plugin. Esto implicará la configuración de los archivos y carpetas, así como la implementación de todas las características que nuestro plugin requiere en PHP. Tendremos que hacer lo siguiente:
- Registrar un tipo de entrada personalizada para nuestras preguntas
- Añadir una caja meta que nos permitirá introducir respuestas en la misma página
- Guardar información de las cajas meta cuando se guarde la entrada
- Guardar información de nuestras solicitudes ajax (a través de Backbone)
Luego en la segunda parte...
Una vez que tengamos nuestro back-end configurado, procederemos a generar el HTML necesario para nuestra caja meta junto con los datos para cada respuesta en formato JSON. También escribiremos el JavaScript que lo une todo. Cubriremos lo siguiente:
- La salida HTML base para la caja meta
- Generar una plantilla del lado del cliente junto con las respuestas en JSON
- El JavaScript necesario para unirlo todo
Espero que esta pequeña serie te resulte interesante, y estoy deseando ayudarte a iniciarte en el uso de Backbone.js dentro de un plugin de WordPress.
Lo que vamos a construir
Este pequeño plugin utilizará un tipo de entrada personalizada para guardar las preguntas. Luego, en una caja meta, crearemos cuatro campos de entrada que permitirán a los usuarios introducir posibles respuestas a la pregunta actual y seleccionar cuál de ellas es la respuesta correcta. Cuando se cambia una respuesta, se activará el botón de guardar correspondiente. Cuando se haga clic, usaremos el método model.save()
integrado de Backbone para guardar los datos en la base de datos de WordPress. Además, cuando las respuestas se escriben en los campos de entrada, el cuadro de selección bajo él actualizará automáticamente sus valores, ya que estará buscando cambios en los modelos. Todas estas cosas son relativamente sencillas de hacer con Backbone y después de leer este tutorial, serás capaz de empezar a llevar tus plugins al siguiente nivel utilizándolos dentro de WordPress.
Hay mucho que cubrir, ¡así que empecemos!
1. Crea el plugin
Tenemos que hacer todos los primeros pasos habituales involucrados en la creación de cualquier plugin, crear las carpetas de archivos.
- Crear una carpeta llamada wp_quiz
- Crear un archivo PHP con el mismo nombre
- Crear una carpeta llamada js
- Crear una carpeta llamada src
La estructura de carpetas debería tener el siguiente aspecto.
2. Añade la cabecera del plugin
Dentro de wp_quiz.php.
1 |
/*
|
2 |
Plugin Name: WP Quiz
|
3 |
Plugin URI: https://wp.tutsplus.com/author/shaneosbourne/
|
4 |
Description: An example of using Backbone within a plugin.
|
5 |
Author: Shane Osbourne
|
6 |
Version: 0.1
|
7 |
Author URI: http://wp.tutsplus.com/author/shaneosbourne/
|
8 |
*/
|
3. Añadir ganchos para crear una instancia del plugin
Todavía dentro de wp_quiz.php, tenemos que hacer lo siguiente:
- Incluir la clase principal de nuestro plugin
- Crear una función que cree una instancia de la clase
- Añadir un gancho para llamar a la función únicamente cuando el usuario sea un administrador
1 |
/** wp_quiz.php **/
|
2 |
|
3 |
include 'src/WpQuiz.php'; // Class File |
4 |
|
5 |
// Create an instance of the Plugin Class
|
6 |
function call_wp_quiz() { |
7 |
return new WpQuiz( 'admin' ); |
8 |
}
|
9 |
|
10 |
// Only when the current user is an Admin
|
11 |
if ( is_admin ) |
12 |
add_action( 'init', 'call_wp_quiz' ); |
13 |
|
14 |
// Helper function
|
15 |
if ( ! function_exists( 'pp' ) ) { |
16 |
function pp() { |
17 |
return plugin_dir_url( __FILE__ ); |
18 |
}
|
19 |
}
|
Poner la función auxiliar pp()
dentro de este archivo nos permitirá hacer referencia a otros archivos relativos a la raíz de la carpeta del plugin (lo verás en acción en breve).
4. Crear la clase plugin
Dentro de la carpeta src, crea un archivo llamado WpQuiz.php.
En esta clase del plugin, necesitaremos algunos métodos diferentes para lograr lo siguiente:
- Registrar el tipo de entrada personalizada
- Añadir una caja meta
- Recuperar el contenido de la caja meta y generar html y algunos datos JSON en ella
- Escuchar las solicitudes PUT y guardar los datos en la base de datos
- Guardar los datos de nuestras cajas meta en acciones 'guardar' estándar
Sin embargo, antes de escribir los métodos, vamos a almacenar información como propiedades de clase. Almacenamos esta información en la parte superior de nuestro archivo de clase para que las modificaciones sean más fáciles de hacer más adelante. La matriz answerIds
contiene las claves que usaremos a lo largo de este plugin para guardar datos usando el add_post_meta()
integrado.
Añadir las propiedades
1 |
/** src/WpQuiz.php **/
|
2 |
class WpQuiz { |
3 |
|
4 |
// Names of Custom Post Type
|
5 |
public $postTypeNameSingle = 'Question'; |
6 |
public $postTypeNamePlural = 'Questions'; |
7 |
|
8 |
// Meta Box Stuff
|
9 |
public $metaBoxTitle = 'Answers'; |
10 |
public $metaBoxTempl = 'templates/metabox.templ.php'; |
11 |
|
12 |
// Question Id's
|
13 |
public $answerIds = array( 'quiz-a-1', 'quiz-a-2', 'quiz-a-3', 'quiz-a-4' ); |
14 |
|
15 |
// Javascript
|
16 |
public $jsAdmin = '/js/admin.js'; |
17 |
|
18 |
}
|
Añadir el constructor
- Primero registramos el Tipo de Entrada Personalizada usando otro método auxiliar (que se verá más adelante)
- Después estamos registrando un gancho para cargar nuestra meta caja
- También necesitamos un método separado para aceptar nuestras solicitudes Ajax
- Finalmente, cuando se carga una página, queremos guardar la información de nuestra caja meta
1 |
/** src/WpQuiz.php **/
|
2 |
|
3 |
public function __construct( $type ) { |
4 |
switch ( $type ) { |
5 |
case 'admin' : |
6 |
// Register the Post Type
|
7 |
$this->registerPostType( |
8 |
$this->postTypeNameSingle, |
9 |
$this->postTypeNamePlural |
10 |
);
|
11 |
|
12 |
// Add the Meta Box
|
13 |
add_action( 'add_meta_boxes', array( $this, 'addMetaBox' ) ); |
14 |
|
15 |
// Accept an Ajax Request
|
16 |
add_action( 'wp_ajax_save_answer', array( $this, 'saveAnswers' ) ); |
17 |
|
18 |
// Watch for Post being saved
|
19 |
add_action( 'save_post', array( $this, 'savePost' ) ); |
20 |
}
|
21 |
}
|
Añadir la caja meta
- Añade los archivos JavaScript necesarios para este plugin, de nuevo utilizando un método auxiliar (lo veremos más tarde)
- Crea un ID único para este plugin basado en el nombre del tipo de entrada
- Añade la caja meta usando las propiedades que establecimos anteriormente
1 |
/** src/WpQuiz.php **/
|
2 |
|
3 |
public function addMetaBox() { |
4 |
|
5 |
// Load the Javascript needed on this admin page.
|
6 |
$this->addScripts(); |
7 |
|
8 |
// Create an id based on Post-type name
|
9 |
$id = $this->postTypeNameSingle . '_metabox'; |
10 |
|
11 |
// Add the meta box
|
12 |
add_meta_box( |
13 |
$id, |
14 |
$this->metaBoxTitle, |
15 |
array( $this, 'getMetaBox' ), // Get the markup needed |
16 |
$this->postTypeNameSingle |
17 |
);
|
18 |
|
19 |
}
|
Obtener el contenido de la caja meta
Aquí estamos recorriendo nuestros ID de respuesta y construyendo una matriz que contiene los datos meta extraídos con nuestro método auxiliar getOneAnswer
. Creamos esta nueva matriz para poder codificarla y enviarla a nuestra plantilla en formato JSON, tal y como le gusta a Backbone. Enviamos datos a nuestra plantilla utilizando la matriz $viewData
que puedes ver a continuación. Esto mantiene todo el HTML fuera de peligro y nos permite trabajar en él en un archivo separado. Echaremos un vistazo rápido al método getTemplatePart
más adelante, pero si quieres una explicación en profundidad sobre por qué lo uso, por favor echa un vistazo a Mejorar tu flujo de trabajo – ¡Separa tu márcado de tu lógica!
1 |
/** src/WpQuiz.php **/
|
2 |
|
3 |
public function getMetaBox( $post ) { |
4 |
|
5 |
// Get the current values for the questions
|
6 |
$json = array(); |
7 |
foreach ( $this->answerIds as $id ) { |
8 |
$json[] = $this->getOneAnswer( $post->ID, $id ); |
9 |
}
|
10 |
|
11 |
// Set data needed in the template
|
12 |
$viewData = array( |
13 |
'post' => $post, |
14 |
'answers' => json_encode( $json ), |
15 |
'correct' => json_encode( get_post_meta( $post->ID, 'correct_answer' ) ) |
16 |
);
|
17 |
|
18 |
echo $this->getTemplatePart( $this->metaBoxTempl, $viewData ); |
19 |
|
20 |
}
|
Obtener una respuesta única - Ayuda contextual
Solo estamos devolviendo una matriz de los datos necesarios en nuestra plantilla. Puedes pensar en esto como la creación de un modelo único necesario en el front-end.
1 |
/** src/WpQuiz.php **/
|
2 |
|
3 |
public function getOneAnswer( $post_id, $answer_id ) { |
4 |
return array( |
5 |
'answer_id' => $answer_id, |
6 |
'answer' => get_post_meta( $post_id, $answer_id, true ) |
7 |
);
|
8 |
}
|
Guardar entrada
Cuando un usuario hace clic para guardar una entrada en la que aparezca nuestra caja meta, tenemos que hacer un par de comprobaciones para asegurarnos de que estamos guardando nuestro tipo de entrada personalizada y que el usuario actual tiene los permisos correctos, si ambas comprobaciones están bien, entonces guardamos las cuatro respuestas de la meta caja y la respuesta correcta.
1 |
/** src/WpQuiz.php **/
|
2 |
|
3 |
public function savePost( $post_id ) { |
4 |
// Check that we are saving our Custom Post type
|
5 |
if ( $_POST['post_type'] !== strtolower( $this->postTypeNameSingle ) ) { |
6 |
return; |
7 |
}
|
8 |
|
9 |
// Check that the user has correct permissions
|
10 |
if ( ! $this->canSaveData( $post_id ) ) { |
11 |
return; |
12 |
}
|
13 |
|
14 |
// Access the data from the $_POST global and create a new array containing
|
15 |
// the info needed to make the save
|
16 |
$fields = array(); |
17 |
foreach ( $this->answerIds as $id ) { |
18 |
$fields[$id] = $_POST[$id]; |
19 |
}
|
20 |
|
21 |
// Loop through the new array and save/update each one
|
22 |
foreach ( $fields as $id => $field ) { |
23 |
add_post_meta( $post_id, $id, $field, true ); |
24 |
// or
|
25 |
update_post_meta( $post_id, $id, $field ); |
26 |
}
|
27 |
|
28 |
// Save/update the correct answer
|
29 |
add_post_meta( $post_id, 'correct_answer', $_POST['correct_answer'], true ); |
30 |
// or
|
31 |
update_post_meta( $post_id, 'correct_answer', $_POST['correct_answer'] ); |
32 |
|
33 |
}
|
Guardar las respuestas a parir de las solicitudes Ajax
Aquí es donde recibiremos los datos pasados al servidor desde Backbone. Necesitamos lo siguiente:
- Accede a los datos enviados como una solicitud PUT. Como estará en formato JSON, necesitamos decodificarlo
- Comprueba de nuevo que el usuario actual tenga los permisos necesarios
- Sigue adelante e intenta salvarlos
- Si Add o Update fueron realizados correctamente, simplemente podemos devolver los datos recién guardados y Backbone verá esto como un guardado exitoso
- Si ninguno de los dos tuvo éxito, simplemente devolvemos 0 para indicar un error
1 |
/** src/WpQuiz.php **/
|
2 |
|
3 |
public function saveAnswers() { |
4 |
// Get PUT data and decode it
|
5 |
$model = json_decode( file_get_contents( "php://input" ) ); |
6 |
|
7 |
// Ensure that this user has the correct permissions
|
8 |
if ( ! $this->canSaveData( $model->post_id ) ) { |
9 |
return; |
10 |
}
|
11 |
|
12 |
// Attempt an insert/update
|
13 |
$update = add_post_meta( $model->post_id, $model->answer_id, $model->answer, true ); |
14 |
// or
|
15 |
$update = update_post_meta( $model->post_id, $model->answer_id, $model->answer ); |
16 |
|
17 |
// If a save or update was successful, return the model in JSON format
|
18 |
if ( $update ) { |
19 |
echo json_encode( $this->getOneAnswer( $model->post_id, $model->answer_id ) ); |
20 |
} else { |
21 |
echo 0; |
22 |
}
|
23 |
die(); |
24 |
}
|
Los métodos de ayuda
Aquí están las cuatro ayudas auxiliares mencionadas en los fragmentos anteriores.
-
canSaveData()
- Esto solo garantiza que el usuario actual tiene los permisos relevantes para editar/actualizar esta entrada. -
addScripts()
- Ten en cuenta que aquí estamos asegurando que pasamos el quinto parámetro a la funciónwp_register_script()
. Esto cargará nuestro JavaScript personalizado en el pie de página y se asegurará de que nuestros datos JSON estén disponibles. Además, si estás utilizando el editor de WordPress en tu plugin, no necesitas especificar Backbone como dependencia, ya que ya estará disponible para ti. Sin embargo, Yo lo incluyo aquí por el bien del ejemplo. -
registerPostType(
) - Esto es algo que uso a menudo en los plugins. Simplemente hace la vida más fácil al añadir un nuevo tipo de entrada personalizada. Acepta versiones singulares y plurales del nombre porque no siempre es tan sencillo como simplemente añadir una 's'. -
getTemplatePart()
- Nunca he sido aficionado a tener marcado dentro de mis métodos. Este pequeño ayudante permitirá el uso de un archivo de plantilla independiente.
1 |
/** src/WpQuiz.php **/
|
2 |
|
3 |
/**
|
4 |
* Determine if the current user has the relevant permissions
|
5 |
*
|
6 |
* @param $post_id
|
7 |
* @return bool
|
8 |
*/
|
9 |
private function canSaveData( $post_id ) { |
10 |
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) |
11 |
return false; |
12 |
if ( 'page' == $_POST['post_type'] ) { |
13 |
if ( ! current_user_can( 'edit_page', $post_id ) ) |
14 |
return false; |
15 |
} else { |
16 |
if ( ! current_user_can( 'edit_post', $post_id ) ) |
17 |
return false; |
18 |
}
|
19 |
return true; |
20 |
}
|
21 |
|
22 |
private function addScripts() { |
23 |
wp_register_script( 'wp_quiz_main_js', pp() . $this->jsAdmin , array( 'backbone' ), null, true ); |
24 |
wp_enqueue_script( 'wp_quiz_main_js' ); |
25 |
}
|
26 |
|
27 |
/**
|
28 |
* Register a Custom Post Type
|
29 |
*
|
30 |
* @param $single
|
31 |
* @param $plural
|
32 |
* @param null $supports
|
33 |
*/
|
34 |
private function registerPostType( $single, $plural, $supports = null ) { |
35 |
|
36 |
$labels = array( |
37 |
'name' => _x( $plural, 'post type general name' ), |
38 |
'singular_name' => _x( "$single", 'post type singular name' ), |
39 |
'add_new' => _x( "Add New $single", "$single" ), |
40 |
'add_new_item' => __( "Add New $single" ), |
41 |
'edit_item' => __( "Edit $single" ), |
42 |
'new_item' => __( "New $single" ), |
43 |
'all_items' => __( "All $plural" ), |
44 |
'view_item' => __( "View $single" ), |
45 |
'search_items' => __( "Search $plural" ), |
46 |
'not_found' => __( "No $plural found" ), |
47 |
'not_found_in_trash' => __( "No $single found in Trash" ), |
48 |
'parent_item_colon' => '', |
49 |
'menu_name' => $plural |
50 |
);
|
51 |
$args = array( |
52 |
'labels' => $labels, |
53 |
'public' => true, |
54 |
'publicly_queryable' => true, |
55 |
'show_ui' => true, |
56 |
'show_in_menu' => true, |
57 |
'query_var' => true, |
58 |
'rewrite' => true, |
59 |
'capability_type' => 'post', |
60 |
'has_archive' => true, |
61 |
'hierarchical' => false, |
62 |
'menu_position' => null, |
63 |
'supports' => ( $supports ) ? $supports : array( 'title', 'editor', 'page-attributes' ) |
64 |
);
|
65 |
register_post_type( $single, $args ); |
66 |
}
|
67 |
|
68 |
/**
|
69 |
* Render a Template File
|
70 |
*
|
71 |
* @param $filePath
|
72 |
* @param null $viewData
|
73 |
* @return string
|
74 |
*/
|
75 |
public function getTemplatePart( $filePath, $viewData = null ) { |
76 |
|
77 |
( $viewData ) ? extract( $viewData ) : null; |
78 |
|
79 |
ob_start(); |
80 |
include ( "$filePath" ); |
81 |
$template = ob_get_contents(); |
82 |
ob_end_clean(); |
83 |
|
84 |
return $template; |
85 |
}
|
5. En el front-end
En este punto, hemos configurado todo lo necesario para nuestro back-end. Es hora de tomarnos un descanso y prepararnos para la siguiente parte donde nos ensuciaremos con las plantillas del lado cliente, JavaScript y Backbone.js. Espero verte allí, va a ser un fantástico tutorial.