Guía de actualización de Drupal 8 a Drupal 9
30 de junio de 2021
Este artículo está basado en hechos reales.
Introducción
La versión Beta de Drupal 9 está disponible desde Marzo de 2020, actualmente (Junio de 2021), la versión es perfectamente estable y está en la 9.1.9
En Noviembre de 2021 está previsto que la versión 8.x deje de tener soporte por lo que es importante actualizarnos antes de que llegue esa fecha.
Hay varios artículos técnicos en Internet respecto a cómo proceder con este tipo de actualización, uno de los más interesantes puede ser éste: https://drupalize.me/tutorial/upgrade-drupal-9?p=2766
Con todo, los cambios de D8 a D9 no son demasiado drásticos y realizando el proceso de actualización de manera ordenada no debería demorarse más de algunos días de trabajo. Siempre dependerá mucho tanto de los módulos contribuidos que tengamos en nuestro proyecto (cantidad y grado de actualización), y los módulos custom que hayamos desarrollado (cuánto código está deprecado en ellos). También influirá el número de patchs que tengamos sobre nuestros módulos; en este sentido, conviene además revisar dichos parches pues es posible que algunos de ellos ya no sean necesarios, bien porque el módulo contrib ya haya solucionado el problema o bien porque sea un patch del core de Drupal que también esté ya solucionado en la última versión de Drupal 9.
Proceso de actualización
Qué cambia realmente de Drupal 8 a Drupal 9
La mayoría de cambios desde Drupal 8 (última versión) hasta Drupal 9 son menores, hay dos tipos de cambios principales:
Métodos deprecated:
Hay varios métodos que estaban deprecated en la última versión de Drupal 8 y pasan a no estar disponibles en Drupal 9.
La mayoría de estos métodos son servicios que han cambiado de nombre, o funciones que ahora se llaman de otra forma. Especial mención tienen algunos cambios en los ficheros de tipo .install, respecto al uso y acceso a las tablas y base de datos, que ahora se gestiona de otra forma. Desarrollaremos todos los cambios reseñables más adelante en este documento.
Actualización de la información sobre el módulo:
En los ficheros de tipo MODULE.info, debe aparecer ahora una línea adicional que indique que el módulo es compatible con Drupal 9.
Actualizar módulos contribuidos:
Los módulos contribuidos deben cumplir con los dos puntos anteriores: no tener código deprecado para la última versión de D8 y tener actualizado su fichero .info
El proceso para actualizar cada módulo contribuido sería el siguiente, deteniéndonos en el momento que conseguimos llegar a buen término en alguno de los siguientes puntos ejecutándolos por orden:
- Buscar si hay una versión compatible con D9
Buscaremos en drupal.org el módulo en cuestión, por ejemplo, pensemos en el módulo flag. Una vez encontrado nos vamos a su repositorio de código y buscamos la rama más actualizada, en este caso es: https://git.drupalcode.org/project/flag/-/tree/8.x-4.x
Si nos vamos al fichero .info, veremos que existe la línea:
core_version_requirement: ^8.8 || ^9
Por tanto, este módulo, en esa versión, es compatible con Drupal 9.
También podríamos utilizar el enlace: https://dev.acquia.com/drupal9/deprecation_status donde revisar si el módulo que necesitamos está ya disponible para Drupal 9 o no.
Sabiendo esto sólo necesitaremos indicar en composer.json la rama del módulo en cuestión. Como la rama del módulo de ejemplo es 8.x-4.x, podremos indicar en composer “4.x” como versión para este módulo, ya que suele corresponder con la rama obviando la primera parte antes del guión, en este caso, 8.x.
- No hay versión compatible con D9 pero sí un Merge request creado por alguien
Puede ocurrir que cuando busquemos un módulo contribuido con el proceso anterior éste no sea compatible con D9, es decir, no encontremos una rama que soporte esta versión.
En este caso buscaremos en el módulo información en las issues abiertas respecto a compatibilidad con Drupal 9. Por ejemplo, en el módulo Brightcove, que actualmente tiene más de 100 issues abiertas:
https://www.drupal.org/project/issues/brightcove?categories=All
Buscando por Drupal 9 o D9 normalmente encontraremos una o más issues relacionadas con la actualización del módulo.
Hay que tener en cuenta que Drupal tiene una especie de robot que automáticamente marcará aquellos módulos contribuidos que no están actualizados a Drupal 9, por lo cual, en teoría, todos los módulos no contribuidos que no estén correctos deberían tener una issue creada con un nombre parecido a “Drupal 9 compatibility” o “Automated Drupal 9 compatibility fixes”.
Entraremos y revisaremos estas issues y si en alguna de ellas aparece ya un Merge Request realizado, qué pinta tiene este MR, cómo de reciente es, quién es el que lo ha realizado, etc. También podremos revisar cada uno de los MR (si hubiese más de uno) y compararlos, con el fin de intentar entender cuál es el que está más completo.
En el caso que nos ocupa vemos este MR como buen candidato a utilizar: https://git.drupalcode.org/issue/brightcove-3146320/-/tree/3146320-D9-compatibility
Una vez identificado, lo necesitamos incorporar a nuestro fichero composer.json con la siguiente nomenclatura:
<p> {</p><p> "type": "package",</p><p> "package": {</p><p> "name": "drupal/brightcove",</p><p> "version": "dev-3146320-D9-compatibility",</p><p> "type": "drupal-module",</p><p> "source": {</p><p> "url": "https://git.drupalcode.org/issue/brightcove-3146320.git",</p><p> "type": "git",</p><p> "reference": "3146320-D9-compatibility"</p><p> }</p><p> }</p><p> },</p>
Donde indicaremos la URL del repositorio en el que se ha hecho fork del módulo oficial, y la referencia a la rama en concreto, además, le indicaremos un nombre (el que queramos) en el parámetro version para llamar a esta versión más adelante en nuestro composer.json.
En nuestro caso solemos nombrarlos como dev-NOMBREDELARAMA.
Por último, incorporamos este módulo en el listado de composer.json de manera habitual:
<p>"drupal/brightcove": "dev-3146320-D9-compatibility",</p>
- No hay versión compatible ni MR creado
Podría ocurrir también que el módulo contribuido sólo tenga creada la issue, no tenga ningún MR aún y ni siquiera ningún usuario la haya comentado o haya indicado un posible patch para adaptar el módulo a Drupal 9.
En este caso, nos tocará hacer el proceso de actualización de manera similar a como lo haríamos con el módulo custom, con la única diferencia de que tendremos que crear el Merge Request siguiendo las reglas y procedimientos de Drupal y su comunidad.
- Creamos un fork
Lo primero que haremos será crear un fork desde la Issue de compatibilidad con Drupal 9. Cuando accedemos a la página de la issue en cuestión nos aparece un botón de “Create issue fork”; si no estuviese es porque tenemos que registrarnos en drupal.org previamente. Nos sugiere también un nombre por defecto, lo ideal es dejarlo tal como nos sugiera.
- Descargamos y corregimos
Descargamos ese repo en nuestro equipo local, arreglamos los problemas de compatibilidad con Drupal 9 según el punto siguiente de este documento, de cómo actualizar código deprecated.
Podríamos incluso incluir el módulo localmente en nuestro proyecto para testearlo.
Una vez corregido realizaremos commit & push y luego enviaremos el Merge Request al módulo oficial, para que lo incluyan lo antes posible (no esperéis que esta aprobación sea inmediata, sospecho que se empezarán a aprobar este tipo de Merge Request cuando llegue Noviembre de 2021.
- Incluímos nuestra versión en composer
De forma análoga a como hicimos en el punto anterior, ahora podremos incluir nuestro propio fork del módulo en composer.json
Actualizar código deprecated:
Como ya hemos comentado, la mayoría de los errores de código que nos va a lanzar el cambio de D8 a D9 son menores: cambiar una línea por otra, normalmente, cambiar la llamada a un servicio por la llamada a otro similar con otro nombre.
La forma más elegante de detectar estos problemas es situarnos en actualizar nuestro proyecto a la última versión de D8 y utilizar algunos de estos módulos para que nos vayan indicando que errores aparecen, por ejemplo éste: https://github.com/mglaman/drupal-check
Esta librería detectará qué código dejará de funcionar en Drupal 9 y qué sustitución tenemos que hacer para actualizarlo. Los cambios que realicemos en nuestro código funcionarán tanto en la última versión de Drupal 9 hasta la fecha.
Algunos de los ejemplos más habituales de errores que nos pueden aparecer son:
-
Call to deprecated constant NODE_PUBLISHED: Deprecated in drupal:8.?.? and is removed from drupal:9.0.0. Use \Drupal\node\NodeInterface::PUBLISHED instead.
-
Call to deprecated method entityManager() of class Drupal: in drupal:8.0.0 and is removed from drupal:9.0.0. Use \Drupal::entityTypeManager() instead in most cases. If the needed method is not on \Drupal\Core\Entity\EntityTypeManagerInterface, see
the deprecated \Drupal\Core\Entity\EntityManager to find the correct interface or service.
-
Parameter $entityQuery of method Drupal\your_module\EventSubscriber\FeedSubscriber::__construct() has typehint with deprecated class Drupal\Core\Entity\Query\QueryFactory: in drupal:8.3.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Entity\EntityStorageInterface::getQuery() or \Drupal\Core\Entity\EntityStorageInterface::getAggregateQuery() instead.
-
Call to deprecated function db_drop_index(): in drupal:8.0.0 and is removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call dropIndex() on it.
-
Call to deprecated function drupal_set_message(): in drupal:8.5.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::addMessage() instead.
-
Call to deprecated function db_change_field(): in drupal:8.0.0 and is removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call changeField() on it
-
Parameter $alias_manager of method Drupal\your_module\Form\customForm::__construct() has typehint with deprecated interface Drupal\Core\Path\AliasManagerInterface: in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\path_alias\AliasManagerInterface.
-
Parameter $temp_store_factory of method Drupal\your_module\Form\customForm::__construct() has typehint with deprecated class Drupal\user\PrivateTempStoreFactory: in drupal:8.5.0 and is removed from drupal:9.0.0. Use \Drupal\Core\TempStore\PrivateTempStoreFactory instead.
La mayoría de actualizaciones de código consistirán, por tanto, en seguir la recomendación de la propia librería drupal-check, si en alguna de las alertas no entendemos bien qué debemos hacer podremos buscar más información al respecto en la documentación oficial de Drupal (drupal.org) o bien en otros foros relacionados.
Actualizar hosting:
Los requisitos de hosting para Drupal 9 han cambiado ligeramente, lo más destacable al respecto es que ahora la versión mínima de PHP requerida es la 7.3. En este enlace disponemos del resto de requisitos de hosting: https://www.drupal.org/docs/understanding-drupal/how-drupal-9-was-made-and-what-is-included/environment-requirements-of
Podríamos actualizar las características de hosting en nuestra versión Drupal 8 directamente, para estar listos para el cambio a Drupal 9.
Actualizar core de Drupal
Lo último que realizaremos será la actualización del core de Drupal en nuestro proyecto.
Para ello, utilizaremos la opción “--no-update”, que permite a composer establecer las versiones que necesitamos de cada módulo y librería en nuestro proyecto, evitando interdependencias que pueden generar errores. Una forma alternativa sería editar todo nuestro composer.json actualizando todos nuestros módulos y teniendo en cuenta las dependencias de todos ellos.
Actualización del core-dev:
composer require drupal/core-dev:9.*@dev --dev --no-update --update-with-dependencies
Actualización del core-recommended y core-composer-scaffold:
composer require drupal/core-recommended:9.*@dev drupal/core-composer-scaffold:9.*@dev --no-update --update-with-dependencies
Ejecutamos la actualización del resto de módulos:
composer update
Nótese que hemos usado 9.* como versión de Drupal, esto permitirá a composer establecer automáticamente la última versión estable, una vez establecida podremos setearla manualmente en el composer.json, por ejemplo 9.1.9, o usar * para las actualizaciones menores (9.1.*)
Consideraciones a tener en cuenta
-
Ante cualquier duda en el proceso se recomienda revisar uno de los artículos más completos respecto a cómo actualizar de D8 a D9: https://drupalize.me/tutorial/upgrade-drupal-9?p=2766
-
Necesitaremos prestar atención a los módulos contribuidos y las ramas no oficiales que hayamos incluido en nuestro proyecto. En algún momento dichas ramas deberían ser aceptadas por los mantenedores de código de esos módulos y entonces podremos incluir directamente esos módulos sin necesidad de establecer un fork personalizado en nuestro composer.json
-
Respecto a estos módulos contribuidos a los que hemos hecho (u otras personas) un fork para poder incluir la compatibilidad con Drupal 9, también podría pasar que el módulo oficial actualice alguna funcionalidad o corrija algún problema de seguridad o bug crítico, si se da el caso, deberíamos hacer merge desde ese nuevo cambio oficial en el módulo en nuestro fork, así como volver a revisar que el código de ese módulo sigue siendo compatible con Drupal 9.
-
Por último, deberemos prestar atención al código que escribamos a partir de ahora, para no utilizar funciones o métodos y clases incompatibles con Drupal 9.