Mejore su flujo de trabajo: ¡separe su marcado de su lógica!
Spanish (Español) translation by Elías Nicolás (you can also view the original English article)
En este tutorial, explicaré una técnica que le permite utilizar un archivo de plantilla para todas sus necesidades de HTML. Ya no tendrá que hacer "eco" de las cadenas desde sus funciones, o preocuparse por ingresar y salir de PHP solo para generar un margen de beneficio.
He pasado muchos años utilizando marcos MVC (como Zend y hoy en día Laravel) donde es una buena práctica separar su 'lógica de programación' (funciones o métodos) de su 'vista' (el marcado HTML resultante). Esto siempre lleva a una base de código más mantenible y en realidad es mucho más fácil de escribir. Teniendo este trasfondo, me invitó a encontrar una solución similar al desarrollar complementos para WordPress. No es nada demasiado sofisticado: es solo un pequeño 'ayudante' que te permitirá eliminar todos los fragmentos de código HTML y las 'escapadas' extrañas de tus funciones y colocarlas de forma segura en su propio archivo 'plantilla'.
Entonces, espero que este tutorial te suene interesante y sin más preámbulos, ¡comencemos!
Paso 1 Entendiendo lo que vamos a mejorar
Comencemos este tutorial echando un vistazo a exactamente lo que vamos a mejorar.
Es muy común ver algo como esto dentro de un complemento: (este fragmento proviene de uno de mis propios tutoriales en este sitio :p)
1 |
|
2 |
add_shortcode( 'faq', function() { |
3 |
$posts = get_posts( array( // Get the FAQ Custom Post Type |
4 |
'numberposts' => 10, |
5 |
'orderby' => 'menu_order', |
6 |
'order' => 'ASC', |
7 |
'post_type' => 'faq', |
8 |
));
|
9 |
$faq = '<div id="wptuts-accordion">'; // Open the container |
10 |
foreach ( $posts as $post ) { // Generate the markup for each Question |
11 |
$faq .= sprintf( ( '<h3><a href="">%1$s</a></h3><div>%2$s</div>' ), |
12 |
$post->post_title, |
13 |
wpautop( $post->post_content ) |
14 |
);
|
15 |
}
|
16 |
$faq .= '</div>'; // Close the container |
17 |
return $faq; // Return the HTML. |
18 |
});
|
¿Qué está mal con eso?
Pues nada, en realidad. ¡Pero podría ser más limpio, más fácil de escalar y más fácil de mantener!
Corriendo de arriba a abajo, podemos ver que todos dentro de una sola función somos:
- Consultar la base de datos para publicaciones de un determinado tipo.
- Asignar una cadena HTML a una variable
- Realizar un bucle y concatenar más marcas en la cadena
- Devolviendo la cadena construida
Ahora bien, puedes estar mirando esto y pensando "¡Gran cosa! Solo son unas pocas líneas de HTML, ¿cuál es el problema?" En algunos aspectos, está bien dentro de sus derechos para pensar así. Pero recuerde, en este momento solo hay 17 líneas de código. ¿Qué sucede cuando amplía / mejora el complemento? ¿Qué sucede cuando su complemento crece hasta 50/100/1000 líneas de código (o más)? ¿Seguirá siendo feliz teniendo cadenas de HTML alrededor de su función en varios lugares? ¿Qué sucede cuando desea generar algún HTML que necesite un poco de 'escape' incómodo para funcionar correctamente dentro de su código PHP?
¡Esperamos que pueda ver que este enfoque para crear y generar marcado HTML puede ser muy problemático! Sin mencionar que se vuelve muy difícil mantener y mejorar el HTML cuando está disperso.
Así que, con todo esto en mente, me he propuesto cambiar la forma en que piensas acerca de cómo generar HTML en WordPress. Siempre.
Paso 2 Creando el complemento Renderizador de vista
Ok, vamos a romper con esto.
Crea los archivos y carpetas
- Crear una nueva carpeta de plugin llamada View
- Dentro de esa carpeta, cree el archivo plugin view_renderer.php
- Ahora crea un archivo llamado View.php - Esta será nuestra clase
Incluir la clase
Nuestro complemento es simple, solo incluye la clase View
para que podamos usarlo en cualquiera de nuestros otros complementos.
1 |
|
2 |
/* view_renderer.php */
|
3 |
|
4 |
include('View.php'); |
Ok, ahora que hemos incluido la clase View
, es hora de construirla.
La clase View
Aquí tenemos una clase llamada View
con una única función estática llamada render
(esto nos permitirá usar la sintaxis View::render( $template )
desde cualquier lugar dentro de nuestros complementos) y toma dos parámetros:
-
$filePath
- La ruta al archivo de plantilla. No olvide que vamos a mantener nuestras plantillas dentro de la carpeta View que creamos anteriormente -
$viewData
- cualquier variable a la que nos gustaría tener acceso dentro de la plantilla (mucho más sobre esto más adelante)
Copie el siguiente código en View.php:
1 |
|
2 |
<?php
|
3 |
|
4 |
/** View.php **/
|
5 |
|
6 |
class View { |
7 |
|
8 |
/**
|
9 |
* -------------------------------------
|
10 |
* Render a Template.
|
11 |
* -------------------------------------
|
12 |
*
|
13 |
* @param $filePath - include path to the template.
|
14 |
* @param null $viewData - any data to be used within the template.
|
15 |
* @return string -
|
16 |
*
|
17 |
*/
|
18 |
public static function render( $filePath, $viewData = null ) { |
19 |
|
20 |
// Was any data sent through?
|
21 |
( $viewData ) ? extract( $viewData ) : null; |
22 |
|
23 |
ob_start(); |
24 |
include ( $filePath ); |
25 |
$template = ob_get_contents(); |
26 |
ob_end_clean(); |
27 |
|
28 |
return $template; |
29 |
}
|
30 |
}
|
31 |
?>
|
Entonces, ¿qué es exactamente lo que está pasando aquí?
-
En primer lugar, verificamos si la variable
$viewData
tiene un valor (es decir, ¿enviamos algo para usar en la plantilla?). Si lo hace, extraemos los contenidos (más sobre esto más adelante). -
Entonces estamos haciendo uso del buffer de salida de PHP. Nos permite analizar un archivo PHP y guardar el contenido en una variable
-
Finalmente devolvemos la cadena.
Nota: No olvide activar el complemento ahora desde el panel de administración
Parece bastante simple eh? ¡Exactamente! Pero si bien parece ser solo una pequeña función muy simple, en realidad nos brinda el lujo de poder escribir nuestros complementos de una manera súper organizada, escalable y mantenible. Por favor, permítame demostrar ...
Paso 3 Un ejemplo del mundo real
Vamos a crear un plugin simple llamado Slider
** Nota: Esto es sólo para fines de demostración. Siéntete libre de usar tu propio plugin aquí.
- Crea una carpeta llamada Slider
- En esa carpeta, crea un archivo llamado Slider.php
- Copia el siguiente código en Slider.php
1 |
|
2 |
<?php
|
3 |
/*
|
4 |
Plugin Name: Slider
|
5 |
Plugin URI: https://wp.tutsplus.com
|
6 |
Description: Generic Slider plugin to demonstrate View Renderer.
|
7 |
Author: Shane Osbourne
|
8 |
Version: 0.1
|
9 |
Author URI: http://wp.tutsplus.com/author/shaneosbourne/
|
10 |
*/
|
11 |
?>
|
Añadir un código corto
Bien, ahora vamos a agregar un shortcode que recogerá las últimas 5 publicaciones y las mostrará en una lista con el título y el contenido. (En aras de la brevedad, agregaremos nuestra clase de complementos y nuestros enlaces de acción en el mismo archivo de complementos, pero no hagas esto en la "vida real": p)
1 |
|
2 |
/**
|
3 |
* Add the Shortcode (PHP 5.3 and above)
|
4 |
*/
|
5 |
add_shortcode( 'slider', function() { |
6 |
|
7 |
return Slider::display(); |
8 |
|
9 |
} ); |
Eso nos permitirá usar simplemente [slider]
en cualquier publicación / página y generará el resultado de Slider::display()
Agregue el método de clase Slider y display()
1 |
|
2 |
class Slider { |
3 |
|
4 |
public static function display() { |
5 |
|
6 |
// Return HTML HERE.
|
7 |
|
8 |
}
|
9 |
}
|
Recibe los últimos 5 mensajes.
1 |
|
2 |
|
3 |
/*
|
4 |
* Get the latest 5 posts
|
5 |
*/
|
6 |
public static function display() { |
7 |
|
8 |
$posts = get_posts( array( 'numberposts' => 5 ) ); |
9 |
|
10 |
}
|
Ahora tenemos una variedad de objetos post
y estamos listos para construir nuestro HTML en bucle a través de ellos. ¡Pero no vamos a comenzar simplemente a insertar cadenas HTML en nuestra función aquí! En su lugar, vamos a pasar la matriz de objetos a un archivo de plantilla y tendremos todo el HTML generado fuera de peligro.
Crear la plantilla
- Crear una carpeta llamada templates
- Dentro de esa carpeta, crea un archivo llamado 01.template.php
Esta plantilla mantendrá todo nuestro marcado y nos permitirá acceder a los datos que le enviemos más adelante.
Enviando datos a la plantilla
Cada vez que deseamos utilizar cualquier variable dentro de nuestras plantillas, simplemente podemos enviarlas estableciendo un valor en la matriz $viewData
. Cualquier persona familiarizada con el uso de marcos MVC se sentirá como en casa con este enfoque.
1 |
|
2 |
$viewData = array( 'posts' => $posts ); |
La matriz key aquí ('posts
') es importante porque así es como nos referiremos a los datos desde la plantilla. (Puedes llamar a esto como quieras, pero apégate a algo que tenga sentido).
Construyendo la Plantilla
Bien, hemos visto cómo recuperar las últimas 5 publicaciones y cómo enviar esa matriz de objetos a la plantilla, ahora es el momento de completar el archivo de la plantilla.
1 |
|
2 |
<div>
|
3 |
<ul>
|
4 |
<?php foreach ($posts as $post ) : ?> |
5 |
<!-- Loop -->
|
6 |
|
7 |
<li>
|
8 |
<h1><?= $post->post_title ?></h1> |
9 |
<p><?= $post->post_content ?></p> |
10 |
</li>
|
11 |
|
12 |
<!-- Loop End -->
|
13 |
<?php endforeach; ?> |
14 |
</ul>
|
15 |
</div>
|
Ah! ¿Qué bien se siente tener todo ese marcado en su propio archivo separado, lejos de nuestra lógica de programación y recuperación de datos? Genial, lo sé! La parte más importante de este enfoque es que solo estamos 'accediendo' a datos desde variables dentro de la plantilla. Toda la 'lógica' debe hacerse dentro del método que llama a la plantilla. Esto conduce a un flujo de trabajo muy agradable ya que tiene una completa separación de preocupaciones.
Solo imagine lo fácil que será ahora cuando esté listo para desarrollar este complemento. No más concatenación de cadenas y caracteres de escape dentro de las funciones.
Devolviendo la plantilla renderizada
Ok, hemos visto todas las partes componentes, veamos cómo encaja todo esto para permitirnos renderizar una plantilla y recuperar una cadena (que luego podemos volver a nuestro código corto):
- Primero necesitamos almacenar una referencia a nuestra plantilla en una propiedad estática
- Entonces tenemos que comprobar que la clase
View
existe - Luego generamos la ruta completa a nuestro archivo de plantilla al tomar una referencia al directorio del complemento actual y concatenar nuestra propiedad estática
$template
- Finalmente, llamamos a nuestro método
View::render()
y le pasamos los dos parámetros necesarios
En este caso, return el resultado de la plantilla renderizada porque así es como funcionan los códigos cortos. Pero si en su lugar tenía que hacer echo de los resultados (por ejemplo, cuando crea una página de administrador, la devolución de llamada espera que su salida se imprima directamente), simplemente reemplace el return por echo.
El método display()
completo
1 |
|
2 |
class Slider { |
3 |
|
4 |
static $template = '/templates/01.template.php'; |
5 |
|
6 |
public static function display() { |
7 |
|
8 |
if ( class_exists( 'View' ) ) { |
9 |
|
10 |
// Get the last 5 posts
|
11 |
$posts = get_posts( array( 'numberposts' => 5 ) ); |
12 |
|
13 |
// Set view Data
|
14 |
$viewData = array( |
15 |
'posts' => $posts |
16 |
);
|
17 |
|
18 |
// Get the full path to the template file.
|
19 |
$templatePath = dirname( __FILE__ ) . static::$template; |
20 |
|
21 |
// Return the rendered HTML
|
22 |
return View::render( $templatePath, $viewData ); |
23 |
|
24 |
}
|
25 |
else { |
26 |
return "You are trying to render a template, but we can't find the View Class"; |
27 |
}
|
28 |
}
|
29 |
}
|
¡Espero que pueda apreciar el nivel de organización que este enfoque le brindará! Ahora su función display solo es responsable de recopilar los datos que necesita y de devolver el resultado de la plantilla renderizada.
Llevándolo más lejos
Nuestro ejemplo anterior es tan básico como se pone. Aun así, sigue siendo un flujo de trabajo muy mejorado. Ahora veamos otro ejemplo que muestra lo útil que realmente puede ser.
Digamos, por ejemplo, que su complemento hace uso de un meta box personalizado. Para hacer eso tendríamos que:
- Agregar una función constructora a la clase
Slider
- Agrega un método para agregar el metabox a cada publicación
- Agregue un método de devolución de llamada para representar el HTML para el meta box.
- Agregue el enlace apropiado en el archivo del complemento para crear una instancia de la clase solo cuando agregue / edite publicaciones
- Finalmente, agregaríamos un archivo de plantilla como lo hicimos anteriormente, y lo agregaríamos como una propiedad al comienzo de la clase
1 |
|
2 |
class Slider { |
3 |
|
4 |
static $metaBox = '/templates/metabox.template.php'; |
5 |
|
6 |
public function __construct() { |
7 |
add_action( 'add_meta_boxes', array( $this, 'add_some_meta_box' ) ); |
8 |
}
|
9 |
|
10 |
/**
|
11 |
* Adds the meta box container
|
12 |
*/
|
13 |
public function add_some_meta_box() { |
14 |
add_meta_box( |
15 |
'some_meta_box_name', |
16 |
'Some Meta Box Headline', |
17 |
array( $this, 'render_meta_box_content' ), |
18 |
'post', |
19 |
'advanced', |
20 |
'high', |
21 |
);
|
22 |
}
|
23 |
|
24 |
/**
|
25 |
* Render Meta Box content
|
26 |
*/
|
27 |
public function render_meta_box_content() { |
28 |
/** From the Codex **/
|
29 |
echo '<h1>TEST OUTPUT - this gets rendered inside the meta box.</h1>'; |
30 |
}
|
31 |
} // class |
32 |
|
33 |
// add the action hook
|
34 |
function call_Slider() { |
35 |
return new Slider(); |
36 |
}
|
37 |
|
38 |
if ( is_admin() ) |
39 |
add_action( 'load-post.php', 'call_Slider' ); |
Eche un vistazo al método render_meta_box_content
allí. ¡Es la oportunidad perfecta para usar View Renderer! Imagina un ejemplo más realista como este:
1 |
|
2 |
/**
|
3 |
* Render Meta Box content
|
4 |
*/
|
5 |
public function render_meta_box_content( $post ) { |
6 |
|
7 |
$name = get_post_meta( $post->ID, "name" ); |
8 |
$fieldName = static::$fieldName; |
9 |
|
10 |
echo '<h3>Your name: </h3>'; |
11 |
echo '<label for="' . $fieldName . '">Name: </label>'; |
12 |
echo '<input id="' . $fieldName . '" name="' . $fieldName . '" value="' . $name . '" placeholder="Enter your name here" />'; |
13 |
echo '<button class="button">Update</button>'; |
14 |
}
|
Urg! Claro, hace el trabajo, ¡pero es tan difícil hacerlo de esta manera! ¿Qué tal si utilizamos nuestro View Renderer?
1 |
|
2 |
/**
|
3 |
* Render Meta Box content
|
4 |
*/
|
5 |
public function render_meta_box_content( $post ) { |
6 |
|
7 |
$viewData = array( |
8 |
'name' => get_post_meta( $post->ID, 'name' ), |
9 |
'field' => static::$fieldName |
10 |
);
|
11 |
|
12 |
$templatePath = dirname( __FILE__ ) . static::$metabox; |
13 |
echo View::render( $templatePath, $viewData ); |
14 |
|
15 |
}
|
Y en el archivo de plantilla:
1 |
|
2 |
<h3>Your name: </h3> |
3 |
<label for="<?= $field ?>">Name: </label> |
4 |
<input id="<?= $field ?>" name="<?= $field ?>" value="<?= $name ?>" placeholder="Enter your name here" /> |
5 |
<button class="button">Update</button> |
Puede que solo parezca un beneficio muy pequeño en este ejemplo. Pero confía en mí, si mantienes tus preocupaciones separadas de esta manera, te convertirás en un desarrollador de WordPress mucho mejor rápidamente.
Conclusión
Creo que a estas alturas es probable que tenga una buena comprensión de lo que estamos tratando de lograr aquí y le insto a que intente usar esta técnica cuando cree complementos en el futuro. Esperamos que encuentre que la "separación de preocupaciones" sea beneficiosa para usted.
Notas del tutorial:
- A pesar de que convertimos el View Renderer en un complemento por sí mismo, en su lugar, simplemente podría agregarlo a los complementos existentes. Esto eliminará el paso adicional de tener que asegurarse de que el complemento esté activado antes de usarlo en cualquier lugar.
- No está limitado a los casos de uso explicados en este tutorial, se puede usar donde quiera que normalmente genere HTML (¿Qué le parece usar un archivo de plantilla para generar algún JavaScript 'en línea'? O sobre algunas reglas CSS específicas basadas en opciones recuperadas de la base de datos?)
Me interesaría saber qué usos ha encontrado para esta técnica, así que por favor comparta los comentarios :)