código mantenible, en wordpress

63
Código mantenible, con WordPress Asier Marqués @asiermarques

Upload: asier-marques

Post on 21-Jul-2015

201 views

Category:

Technology


0 download

TRANSCRIPT

Código mantenible, con WordPress

Asier Marqués @asiermarques

De qué va esta charlaIntroducción a los conceptos básicos de desarrollo con WordPress

Abrir perspectiva sobre buenas prácticas 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

El 23% de Internet utiliza WordPress.

• techcrunch.com

• thenewyorker.com

• BBC America

• Time Inc

• Fortune

• Google Ventures

• The walking dead

• The rolling stones

Beneficios

• Es un CMS muy conocido, muchos usuarios ya saben utilizar su backoffice

• Existe una gran cantidad de plugins

• Existe una gran cantidad de themes

• Un mercado amplio

Puntos negativos• Una base de desarrollo obsoleta

• Gran cantidad de plugins que no tienen ningún tipo de requisito de calidad

• Atado a un esquema de base de datos concreto

• Para proyectos cuyo core de negocio se salga de contenido, puede quedarse corto sin un coste de mantenimiento elevado

Cuando leemos en los requisitos que el proyecto debe programarse sobre Wordpress…

Guarrindongadas!!!

¿De qué se compone Wordpress?

• Plugins y Themes

• Filters y actions

• WP_Query y loop

• Custom post type y taxonomies

MultiSite

• WordPress es multisite

• Disponemos de funciones, actions y filters específicos para gestionar los blogs dentro de un network

• Casi todas esas funciones son las originales del proyecto Wordpress Mu

http://codex.wordpress.org/WPMU_Functions

wpmu_create_blog($domain, $path, $title, $user_id, $meta, $site_id)

Backoffice• Nos permite gestionar nuestro sitio y nuestro contenido

• Es totalmente personalizable, podemos

• Añadir secciones en el menú

• Añadir columnas en las tablas de contenido

• Añadir campos, y metaboxes, para usuarios, contenido y terms

• Añadir campos y opciones a los menús dinámicos de themes

Post Types• Todo es un Post

• Por defecto tenemos contenido de tipo post, page y attachment.

• El contenido puede ser jerárquico

• El contenido tiene un archivo

• El contenido puede tener metainformación

Custom post typeregister_post_type( 'book', array(

'labels' => array(“name” => “Libros”),

'public' => true,

'rewrite' => array( 'slug' => 'books' ),

'has_archive' => true,

'hierarchical' => false,

'supports' => array( 'title', 'editor', 'thumbnail', 'comments' )

);

Custom post type$product = get_post( $product_id );

$product_color = get_post_meta( $product, “color”, true);

$products = get_posts( array( “post_type” => “product” ) );

foreach( $products as $product ){

echo get_the_title( $product );

}

Taxonomies

• Todas las taxonomias son terms

• Por defecto tenemos los term “tag” y “category”

• Los term pueden ser jerárquicos o no

• Por defecto no hay soporte de metainformación

Custom taxonomyregister_taxonomy( ‘author’, 'book', array(

'hierarchical' => false,

'labels' => array( “name”=> “Autor” ),

'show_admin_column' => true,

'update_count_callback' => '_update_post_term_count',

'rewrite' => array( 'slug' => ‘authors’ ),

));

Custom Taxonomy$terms = get_terms(“talla”, array(“order_by”=>”count”));

foreach( $terms as $term ){

echo $term->name;

}

$product_terms = wp_get_post_terms( $product, “talla” );

Obtener enlacesget_permalink($post);

get_term_link($term, “color”);

get_post_type_archive_link( “post_type” )

wp_get_attachment_url( $post->ID );

$src_array = wp_get_attachment_image_src($post->ID, “size”);

$image_src = $src_array[0];

Plugins vs Themes• La responsabilidad principal de un theme es la de

dotar de un diseño al sitio web

• La responsabilidad de un plugin es la de dotar funcionalidad y personalización, sin acoplarse a un theme especifico

• Un theme puede actuar adicionalmente como plugin

• Los plugins pueden personalizar las funciones core de tipo “pluggables”

Themes

Los archivos más importantes en un theme

• styles.css

• index.php

• header.php y footer.php

• functions.php

index.phpget_header(“key”); //header-key.php

if ( have_posts() ) {

while ( have_posts() ) {

the_post();

//nuestro código

}

}

get_footer(“key”); //footer-key.php

header.php<html>

<head>

<?php wp_head(); ?>

</head>

<body>

footer.php

<?php wp_footer(); ?>

</body>

</html>

Template Hierarchyarchive-producto.php -> archive.php -> index.php

single-producto.php -> single.php -> index.php

taxonomy-color.php -> taxonomy.php -> index.php

search.php -> index.php

404.php -> index.php

front-page -> index.php

home.php -> index.php

http://codex.wordpress.org/Template_Hierarchy

Loop<?php

if ( have_posts() ) {

while ( have_posts() ) {

the_post(); ?>

<h2><?php the_title() ?></h2>

<p><?php the_content() ?></p>

<?php }

}

En realidad es un WP_Query<?php

global $wp_query;

if ( $wp_query->have_posts() ) {

while ( $wp_query->have_posts() ) {

$post = $wp_query->the_post(); ?>

<h2><?php echo get_the_title($post) ?></h2>

<p><?php echo get_the_content($post) ?></p>

<?php }

}

Actions y Filters

• Nos podemos suscribir a ellos para modificar y extender Wordpress

• Los actions actúan como eventos a los que nos suscribimos para realizar una operación.

• Los filters también son eventos, pero nos permiten manipular información o html del template.

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ón1. /category/animales = index.php?category=animales

2. new WP_Query( array( “category” => “animales”) );

3. theme/category.php

Rutas personalizadasadd_action('init', function() {

add_rewrite_rule(

‘^tasks/([^/]*)/([^/]*)/?’,

’index.php?post_type=“task”&category=$matches[1]&custom=$matches[2]',

‘top'

);

       add_rewrite_tag('%custom%', '(([^/]*))');

});

Rutas personalizadasAl 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”]

QueriesWP_Query $wp_query http://codex.wordpress.org/Class_Reference/WP_Query

wpdb $wpdb

http://codex.wordpress.org/Class_Reference/wpdb

$wp_query = new WP_Query(array(

“post_type” => “producto”,

“nombre_taxonomy” => “valor”,

“posts_per_page” => 20,

“meta_query” => array(

“relation” => “OR”,

array( “meta_key” => “color”, “meta_value” => “azul” ),

array( “meta_key” => “precio”,

“meta_value” => array(20, 200),

“compare” => “BETWEEN”,

“type” => numeric ) )

));

$wp_query = new WP_Query(array(

“post_type” => “producto”,

“nombre_taxonomy” => “valor”,

“posts_per_page” => 20,

“meta_query” => array(

“relation” => “OR”,

array( “meta_key” => “color”, “meta_value” => “azul” ),

array( “meta_key” => “precio”,

“meta_value” => array(20, 200),

“compare” => “BETWEEN”,

“type” => numeric ) )

));

WP_Query//obtenemos el SQL de la consulta $sql = $wp_query->request;

//obtenemos los posts de resultados de la consulta $posts = $wp_query->get_posts();

WPDB$posts = $wpdb->get_results(

“ SELECT * FROM $wpdb->posts

WHERE post_status = ‘publish'" );

WPDB$wpdb->prepare( "SELECT * FROM table WHERE ID = %d", $id );

$wpdb->update( $table, $data_array, $where_array);

$wpdb->delete( $table, $where_array );

Templates personalizadasadd_filter( 'template_include', function ( $template ) {

if ( is_page( get_option(“landing_page_id”) ) ) {

$template = __DIR__ . “/../Templates/landing.php“;

}

return $template;

}

function replaceQuery(WP_Query $wp_query){

if( get_option(“landing_page_id”) ){

//cambiamos el loop de la portada

$wp_query = new WP_Query(array( “category” => 1 ));

//evitamos un loop infinito

remove_action(“replaceQuery”);

}

};

add_action(“pre_get_posts”, “replaceQuery”);

Ajax

$.getJSON( ajaxurl,

{ action: “controller.action” },

function( data ){

});

Ajax: En el Backendadd_action('wp_head',function() {

?>

<script type="text/javascript">

var ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';

</script>

<?php

});

add_action( ‘wp_ajax_controller.action’, “callback”);

add_action( 'wp_ajax_nopriv_controller.action’, “callback”);

Herramientas• Gestión de librerías: composer

• Inyección de dependencias: Symfony o Pimple

• Configuración: Yaml y Configuration Component

• Assets: Assetic

• Templates: Twig

ComposerWordPress no ofrece gestión de librerías ni componentes por Composer

Por suerte existe el proyecto wpackagist.org

Demo

Inyección de dependenciasNos permite hacer más portable y desacoplado nuestro código.

• Pimple

• Symfony Dependency Injection

Inyección de dependencias$container[“wp_query”] = function ($c) use ($wp_query) {

return $wp_query;

};

$container[“blog.repository”] = function ($c) {

return new WPBlogRepository($c[“wp_query”]);

};

$container[“blog.model”] = function(){

return new BlogModel( $c[“blog.repository”] );

}

Modelo Portableclass BlogModel {

private $_repository;

function __construct(RepositoryInterface $adapter){

$this->_repository = $adapter;

}

function getPosts($page, $limit=20){

return $this->_repository->getPosts($page, $limit);

}

}

Assets

• Wordpress nos permite registrar y encolar scripts (y css) gestionando sus dependencias

• Al llamar al wp_head o wp_footer, los scripts y hojas de estilos se añaden de forma automática al html de la página

Assets wp_enqueue_script( ‘bootstrap',

get_template_directory_uri() . '/assets/js/bootstrap.min.js',

array('jquery'),

'3.0.0',

$in_footer = true

);

Assetic• Assetic nos permite gestionar de forma más

avanzada los assets, uniéndolos y optimizándolos para servirlos en la página.

• Mediante el plugin WPAssetic podemos disponer de esta utilidad en nuestro sitio

WP CronAl activar un plugin o theme, nos permite disparar eventos con una periodicidad concreta

register_activation_hook( __FILE__, function() {

wp_schedule_event( time(), 'hourly', ‘evento_cada_hora’ );

});

add_action( ‘evento_cada_hora', function(){

});

Rendimiento con Varnish• Varnish es un servicio de proxy-cache que permite

servir el resultado del html desde memoria

• Podemos actualizar e invalidar la caché de nuestro contenido de forma automática mediante el plugin wp varnish

Pluggable• WordPress permite sobreescribir algunas

funciones internas desde nuestros plugins.

• Dichas funciones se denominan pluggables

Pluggable Emailsif ( !function_exists('wp_new_user_notification') ) { function wp_new_user_notification( $user_id, $plaintext_pass = '' ) {

wp_email( “[email protected]”, “asunto”, “cuerpo del mensaje” );

}

}

CachéWordpress tiene una cache clave-valor mediante un conjunto de funciones llamadas transients

Utilizan de forma interna el objeto wp_object_cache

set_transient( ‘datos_cacheados',

$datos,

12 * HOUR_IN_SECONDS );

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

Gracias!

¿Preguntas?

Asier Marqués @asiermarques [email protected]