Download - Drupal 8 module development
Drupal 8 module development
Lakshmi Narasimhan
This book is for sale at http://leanpub.com/drupal8book
This version was published on 2015-01-29
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishingprocess. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools andmany iterations to get reader feedback, pivot until you have the right book and build traction onceyou do.
This work is licensed under a Creative Commons Attribution 3.0 Unported License
Tweet This Book!Please help Lakshmi Narasimhan by spreading the word about this book on Twitter!
The suggested tweet for this book is:
I just bought Drupal 8 module development by @lakshminp
The suggested hashtag for this book is #drupal8book.
Find out what other people are saying about the book by clicking on this link to search for thishashtag on Twitter:
https://twitter.com/search?q=#drupal8book
Contents
Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iCover image attribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ii
Basic concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1Drupal 8 directory structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1Classes and OOP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1Using Drupalconsole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Service Containers and Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . . 3What is a service? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3Example service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3Tagged services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3Services used in core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Configuration management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5Variables RIP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5What parts of your site are configurable? . . . . . . . . . . . . . . . . . . . . . . . . . . . 5Example configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Routing and Controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6Routing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6Dynamic routes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6Controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6AJAX using routing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Designing forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7The forms class hierarchy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7Creating custom forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Writing plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8When should you write a plugin? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8What constitutes a plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
CONTENTS
The plugin annotation system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8Plugins used in core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8Example plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9Creating custom entites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9Entity CRUD hooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9Storing entity information in annotation . . . . . . . . . . . . . . . . . . . . . . . . . . . 9Entities used in core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9Example entity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Modelling data with field API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10Specifying the field type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10Field formatter plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10Creating a field widget . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10Example field . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Building views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11Data integration: exposing your module to views . . . . . . . . . . . . . . . . . . . . . . . 11Data handlers: field, filter and argument views handlers . . . . . . . . . . . . . . . . . . . 11Other views plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
RESTify your data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12Why REST? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12core and contrib modules related to REST . . . . . . . . . . . . . . . . . . . . . . . . . . . 12Headless Drupal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13PHPUnit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13Simpletest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13Example code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Exploring Drupal 8’s multilingual capabilities . . . . . . . . . . . . . . . . . . . . . . . . . 14Core modules related to language and translation . . . . . . . . . . . . . . . . . . . . . . . 14Creating multilingual content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14Using translation APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14Config and Content translation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Appendix A: Using the migrate module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15How migration is different in Drupal 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15Example migration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Appendix B: List of YAML files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16services.yml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16info.yml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
CONTENTS
routing.yml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16libraries.yml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16settings.yml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
AcknowledgmentsCover image attribution
“Electric Sphere¹” by spettacolopuro² is licensed under CC BY 2.0³
Drupal is a registered trademark of Dries Buytaert.
¹https://www.flickr.com/photos/spettacolopuro/3842161463²https://www.flickr.com/photos/spettacolopuro/³http://creativecommons.org/licenses/by/2.0/
Introduction
Basic conceptsDrupal 8 directory structure
Classes and OOP
Namespaces
Using Drupalconsole
Though Drupal 8 is technically advanced compared to its predecessor, writing a module involves alot of boilerplate code. There w many gotcha moments when you will forget to add a namespaceand get puzzling errors.
Fortunately, all that code can be generated automatically using a tool called the Drupal Console⁴.Drupal Console is another cool addition to the Proudly Found Elsewhere⁵ school of thought as itleverages the Symfony Console⁶ component to handle the CLI part.
Installing
Note that Drupal Console supports only Drupal 8.0.0-beta4 at the time of writing this.
Get the latest version:
1 $ curl -LSs http://drupalconsole.com/installer | php
Move it to somewhere convenient so that it can be used throughout the system:
1 $ mv console.phar /usr/local/bin/drupal
Go to the drupal root directory of any Drupal 8 beta4 setup and run drupal list to get somethinglike:
⁴http://drupalconsole.com/⁵https://prague2013.drupal.org/session/not-invented-here-proudly-found-elsewhere-drupal-8-story⁶http://symfony.com/doc/current/components/console/index.html
Basic concepts 2
1 d8$ drupal list
2 Drupal version Drupal Console 0.5.2 - prod
3
4 Usage:
5 [options] command [arguments]
6 ...
7 router
8 router:debug Displays current routes for an application
9 router:rebuild Rebuild routes
Usage
Drupal Console currently supports generating PSR-4 compliant code for plugins, controllers,modules, services, entities and forms.It also has basic debugging commands for listing currentconfiguration and routes.
Service Containers and DependencyInjectionWhat is a service?
A service is a fancy name for a reusable PHP object in your code. A flag service in flag module canbe used to manage flags across all entities. An Alias uniquifier service(in pathauto module) is usedto create a unique path alias.
A service container is a PHP object that dictates how your service object is constructed. Drupal 8’sservice container is built on top of Symfony 2 service container.
Why do we need a service container? Why can’t we instantiate the service opject directly wheneverwe need it? For the simple reason that it makes our code tightly coupled and less reusable.
The service container is a global object created by the Kernel(another Symfony 2 component) beforeevery request. A popular method of service instantiation by service container is via dependencyinjection.
If youwant towrite a service for yourmodule, youwill have to create a <module_name>.services.ymlfile and the service class.
Here’s how you can generate scaffolding for your service using Drupal Console:
1 d8$ drupal generate:service
It is always a good practice to write a service which does only one thing and does it reallywell, akin to the UNIX tools philosophy.
Example service
Tagged services
Services used in core
Many services in core are replacements of global variables which existed in Drupal 7. The mostnotorious example of global variable usage was the $user case.
In Drupal 7, one could write
Service Containers and Dependency Injection 4
1 global $user;
2 if ($user->uid == 0) {
3 //user is not logged in
4 }
The Drupal 8 way would be:
1 if (\Drupal::currentUser()->isAuthenticated() == 0) {
2 //user is not logged in
3 }
Why globals are evil?Global variables are a very clumsy way to write code, not just in Drupal but anywhere ingeneral. They can be mutated accidentally and these mutations are hard to track down.
Configuration managementVariables RIP
What parts of your site are configurable?
Example configuration
Routing and ControllersRouting
Dynamic routes
Controllers
AJAX using routing
Designing formsThe forms class hierarchy
Creating custom forms
Writing pluginsWhen should you write a plugin?
What constitutes a plugin
The plugin annotation system
Plugins used in core
Example plugin
EntitiesCreating custom entites
Entity CRUD hooks
Storing entity information in annotation
Entities used in core
Example entity
Modelling data with field APISpecifying the field type
Field formatter plugin
Creating a field widget
Example field
Building viewsData integration: exposing your module to views
Data handlers: field, filter and argument viewshandlers
Other views plugins
RESTify your dataWhy REST?
core and contrib modules related to REST
Headless Drupal
TestingPHPUnit
Simpletest
Example code
Exploring Drupal 8’s multilingualcapabilitiesCore modules related to language and translation
Creating multilingual content
Using translation APIs
Config and Content translation
Appendix A: Using the migratemoduleHowmigration is different in Drupal 8
Example migration
Appendix B: List of YAML filesservices.yml
info.yml
routing.yml
libraries.yml
One of the things you are likely to do if you write a custom module or a theme is include third partyJavascript and/or CSS assets in it. Previously, this used to be a clumsy hook_library_info() arraybut is replaced by a YML file in D8. It makes asset management look more organized and easier toedit. Let’s see how to do this for the colorbox module.
The new YML file will have the naming convention modulename.libraries.yml. So, ours will becolorbox.libraries.yml and will reside in the top level of the module directory like all other YMLfiles.
Each entry has a unique name and a set of properties.
1 colorbox:
2 version: VERSION
3 js:
4 js/colorbox.js: {}
5 dependencies:
6 - core/jquery
7 - core/drupal
Here, colorbox is the name of the asset bundle to be included. This identifier will be used to referthe asset in the module or another yml file. The js property lists all the js files needed for the asset.The {} next to it can be used for specifying metadata like cache, preprocess, minify and weight.
The dependencies are the assets list which colorbox expects to be loaded. The core/jquery meansthe jquery asset present in core.libraries.yml.
Here’s how the jquery entry looks in core.libraries.yml:
Appendix B: List of YAML files 17
1 jquery:
2 remote: https://github.com/jquery/jquery
3 version: 2.1.0
4 license:
5 name: MIT
6 url: https://github.com/jquery/jquery/blob/2.1.0/MIT-LICENSE.txt
7 gpl-compatible: true
8 js:
9 assets/vendor/jquery/jquery.js: { weight: -20 }
Another example entry from core.libraries.yml:
1 classList:
2 remote: https://github.com/eligrey/classList.js
3 # @todo Stable release required for Drupal 8.0.
4 version: master
5 license:
6 name: Public Domain
7 url: https://github.com/eligrey/classList.js/blob/master/LICENSE.md
8 gpl-compatible: true
9 js:
10 assets/vendor/classList/classList.min.js: { weight: -21, browsers: { IE: 'lt\
11 e IE 9', '!IE': false }, minified: true }
Note that it is possible to add comments in YML files(They start with a “#”).
Likewise, the core/drupal dependency needs to be put up if the respective js exposes a Drupalsetting.
Colorbox itself listed as a dependency in colorbox.libraries.yml for a theme variant:
1 stockholmsyndrome:
2 version: VERSION
3 js:
4 styles/stockholmsyndrome/colorbox_style.js: {}
5 css:
6 theme:
7 styles/stockholmsyndrome/colorbox_style.css: {}
8 dependencies:
9 - colorbox/colorbox
10 - core/jquery
11 - core/drupal
Appendix B: List of YAML files 18
Including the assets in a page
The colorbox assets declared above can be included in a page by implementing the hook_page_-
attachments hook.
1 function colorbox_page_attachments(&$page) {
2 ...
3 $page['#attached']['library'][] = 'colorbox/colorbox';
Configurables can be passed from Drupal/PHP domain to js using the drupalSettings key.
1 $js_settings = array(
2 'opacity' => '0.85',
3 'current' => t('{current} of {total}'),
4 'previous' => t(' « Prev'),
5 'next' => t('Next »'),
6 'close' => t('Close'),
7 'maxWidth' => '98%',
8 );
9 $page['#attached']['drupalSettings']['colorbox'] = $js_settings;
Much of libraries.yml functionality overlaps with the hook_libraries_info of libraries module. Thesame thing can be accomplished by implementing the libraries_info hook, downloading colorboxjquery release in the libraries directory and calling:
1 libraries_load('colorbox', $variant);
In Drupal 7, this is the de facto way of handling third party js assets.
Why 2 ways to do the same thing?
The libraries module is also ported to Drupal 8⁷ and can technically do the same thing, but offers 2advantages:
• The same asset can be used by more than 1 module. For example, colorbox jquery library canbe used by both colorbox module and a custom theme.
• Libraries module also facilitates loading of third party assets written in PHP.
settings.yml
⁷https://www.drupal.org/node/1775738