26

Drupal 8 module development

Embed Size (px)

DESCRIPTION

Dependency injection, Namespaces, Plugins. Drupal 8 has got all of it. Fortunately, it is more developer friendly than before, but unfortunately, it raises the bar. How do you know when to create a service? How to port your module to Drupal 8? Should you write this piece of code as a plugin? Surely, you can find answers by googling. Drupal has excellent documentation. The hard part is, you will end up spending a lot of time finding out how to do things the "Drupal 8" way, and little time writing code.This book is aimed at cutting that time. Most of the content is the notes I took while porting modules to Drupal 8 and studying the code of already ported modules.Drupal 8 is a moving target. I keep updating the book and the code as frequently as I can to keep it working with the latest beta release. If you find anything inconsistent, let me know and I'll update it. You have been warned, this is a WIP book.The target audience is Drupal module developers. It should be OK even if you are entirely new to Drupal but have PHP experience.

Citation preview

Page 1: Drupal 8 module development
Page 2: 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

Page 3: Drupal 8 module development

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

Page 4: Drupal 8 module development

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

Page 5: Drupal 8 module development

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

Page 6: Drupal 8 module development

CONTENTS

routing.yml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16libraries.yml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16settings.yml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

Page 7: Drupal 8 module development

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/

Page 8: Drupal 8 module development

Introduction

Page 9: Drupal 8 module development

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

Page 10: Drupal 8 module development

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.

Page 11: Drupal 8 module development

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

Page 12: Drupal 8 module development

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.

Page 13: Drupal 8 module development

Configuration managementVariables RIP

What parts of your site are configurable?

Example configuration

Page 14: Drupal 8 module development

Routing and ControllersRouting

Dynamic routes

Controllers

AJAX using routing

Page 15: Drupal 8 module development

Designing formsThe forms class hierarchy

Creating custom forms

Page 16: Drupal 8 module development

Writing pluginsWhen should you write a plugin?

What constitutes a plugin

The plugin annotation system

Plugins used in core

Example plugin

Page 17: Drupal 8 module development

EntitiesCreating custom entites

Entity CRUD hooks

Storing entity information in annotation

Entities used in core

Example entity

Page 18: Drupal 8 module development

Modelling data with field APISpecifying the field type

Field formatter plugin

Creating a field widget

Example field

Page 19: Drupal 8 module development

Building viewsData integration: exposing your module to views

Data handlers: field, filter and argument viewshandlers

Other views plugins

Page 20: Drupal 8 module development

RESTify your dataWhy REST?

core and contrib modules related to REST

Headless Drupal

Page 21: Drupal 8 module development

TestingPHPUnit

Simpletest

Example code

Page 22: Drupal 8 module development

Exploring Drupal 8’s multilingualcapabilitiesCore modules related to language and translation

Creating multilingual content

Using translation APIs

Config and Content translation

Page 23: Drupal 8 module development

Appendix A: Using the migratemoduleHowmigration is different in Drupal 8

Example migration

Page 24: Drupal 8 module development

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:

Page 25: Drupal 8 module development

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

Page 26: Drupal 8 module development

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