Portando módulo tablesort de D7 a D8

16/12/2014
Portando módulo tablesort de D7 a D8

La entrada de Drupal8 con nuevos paradigmas de desarrollo ha acercado a muchos profesionales al mundo Drupal y alejados a otros que han visto un giro demasiado drástico en el futuro de Drupal. Yo pertenezco a ese grupo que esperaba con ansias el aire fresco que esta versión parece que va a aportar.  Por esta razón, comencé a adentrarme en Drupal8, esperando encontrarme una forma de trabajar cercana a la orientación de objetos y de esa forma encontré la excusa perfecta para comenzar a contribuir con la comunidad.

Por todo esto, en este post voy a hacer un repaso de cómo porté el módulo tablesort de D7 a D8 y cómo lo subí a drupal.org. Espero que sea útil para todas aquellas personas que se encuentran montañas de información y no saben ni por donde comenzar.

Cuando abres el portatil dices.... muy bien, que ganas tengo ¿y ahora que? ¿Por donde empiezo? Es uno de los momentos más críticos porque cuando hay tanta información al final lo que uno necesita es ese pequeño empujoncito para comenzar a andar.

Pasos Previos

Lo primero Instalar Drupal 8. Para ello es importante que tengamos git instalado.

  git clone --branch 8.0.x http://git.drupal.org/project/drupal.git

Con esto ya tenemos descargado el código de Drupal 8 y lo que es más importante lo podremos ir actualizando conforme vayan produciéndose cambios en la rama de desarrollo.

Ahora habría que realizar la instalación de Drupal 8. Más info aqui:

https://www.drupal.org/documentation/install

En este momento deberías tener un Drupal8 instalado y accesible desde el navegador. Si has tenido problemas en la instalación de Drupal 8, ejecuta drush status en consola para comenzar a analizar el problema.

A continuación me hice con el módulo de examples

  git clone --branch 8.x-1.x http://git.drupal.org/project/examples.git

En mi caso particular cogí el módulo sin modificar, es decir,  nadie había trabajado previamente en él.

https://www.drupal.org/node/2102679

¿Qué ocurriría si tuviese que retomar el trabajo de otra persona que por alguna razón no le dio tiempo de terminarlo? En ese caso tendríamos que coger el último patch que existiese en la issue que estamos intentando resolver y aplicarlo, por ejemplo:

Aplicar patch

Si existiese algún patch previo lo hubiera aplicado:

Así lanzando patch con -p

patch -p0 < cambios.patch

al eliminar 0 directorios utilizará la ruta completa al fichero (es equivalente a no utilizar el -p); en el ejemplo dicha ruta sería

/modules/examples/routing.yml

Utilizando -p1

patch -p1 < cambios.patch

eliminará el primer directorio (/modules) quedando la ruta

/examples/routing.yml

Utilizando -p2

patch -p2 < cambios.patch

eliminaría los dos primeros directorios (/modules/examples/) quedando como ruta única y exclusivamente el nombre del fichero

Aplicando Patch con git-apply

http://git-scm.com/docs/git-apply

Creación rama de trabajo

Con tu drupal 8 instalado, el módulo de examples para D8 bajado, procederíamos a bajarnos el módulo de examples para D7 que nos sirva de base para la construcción del nuevo módulo.

https://www.drupal.org/project/examples

Todo lo que hemos hecho anteriormente es prepararnos para poder comenzar a realizar el port pero aún no hemos tocado ni una línea de código :). No desesperes, ya queda menos.

Nos creamos con git una rama de trabajo. Es bueno que coincida con la issue de drupal XXXX seguido del número de comentario _YY ya que de esta forma podrás tener un control con posterioridad de lo que estás haciendo solo con ver el nombre de la rama.

De esta forma si la issue es la 2102679 y el comentario 14, la rama se crearía así:

 

git branch -B 2102679_14

Ahora comenzamos a tocar código :)

Rutas de menú

Lo primero, o al menos lo que yo hago, es colocar bien las rutas de menú.

En D7 en el fichero tablesort_example.module encontramos esto


/** 
 * Implements hook_menu(). 
 */ 
function tablesort_example_menu() { 
    $items['examples/tablesort_example'] = array( 
    'title' => 'TableSort example', 
    'description' => 'Show a page with a sortable table', 
    'page callback' => 'tablesort_example_page', 'access callback' => TRUE, 
  ); 
  return $items; 
}

¿Cómo lo pasamos a D8?

En D8 las rutas de menú se introducen en el fichero xxx.routing.yml

tablesort_example_description: 
path: 'examples/tablesort_example' 
defaults: 
_controller: '\Drupal\tablesort_example\Controller\TableSortExampleController::description' 
requirements: _access: 'TRUE

Controller

En d7 el page_callback lo encontramos en el mismo .module

¿Y ahora que hago? ¿Donde y cómo le meto mano al controlador?

Lo primero es crearte en esta ruta el controlador

src/Controller/TableSortExampleController.php

La explicación es muy sencilla. Para tenerlo todo bien estructurado, el controller se mete dentro de la carpeta src/Controller donde podremos organizar los controladores como mejor consideremos. El nombre del controlador va a ser el mismo que definamos dentro en la clase correspondiente. Mira como sería:

<?php 
/** 
* @file
* Contains \Drupal\page_example\Controller\TableSortExampleController. 
*/ 
namespace Drupal\tablesort_example\Controller; 
use Drupal\Core\Controller\ControllerBase; 
use Drupal\Core\Database\Query; 
/** 
* Controller routines for tablesort example routes. 
*/ 
class TableSortExampleController extends ControllerBase {

Nuestro controlador extiende de una clase base ControllerBase que dotará de los métodos necesarios a nuestro controlador, pero no nos liemos. ¿Cómo le damos funcionalidad?

¿Recuerdas que en routing.yml pusimos esta línea?

controller: 
'\Drupal\tablesort_example\Controller\TableSortExampleController::description'

Eso es porque en el controlador tenemos esto:

public function description() {

Es decir el método público description lo estamos invocando en el routing y lo estamos definiendo en el controlador.

De esta forma cuando en el navegador ponemos esto examples/tablesort_example

entrará en el routing, localizará el path, automáticamente buscará el controller (en el routing.yml) y  con la ruta definida se dirige al controlador, encuentra el description y lo ejecuta.

Escribiendo funcionalidad

Pues ahora vamos a darle inteligencia a esto que de momento es todo muy mecánico ¿no? :)

En el módulo de D7 tengo esto:

// Using the TableSort Extender is what tells the the query object that we
// are sorting.
$query = db_select('tablesort_example', 't')
->extend('TableSort');
$query->fields('t');
// Don't forget to tell the query object how to find the header information.
$result = $query
->orderByHeader($header)
->execute();

En el módulo de D8 tengo esto:

// Using the TableSort Extender is what tells the query object that we 
// are sorting. 
$query = db_select('tablesort_example', 't') 
->extend('Drupal\Core\Database\Query\TableSortExtender'); 
$query->fields('t'); 
// Don't forget to tell the query object how to find the header information. 
$result = $query ->orderByHeader($header) 
->execute();

¿No has visto la diferencia? Está aquí:

D7: ->extend('TableSort');

D8: ->extend('Drupal\Core\Database\Query\TableSortExtender');

El resto del controlador es parecido:

D8:

$rows = array();
foreach ($result as $row) {
// Normally we would add some nice formatting to our rows
// but for our purpose we are simply going to add our row
// to the array.
$rows[] = array('data' => (array) $row);
}
// Build the table for the nice output.
$build = array(
'#markup' => '<p>' . t('The layout here is a themed as a table
that is sortable by clicking the header name.') . '</p>',
);
$build['tablesort_table'] = array(
'#theme' => 'table',
'#header' => $header,
'#rows' => $rows,
);
return $build;
}

Finalizando

Verás que en un  99% el contenido es igual en D7 y D8. Es decir para adaptar el .module en el callback de D7 a D8 lo que hemos hecho es crear un controlador, colocar el mismo módulo y colocar bien el EXTEND. ¿Cómo llegué a la conclusión de que el extend había que cambiarlo? Eso habría que dejarlo para otros posts, pero fueron muchas horas de pruebas, investigación y “desesperación” :))

No voy a comentar los tests, que eso fue otro viacrucis hasta que llegó a ejecutarse correctamente y quizás en otro posts haga referencia a ellos.

Finalmente despues creé el patch y el diff para subirlo a drupal.org

git format-patch 8.x-1.x --stdout > 2102659_14.patch

y por último generamos el interdiff

git diff 8.x-1.x > interdiff-2102679-14.txt

Espero que haya servido para perder el miedo a portar módulos de D7 a D8 y contribuir a la comunidad.