Post on 17-Feb-2017
De qué va esta charla
Abrir perspectiva sobre buenas prácticas de desarrollo sobre este CMS
Mostrar código mantenible, cercano al que crearíamos con Symfony2, Silex o Laravel
/meAsier Marqués
simettric.com & 4visionshq.com
linkedin.com/in/asier
@asiermarques
Qué buscamos
• Reutilizar código
• Reutilizar código fuera de WordPress
• Una buena estructura de código
• Que el código sea testable
Request y ejecución1. Se recibe la petición HTTP
2. El sistema de routing hace el match de la ruta que coincida con la url de la petición
3. Se crea el objeto WP_Query y las query_vars
4. En base al WP_Query se localiza el template
5. Se retorna el resultado
http://codex.wordpress.org/Plugin_API/Action_Reference
Request y ejecución
1. /category/animales = index.php?category=animales
2. new WP_Query( array( “category” => “animales”) );
3. theme/category.php
Inyección de dependenciasNos permite hacer más portable y desacoplado nuestro código.
• Pimple
• Symfony Dependency Injection
Inyección de dependencias
$container[“blog.repository”] = function ($c) use ($wp_query){
return new WP_BlogRepository( $wp_query );
};
$container[“blog.model”] = function($c){
return new BlogModel( $c[“blog.repository”] );
}
class WP_BlogRepository implements RepositoryInterface {
private $query;
function __construct( WP_Query $wp_query ){
$this->query = $wp_query;
}
function getPosts($page=1, $limit=20){
$this->query->query_vars = array( “paged”=>$page, “posts_per_page” => $limit
);
return $this->query->get_posts();
}
}
class MongoDB_BlogRepository implements RepositoryInterface {
[…]
function getPosts($page=1, $limit=20){
$posts = array();
[…]
return $posts;
}
}
class BlogModel {
private $_repository;
function __construct( RepositoryInterface $adapter ){
$this->_repository = $adapter;
}
function getPosts($page, $limit=20){
return $this->_repository->getPosts($page, $limit);
}
}
task_filter:path: /task/{category}/{custom}controller: TaskController::filterAction
task_detail:path: /task/{id}controller: TaskController::detailAction
Rutas en WordPressadd_action('init', function() {
add_rewrite_rule(
‘^tasks/([^/]*)/([^/]*)/?’,
’index.php?post_type=“task”&category=$matches[1]&custom=$matches[2]',
‘top'
);
add_rewrite_tag('%custom%', '(([^/]*))');
});
Rutas en WordPressAl crear la WP_Query, se incluyen los anteriores parámetros:
$wp_query->query_vars[“post_type”]
$wp_query->query_vars[“category”]
$wp_query->query_vars[“custom”]
task_filter:path: /task/{category}/{custom}controller: TaskController::filterAction
task_detail:path: /task/{id}controller: TaskController::detailAction
function addRoute($name, $path, $controller_name){
\preg_match_all(‘({\w+})’, $path, $found_params);
$found_params = isset($found_params[0]) && is_array($found_params[0]) ?
$found_params[0] : array();
$url_params = array();
$regexp = $path;
foreach($found_params as $i=>$_param){
$_key = str_replace(array(“{“,”}”), “”, $_param);
$regexp = str_replace($_param, ‘(\w+)’, $regexp);
$url_params[$_key] = ‘$matches[‘.($i+1).’]’;
}
$regexp = ‘^’ . $regexp . ‘$’;
$url = “index.php?” . http_build_query($params, ‘’, “&”);
$url = urldecode($url);
\add_rewrite_rule($regexp, $url, ’top’);
Loop personalizadofunction replaceQuery( WP_Query $wp_query ){
if( $wp_query->is_front_page() ){
//cambiamos el loop de la portada$wp_query = new WP_Query(array( “category” => 1 ));
//evitamos un loop infinitoremove_action(“replaceQuery”);
}
};
add_action(“pre_get_posts”, “replaceQuery”);
En el action parse_queryfunction match($wp_query){
if(isset($wp_query->query_vars[“__action”])) {
list( $controller_class, $controller_action ) = explode( “::”, $wp_query->query_vars[“__action”] );
$controller = new $controller_class($this->_container);
$callable = array( $controller, $controller_action );
call_user_func($callable, $wp_query); }
}
Templates personalizadasadd_filter( 'template_include', function ( $template ) {
if ( is_page( get_option(“landing_page_id”) ) ) {
$template = __DIR__ . “/Templates/landing.php“;
}
return $template;
}
En un controladorclass BlogController extends BaseController{
function indexAction( Request $request, Container $container ) {
$model = $container->get( “blog.model” );
$page = $request->get(“page”, 1);
return $this->render(“blog/posts.php”, array(
“posts” => $model->getPosts( $page );
));
}
}
Render con filtroclass BaseController{
private $container;
function render( $template, $params=array() ){
add_filter( “template_include” , function() use ($template){
[…]
});
}
En un controladorclass BlogController extends BaseController{
function indexAction( Request $request, Container $container ) {
$model = $container->get( “blog.model” );
$page = $request->get(“page”, 1);
return $this->render(“blog/index.html.twig”, array(
“posts” => $model->getPosts( $page );
));
}
}
Twigclass BaseController{
private $container;
function render( $template, $params=array() ){
$twig = $container->get(“twig”);
$html = $twig->render( $template, $params );
wp_die( $html );
}
}
Herramientas
• Gestión de librerías: composer
• Inyección de dependencias: Symfony o Pimple
• Configuración: Yaml y Configuration Component
• Templates: Twig
ComposerWordPress no ofrece gestión de librerías ni componentes por Composer
Por suerte existe el proyecto wpackagist.org
TDDWordPress test framework:
https://github.com/nb/wordpress-tests.git
WP Mock
https://github.com/10up/wp_mock
http://slides.com/jlopezmo/test-driven-development-in-wordpress