Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Experiences in migrating a Drupal 7 module to Drupal 8

Experiences in migrating a Drupal 7 module to Drupal 8

https://seville2017.drupaldays.org/sessions/experiences-migrating-drupal-7-module-drupal-8

In this session, I will describe my experiences in updating a Drupal module from Drupal 7 to Drupal 8 (the adsense module).

The session will describe the lessons learned in this process, including the removal of the previous "sub-module as plugins" system, and it's replacement with a true plugin system based on the infrastructure provided by Drupal 8. It will also advocate for a thorough removal of the .module file as much as possible.

Some tools helpful for module development will also be addressed including:

Composer - to manage external dependencies.
Drupal Module Upgrader - for automatic update of code from D7 to D8.
Coder - static checks for coding standards.

Main points to discuss on the subject of module development for Drupal 8:
module.info.yml and the other YAML files
Object Oriented Programming
PHP's PSR-4 and the module file structure
PHP Annotations
The Drupal 8 plugin system
Forms
Blocks
Twig templates
Page controllers and the routing system

João Ventura

March 22, 2017
Tweet

More Decks by João Ventura

Other Decks in Programming

Transcript

  1. E x p e r i e n c e

    s i n m i g r a t i n g a D r u p a l 7 m o d u l e t o D r u p a l 8 João Ventura @jcnventura
  2. J o ã o V e n t u r

    a @ j c n v e n t u r a Senior Developer at Wunder (Germany). Part of Drupal Portugal community (sometimes). Drupal user since Drupal 4.6. Maintainer of the print and adsense modules. 1 patch in D7 (moved drupal_eval to php_eval). 22 patches in D8.
  3. D r u p a l 5 → 6 https://www.drupal.org/update/modules/5/6

    82 API changes! CCK (Content types) Languages / Translations Menu Schema API
  4. D r u p a l 6 → 7 https://www.drupal.org/update/modules/6/7

    264 API changes! hook_nodeapi -> hook_node_xxx (same for + hook_block) CCK (Fields in core) Render API (and render arrays…) New database API
  5. Taken from “Pirates of the Caribbean: At World's End” under

    the fair-use rule. (C) 2007 Walt Disney Pictures / Jerry Bruckheimer Films.
  6. D r u p a l 7 → 8 https://www.drupal.org/update/modules/7/8

    ??? API changes Symfony + Twig OOP Plugins Config
  7. P e r s o n a l e x

    p e r i e n c e
  8. p r i n t + a d s e

    n s e Maintainer of these modules since Drupal 5 Used at first in personal site (scratch my own itch) Allowed me to contribute to Drupal on my own free time Porting from Drupal 5 to 6: easy, able to follow core Porting from Drupal 6 to 7: hard, had to adapt multiple times to changing API.
  9. L e s s o n s l e a

    r n e d p r e - D 8 Wait for beta. Don’t chase core. Leverage the improved system. Don’t just upgrade it. Coder upgrade provided some help. But in retrospective, pretty easy to upgrade a module.
  10. L e t ’ s g e t Te c

    h n i c a l From “Ada Lovelace: The First Computer Programmer” http://iq.intel.com/ada-lovelace-the-first-computer-programmer/
  11. . i n f o → . i n f

    o . y m l adsense.info name = AdSense core description = (…) package = Adsense core = 7.x configure = admin/config/services/ adsense adsense.info.yml name: 'AdSense' type: module description: (…) package: Adsense core: 8.x configure: adsense.main_settings
  12. C o m p o s e r f i

    l e { "name": "drupal/adsense", "description": "Displays Google AdSense ads on your site to earn revenue.", "type": "drupal-module", "homepage": "https://drupal.org/project/adsense", "authors": [ { "name": "João Ventura", "homepage": "https://www.drupal.org/u/jcnventura" } ], "support": { "issues": "https://www.drupal.org/project/issues/adsense" }, "license": "GPL-2.0+" }
  13. V a r i a b l e s adsense.module

    define('ADSENSE_BASIC_ID_DEFAULT', ‘’) adsense.admin.inc variable_get('adsense_basic_id', ADSENSE_BASIC_ID_DEFAULT) adsense.install variable_del(‘adsense_basic_id’)
 config/install/adsense.settings.yml adsense_basic_id: ‘’ src/Form/AdsenseIdSettings.php $config = \Drupal::config('adsense.settings'); $config->get(‘adsense_basic_id’)
  14. U p g r a d e p a t

    h migration_templates/d7_adsense_settings.yml id: d7_adsense_settings label: AdSense 7 configuration migration_tags: - Drupal 7 source: plugin: variable variables: - adsense_basic_id process: adsense_basic_id: adsense_basic_id destination: plugin: config config_name: adsense.settings
  15. M e n u r o u t i n

    g adsense.module function adsense_menu() { $items = array(); $items['admin/settings/adsense'] = array( 'title' => 'AdSense', 'description' => ‘…’, 'page callback' => 'drupal_get_form', 'page arguments' => array('adsense_main_settings'), 'access arguments' => array('administer adsense'), 'file' => 'adsense.admin.inc', );
 adsense.routing.yml adsense.main_settings: path: /admin/config/services/adsense defaults: _title: AdSense _form: \Drupal\adsense\Form\AdsenseMainSettin gs requirements: _permission: 'administer adsense'
  16. C o n f i g u r a t

    i o n f o r m adsense.admin.inc function adsense_id_settings() { $form['adsense_basic_id'] = array( '#type' => 'textfield', '#title' => t(…), '#required' => TRUE, '#default_value' => variable_get(), '#description' => t(…), ); $form['#validate'][] = '_adsense_id_settings_validate'; return system_settings_form($form); } src/Form/AdsenseIdSettings.php class AdsenseIdSettings extends ConfigFormBase { public function getFormId() { return 'adsense_id_settings'; } protected function getEditableConfigNames() { return ['adsense.settings']; } public function buildForm(array $form, FormStateInterface $form_state) { $form['adsense_basic_id'] = [ '#type' => 'textfield', '#title' => t(…), '#required' => TRUE, '#default_value' => $config->get(…), '#pattern' => 'pub-[0-9]+', '#description' => t(…), ]; return parent::buildForm($form, $form_state); }
  17. Tw i g t e m p l a t

    e templates/adsense-managed-async.html.twig <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></ script> <!-- {{ format }} --> <ins class="adsbygoogle" style="display:inline-block;width:{{ width }}px;height:{{ height }}px" data-ad-client="ca-{{ client }}" data-ad-slot="{{ slot }}"></ins> <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script>
  18. B l o c k s adsense_managed.module function adsense_managed_block_info() function

    adsense_managed_block_configure($delta = ‘’) function adsense_managed_block_save($delta = ‘', $edit = array()) function adsense_managed_block_view($delta = ‘')
 src/Plugin/Block/ManagedAdBlock.php /** * Provides an AdSense managed ad block. * * @Block( * id = "adsense_managed_ad_block", * admin_label = @Translation("Managed ad"), * category = @Translation("Adsense") * ) */ class ManagedAdBlock extends BlockBase implements AdBlockInterface { public function defaultConfiguration() public function buildConfigurationForm(array $form, FormStateInterface $form_state) public function blockSubmit($form, FormStateInterface $form_state) public function build() }
  19. A n n o t a t i o n

    s / P l u g i n s Drupal 7: plugin system via submodules and hook_adsense_* API Drupal 8: real plugin system, based on Annotations src/Annotation/AdsenseAd.php /** * Defines an adsense ad item annotation object. * Plugin Namespace: Plugin\adsense\AdsenseAd. * @Annotation */ class AdsenseAd extends Plugin { public $id; public $name; public $isSearch; public $needsSlot; }
  20. A n n o t a t i o n

    s / P l u g i n s src/Plugin/AdsenseAd/ManagedAd.php /** * Provides an AdSense managed ad unit. * @AdsenseAd( * id = "managed", * name = @Translation("Content ads"), * isSearch = FALSE, * needsSlot = TRUE * ) */ class ManagedAd extends ContentAdBase {
  21. P S R - 4 “describes a specification for autoloading

    classes from file paths. (…) This PSR also describes where to place files that will be autoloaded according to the specification” Class namespace + name: namespace Drupal\adsense\Plugin\Block class ManagedAdBlock extends BlockBase implements AdBlockInterface { Class filename src/Plugin/Block/ManagedAdBlock.php
  22. D r u p a l M o d u

    l e U p g r a d e r Handles the boring parts Converts module.info to module.info.yml Converts your hook_menu() to module.routing.yml Converts the configuration forms to src/Form/FormName.php etc. NOT a magic wand. You still have to port your functionality to work with D8 https://www.drupal.org/project/drupalmoduleupgrader
  23. C o d e r S n i f f

    e r drupalcs: signals all code standards violations (coder sniffer). drupalcbf: automatically fixes everything it can (coder beautifier). https://www.drupal.org/project/coder Installing Coder Sniffer: https://www.drupal.org/node/1419988
  24. – Murphy “ W H A T C O U

    L D G O W R O N G ? ” L I V E D E M O
  25. L e s s o n s l e a

    r n e d D 8 Easy path Drupal module upgrader. Keep code in .module files. Make it work. Hard path Object-oriented-programming (Single responsibility principle, Plugins, etc.). No code other than hook_something() in .module file. Rewrite from scratch.
  26. • João Ventura • d.o: jcnventura • Phone: +49.89.85636307 •

    [email protected] • Wunder Germany • [email protected] • www.wunder.io/de • Agnes-Pockels-Bogen 1, D1.019; 80992 München A n y q u e s t i o n s ? ?