Вы находитесь на странице: 1из 26

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 Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you 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 this
hashtag on Twitter:
https://twitter.com/search?q=#drupal8book

Contents
Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Cover image attribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

i
i

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

ii

Basic concepts . . . . . . . . .
Drupal 8 directory structure
Classes and OOP . . . . . .
Namespaces . . . . . . . . .
Using Drupalconsole . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

1
1
1
1
1

Service Containers and Dependency Injection


What is a service? . . . . . . . . . . . . . . .
Example service . . . . . . . . . . . . . . . .
Tagged services . . . . . . . . . . . . . . . .
Services used in core . . . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

3
3
3
3
3

Configuration management . . . . . . . .
Variables RIP . . . . . . . . . . . . . . .
What parts of your site are configurable?
Example configuration . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

5
5
5
5

Routing and Controllers


Routing . . . . . . . .
Dynamic routes . . . .
Controllers . . . . . . .
AJAX using routing . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

6
6
6
6
6

Designing forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
The forms class hierarchy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Creating custom forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7
7
7

Writing plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
When should you write a plugin? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
What constitutes a plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

8
8
8

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

CONTENTS

The plugin annotation system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


Plugins used in core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Example plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

8
8
8

Entities . . . . . . . . . . . . . . . . . . . .
Creating custom entites . . . . . . . . . .
Entity CRUD hooks . . . . . . . . . . . .
Storing entity information in annotation
Entities used in core . . . . . . . . . . . .
Example entity . . . . . . . . . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

9
9
9
9
9
9

Modelling data with field API


Specifying the field type . .
Field formatter plugin . . . .
Creating a field widget . . .
Example field . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

10
10
10
10
10

Building views . . . . . . . . . . . . . . . . . . . . . . . .
Data integration: exposing your module to views . . . .
Data handlers: field, filter and argument views handlers
Other views plugins . . . . . . . . . . . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

11
11
11
11

RESTify your data . . . . . . . . . . . . . .


Why REST? . . . . . . . . . . . . . . . .
core and contrib modules related to REST
Headless Drupal . . . . . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

12
12
12
12

Testing . . . . .
PHPUnit . . .
Simpletest . .
Example code

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

13
13
13
13

Exploring Drupal 8s multilingual capabilities . . .


Core modules related to language and translation .
Creating multilingual content . . . . . . . . . . .
Using translation APIs . . . . . . . . . . . . . . .
Config and Content translation . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

14
14
14
14
14

Appendix A: Using the migrate module . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


How migration is different in Drupal 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Example migration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

15
15
15

Appendix B: List of YAML files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


services.yml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
info.yml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

16
16
16

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

CONTENTS

routing.yml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
libraries.yml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
settings.yml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

16
16
18

Acknowledgments
Cover 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 concepts
Drupal 8 directory structure
Classes and OOP
Namespaces
Using Drupalconsole
Though Drupal 8 is technically advanced compared to its predecessor, writing a module involves a
lot of boilerplate code. There w many gotcha moments when you will forget to add a namespace
and 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 it
leverages 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 something
like:

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

1
2
3
4
5
6
7
8
9

d8$ drupal list


Drupal version Drupal Console 0.5.2 - prod
Usage:
[options] command [arguments]
...
router
router:debug
router:rebuild

Displays current routes for an application


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 current
configuration and routes.

Service Containers and Dependency


Injection
What is a service?
A service is a fancy name for a reusable PHP object in your code. A flag service in flag module can
be used to manage flags across all entities. An Alias uniquifier service(in pathauto module) is used
to create a unique path alias.
A service container is a PHP object that dictates how your service object is constructed. Drupal 8s
service container is built on top of Symfony 2 service container.
Why do we need a service container? Why cant we instantiate the service opject directly whenever
we 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) before
every request. A popular method of service instantiation by service container is via dependency
injection.
If you want to write a service for your module, you will have to create a <module_name>.services.yml
file and the service class.
Heres 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 really
well, 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 most
notorious example of global variable usage was the $user case.
In Drupal 7, one could write

Service Containers and Dependency Injection

1
2
3
4

global $user;
if ($user->uid == 0) {
//user is not logged in
}

The Drupal 8 way would be:


1
2
3

if (\Drupal::currentUser()->isAuthenticated() == 0) {
//user is not logged in
}

Why globals are evil?


Global variables are a very clumsy way to write code, not just in Drupal but anywhere in
general. They can be mutated accidentally and these mutations are hard to track down.

Configuration management
Variables RIP
What parts of your site are configurable?
Example configuration

Routing and Controllers


Routing
Dynamic routes
Controllers
AJAX using routing

Designing forms
The forms class hierarchy
Creating custom forms

Writing plugins
When should you write a plugin?
What constitutes a plugin
The plugin annotation system
Plugins used in core
Example plugin

Entities
Creating custom entites
Entity CRUD hooks
Storing entity information in annotation
Entities used in core
Example entity

Modelling data with field API


Specifying the field type
Field formatter plugin
Creating a field widget
Example field

Building views
Data integration: exposing your module to views
Data handlers: field, filter and argument views
handlers
Other views plugins

RESTify your data


Why REST?
core and contrib modules related to REST
Headless Drupal

Testing
PHPUnit
Simpletest
Example code

Exploring Drupal 8s multilingual


capabilities
Core modules related to language and translation
Creating multilingual content
Using translation APIs
Config and Content translation

Appendix A: Using the migrate


module
How migration is different in Drupal 8
Example migration

Appendix B: List of YAML files


services.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 party
Javascript and/or CSS assets in it. Previously, this used to be a clumsy hook_library_info() array
but is replaced by a YML file in D8. It makes asset management look more organized and easier to
edit. Lets see how to do this for the colorbox module.
The new YML file will have the naming convention modulename.libraries.yml. So, ours will be
colorbox.libraries.yml and will reside in the top level of the module directory like all other YML
files.
Each entry has a unique name and a set of properties.
1
2
3
4
5
6
7

colorbox:
version: VERSION
js:
js/colorbox.js: {}
dependencies:
- core/jquery
- core/drupal

Here, colorbox is the name of the asset bundle to be included. This identifier will be used to refer
the 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 means
the jquery asset present in core.libraries.yml.
Heres how the jquery entry looks in core.libraries.yml:

Appendix B: List of YAML files

1
2
3
4
5
6
7
8
9

17

jquery:
remote: https://github.com/jquery/jquery
version: 2.1.0
license:
name: MIT
url: https://github.com/jquery/jquery/blob/2.1.0/MIT-LICENSE.txt
gpl-compatible: true
js:
assets/vendor/jquery/jquery.js: { weight: -20 }

Another example entry from core.libraries.yml:


1
2
3
4
5
6
7
8
9
10
11

classList:
remote: https://github.com/eligrey/classList.js
# @todo Stable release required for Drupal 8.0.
version: master
license:
name: Public Domain
url: https://github.com/eligrey/classList.js/blob/master/LICENSE.md
gpl-compatible: true
js:
assets/vendor/classList/classList.min.js: { weight: -21, browsers: { IE: 'lt\
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 Drupal
setting.
Colorbox itself listed as a dependency in colorbox.libraries.yml for a theme variant:
1
2
3
4
5
6
7
8
9
10
11

stockholmsyndrome:
version: VERSION
js:
styles/stockholmsyndrome/colorbox_style.js: {}
css:
theme:
styles/stockholmsyndrome/colorbox_style.css: {}
dependencies:
- colorbox/colorbox
- core/jquery
- 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
2
3

function colorbox_page_attachments(&$page) {
...
$page['#attached']['library'][] = 'colorbox/colorbox';

Configurables can be passed from Drupal/PHP domain to js using the drupalSettings key.
1
2
3
4
5
6
7
8
9

$js_settings = array(
'opacity' => '0.85',
'current' => t('{current} of {total}'),
'previous' => t(' Prev'),
'next' => t('Next '),
'close' => t('Close'),
'maxWidth' => '98%',
);
$page['#attached']['drupalSettings']['colorbox'] = $js_settings;

Much of libraries.yml functionality overlaps with the hook_libraries_info of libraries module. The
same thing can be accomplished by implementing the libraries_info hook, downloading colorbox
jquery 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 2
advantages:
The same asset can be used by more than 1 module. For example, colorbox jquery library can
be 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

Вам также может понравиться