Programmer's Guide To Drupal Drupal, 2nd Edition

User Manual:

Open the PDF directly: View PDF PDF.
Page Count: 235

DownloadProgrammer's Guide To Drupal Drupal, 2nd Edition
Open PDF In BrowserView PDF
on 8
iti l 7 &
Ed a
d rup
2n rs D

Co

ve

Programmer’s
Guide to

Drupal
PRINCIPLES,
PRACTICES,
AND PITFALLS

Jennifer Hodgdon
www.allitebooks.com

Programmer's Guide to Drupal
If you’re a web programmer, your experiences have taught you certain
lessons—and only some of them apply well to Drupal. Drupal has its own
set of programming principles that require a different approach, and many
programmers make mistakes when relying on skills they’ve used for other
projects. This book will show you which programming techniques you can
use—and which you should avoid—when building web applications with
this popular content management framework.
Updated to cover both Drupal 7 and Drupal 8, the guidelines in this book
demonstrate which programming practices conform to the "Drupal way"
and which don’t. The book also serves as an excellent guide for Drupal 7
programmers looking to make the transition to Drupal 8.
■■

Get an overview of Drupal, including Drupal core and add-on
modules and themes

■■

Learn Drupal’s basic programming principles, such as the ability
to customize behavior and output with hooks

■■

Compare Drupal 7 and Drupal 8 programming methods, APIs,
and concepts

■■

Discover common Drupal programming mistakes—and why
hacking is one of them

■■

Explore specific areas where you can put your programming
skills to work

■■

Learn about the new object-oriented Drupal 8 API, including
plugins and services

excellent, concise
“An
treatise
to fast-track your
Drupal development.”

—Karoly Negyesi

Drupal Core Developer

by the Drupal
“Written
community's
foremost
expert in developer documentation, Programmer's
Guide to Drupal is a
must-have reference
for anyone looking for a
practical, example-driven
way to dive into Drupal 7
or 8 development.

”

—Angela Byron

Drupal Core Committer
and Product Manager

Jennifer Hodgdon is an experienced software developer who has been working
with Drupal since 2007. She is a former leader of the Drupal Documentation Team
and currently acts as core maintainer/committer for Drupal API documentation, in
addition to doing Drupal programming as a freelancer and contributing code and
documentation to the Drupal project.

DRUPAL

US $29.99

Twitter: @oreillymedia
facebook.com/oreilly
CAN $34.99

ISBN: 978-1-491-91146-4

www.allitebooks.com

SECOND EDITION

Programmer’s Guide to Drupal

Jennifer Hodgdon

Boston

www.allitebooks.com

Programmer’s Guide to Drupal
by Jennifer Hodgdon
Copyright © 2016 Poplar ProductivityWare, LLC. All rights reserved.
Printed in the United States of America.
Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.
O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are
also available for most titles (http://safaribooksonline.com). For more information, contact our corporate/
institutional sales department: 800-998-9938 or corporate@oreilly.com.

Editor: Allyson MacDonald
Production Editor: Matthew Hacker
Copyeditor: Eileen Cohen
Proofreader: Jasmine Kwityn
December 2012:
October 2015:

Indexer: WordCo Indexing Services, Inc.
Interior Designer: David Futato
Cover Designer: Ellie Volckhausen

First Edition
Second Edition

Revision History for the Second Edition
2015-10-08:

First Release

See http://oreilly.com/catalog/errata.csp?isbn=9781491911464 for release details.
The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. Programmer’s Guide to Drupal, the
cover image of a French angelfish, and related trade dress are trademarks of O’Reilly Media, Inc.
While the publisher and the author have used good faith efforts to ensure that the information and
instructions contained in this work are accurate, the publisher and the author disclaim all responsibility
for errors or omissions, including without limitation responsibility for damages resulting from the use of
or reliance on this work. Use of the information and instructions contained in this work is at your own
risk. If any code samples or other technology this work contains or describes is subject to open source
licenses or the intellectual property rights of others, it is your responsibility to ensure that your use
thereof complies with such licenses and/or rights.

978-1-4919-1146-4
[LSI]

www.allitebooks.com

Table of Contents

Preface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vii
1. Overview of Drupal. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
What Is Drupal?
Drupal Core
Drupal Add-Ons: Modules, Themes, Distributions, and Translations
The Evolution of Drupal
How Drupal Handles HTTP Requests
HTTP Request Handling in Drupal 7
HTTP Request Handling in Drupal 8
The Drupal Cache
Drupal 7 Cache API
Drupal 8 Cache API
Automatic Class Loading in Drupal

1
2
3
5
7
8
9
10
11
11
12

2. Drupal Programming Principles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Principle: Drupal Is Alterable
Setting Up a Module or Theme
The Basics of Module and Theme Hook Programming
Making Your Module Output Themeable
The Basics of Drupal 8 Plugin Programming
Drupal 8 Services and Dependency Injection
Interacting with the Drupal 8 Event System
Principle: Drupal Separates Content, Configuration, and State Data
Information Storage in Drupal 7
Information Separation in Drupal 8
Configuration API in Drupal 8
State API in Drupal 8
Principle: Drupal Is International
Internationalizing User Interface Text

17
20
23
25
28
35
38
39
40
41
42
47
48
49
iii

www.allitebooks.com

Internationalizing User-Entered Text in Drupal 7
Internationalizing User-Entered Text in Drupal 8
Principle: Drupal Is Accessible and Usable
Principle: Drupal Is Database-Independent
Setting Up Database Tables: Schema API and hook_update_N()
Querying the Database with the Database API
Principle: Drupal Is Secure; User Input Is Insecure
Cleansing and Checking User-Provided Input
Checking Drupal Permissions
Principle: Drupal Code Is Tested and Documented

50
51
52
54
55
58
61
62
64
67

3. Common Drupal Programming Mistakes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Mistake: Programming Too Much
Avoiding Custom Programming with Fielded Data
Defining Theme Regions for Block Placement
Mistake: Overexecuting Code
Mistake: Executing Code on Every Page Load
Mistake: Using an Overly General Hook
Mistake: Saving PHP Code in the Database
Mistake: Working Alone
Participating in Groups and IRC
Reporting Issues and Contributing Code to the Drupal Community
Contributing to the Drupal Community in Other Ways

71
75
77
79
79
80
81
82
83
83
85

4. Drupal Programming Examples. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Registering for URLs and Displaying Content
Registering for a URL in Drupal 7
Altering a URL Registration in Drupal 7
Registering for a URL in Drupal 8
Providing Administrative Links
Altering Routes and Providing Dynamic Routes in Drupal 8
Registering a Block in Drupal 7
Registering a Block in Drupal 8
Creating Render Arrays for Page and Block Output
Render Arrays in Drupal 8
Generating Paged Output
Using the Drupal Form API
Form Arrays, Form State Arrays, and Form State Objects
Basic Form Generation and Processing in Drupal 7
Basic Form Generation and Processing in Drupal 8
Creating Confirmation Forms
Adding Autocomplete to Forms

iv

| Table of Contents

www.allitebooks.com

88
89
91
92
95
98
100
101
102
106
107
109
110
112
114
116
119

Altering Forms
Programming with Ajax in Drupal
Setting Up a Form for Ajax
Wrapper-Based Ajax Callback Functions
Command-Based Ajax Callback Functions in Drupal 7
Command-Based Ajax Callback Functions in Drupal 8
Programming with Entities and Fields
Terminology of Entities and Fields
Defining an Entity Type in Drupal 7
Defining a Content Entity Type in Drupal 8
Defining a Configuration Entity Type in Drupal 8
Querying and Loading Entities in Drupal 8
Defining a Field Type
Programming with Field Widgets
Programming with Field Formatters
Creating Views Module Add-Ons
Views Programming Terminology and Output Construction
Setting Up Your Module for Views in Drupal 7
Providing a New Views Data Source
Adding Handlers to Views
Adding Fields and Relationships to an Existing Views Data Source
Providing a Style or Row Plugin to Views
Providing Default Views
Creating Rules Module Add-Ons in Drupal 7
Providing Custom Actions to Rules
Providing Default Reaction Rules and Components
Programming with CTools in Drupal 7
Implementing CTools Plugins for Panels
Providing Default CTools Exportables

121
123
124
127
127
128
129
129
132
139
149
160
161
164
168
171
172
174
175
177
179
180
183
184
186
188
189
189
195

5. Programming Tools and Tips. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
Where to Find More Information
Drupal Site Building and General Drupal Information
Drupal Programming Reference and Background
PHP Resources
Database Resources
Other Web Technology Resources
Drupal Development Tools
Discovering Drupal API Functions and Classes
Other Programming Tips and Suggestions

199
199
200
202
203
203
204
205
207

Index. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
Table of Contents

www.allitebooks.com

|

v

www.allitebooks.com

Preface

Welcome! This book is meant to launch you into the world of programming with the
open source web content management system known as Drupal. My hope is that with
the aid of this book, you will pass smoothly through the stage of being a novice Dru‐
pal programmer, while avoiding the mistakes that many expert Drupal programmers
made in their first Drupal programming endeavors. If you make an effort to learn the
“Drupal way” of programming, you can look forward to many enjoyable and fruitful
years of programming with Drupal.
Probably every experienced Drupal programmer has a slightly dif‐
ferent definition of the Drupal way of programming, although
there are many commonalities. My interpretation of the Drupal
way is set out in Chapters 2 and 3; I’ve tried to stick to the com‐
monalities in the definition that most Drupal programmers would
agree on.

Intended Audience
The first edition of this book was written primarily for people with a background in
programming who are new to using and programming with Drupal. If you fit this
profile, the main reason to read this book is that whatever your programming back‐
ground, your experiences have taught you certain lessons—and only some of them
apply well to Drupal. My main goal in the first edition was to make you aware of
which lessons are which, and help you make a successful transition to being an expert
Drupal programmer: someone who knows just how and where to apply your pro‐
gramming skills to have the greatest effect.
The second edition of this book retains this goal but is also aimed at experienced
Drupal 7 programmers who want to make the transition to Drupal 8. Because both
Drupal 7 and Drupal 8 are included in this book, programmers who fit this profile

vii

www.allitebooks.com

will be able to compare the Drupal 7 programming methods, APIs, and concepts that
they are familiar with to their Drupal 8 equivalents.
In addition, this book should be useful for the following audiences:
• Anyone working with Drupal who wants to understand how it works “under the
hood”
• Drupal site builders and themers who have realized they need to do some pro‐
gramming for customization and want to do it the Drupal way
• Drupal users who want to contribute to the Drupal open source project by
programming
The backend of Drupal and most of its code is written in PHP, utilizing some variety
of SQL for database queries. Accordingly, this book concentrates on PHP and data‐
base programming for Drupal, although there are definitely opportunities to pro‐
gram in Flash, JavaScript, and other frontend languages with Drupal.
Because this book was written for a programming audience, it assumes knowledge of
the following:
• The basics of the Web and HTTP requests
• The basics of PHP programming and programming in general (standard pro‐
gramming terminology is not explained)
• Object-oriented programming in PHP, especially for the Drupal 8 sections
• How to use Drupal core and existing add-on modules to build web sites
See “Where to Find More Information” on page 199 to find resources about these
topics, if you need additional background.

How to Use This Book
In order to get the most out of this book, I would suggest that you start by reading
Chapter 1 and making sure you are familiar with all the material in it. If you have
never installed Drupal at all or tried to use it, you should definitely do that too. There
are installation instructions in the INSTALL.txt file that comes with Drupal (this file
is in the core subdirectory in Drupal 8), or at https://drupal.org/documentation/install.
Then I’d suggest reading Chapters 2 and 3 carefully, to learn about the dos and don’ts
of Drupal programming; there are also many programming examples in those chap‐
ters. If you’ve never before done any Drupal programming or looked at any Drupal
code, there are suggestions at the beginning of Chapter 2 for finding some code to
look at before or while you read those chapters. I would especially recommend down‐
loading Examples for Developers, which is a comprehensive set of programming
viii

|

Preface

www.allitebooks.com

examples covering Drupal core (the base Drupal system, not including add-on mod‐
ules). The Examples project is maintained by many contributors within the Drupal
community, and it is an excellent resource.
After reading about Drupal’s principles and common mistakes, look through Chap‐
ter 4 so you know what’s there, and then come back to individual topics and examples
when you need them. Of course, this book would never be able to cover every possi‐
ble topic in Drupal programming, so if you find you need an example that isn’t cov‐
ered here, try the Examples for Developers project. If it isn’t covered there either,
there are many Drupal programmers who blog, so a web search may be helpful.
Drupal 7 programmers transitioning to Drupal 8 may also find it useful to look up
familiar functions and hooks in the index (or by searching an ebook version). In most
cases, the Drupal 8 version of the programming task will be covered in the same sec‐
tion or the following one.
Finally, Chapter 5 offers a few closing tips and suggestions, and many sections of this
book have suggestions for further reading. In particular, this book does not contain
the complete documentation for every Drupal function and class that it mentions or
uses; you can find this information on the official Drupal API reference site,
https://api.drupal.org. See “Using api.drupal.org” on page 201 for more details.
All of the code in this book is available for download from the
book’s website. The downloaded code contains additional docu‐
mentation headers and test code beyond what is included in the
book. See “How to Contact Us” on page xii.

Drupal Versions
Every few years, the Drupal project releases a new major version of Drupal (Drupal 6,
Drupal 7, etc.). Each major version of Drupal brings large, incompatible changes to
the architecture and API, and generally, programming that you do for one major ver‐
sion cannot be used without modification in other major versions. Contributed mod‐
ules (additional modules downloaded from drupal.org) also make large, incompatible
architectural and API changes with their releases (Views 6.x-2.x versus 6.x-3.x, for
instance).
The code samples in this book are compatible with either Drupal 7 or Drupal 8 as
noted, and with particular Drupal 7 or 8 versions of contributed modules as noted in
their sections. The descriptive sections of this book are also written primarily with
Drupal 7 and 8 in mind.

Preface

|

ix

Conventions Used in This Book
The following terminology conventions are used in this book:
• Although on some operating systems directories are called folders, this book
always refers to them as directories.
• Sample site URLs use example.com as the base site URL.
• Sample modules have machine name mymodule, and sample themes have
machine name mytheme.
The following typographical conventions are used in this book:
Italic
Indicates new terms, URLs, email addresses, filenames, and file extensions
Constant width

Used for program listings, as well as within paragraphs to refer to machine names
and program elements such as variable or function names, databases, data types,
environment variables, statements, and keywords
Constant width bold

Shows commands or other text that should be typed literally by the user
Constant width italic

Shows text that should be replaced with user-supplied values or by values deter‐
mined by context
This icon signifies a tip or suggestion.

This icon signifies a note.

This icon indicates a warning or caution.

x

|

Preface

Using Code Examples
Supplemental material (code examples, exercises, etc.) is available for download at
https://github.com/oreillymedia/programmers_guide_to_drupal.
This book is here to help you get your job done. In general, if example code is offered
with this book, you may use it in your programs and documentation. You do not
need to contact us for permission unless you’re reproducing a significant portion of
the code. For example, writing a program that uses several chunks of code from this
book does not require permission. Selling or distributing a CD-ROM of examples
from O’Reilly books does require permission. Answering a question by citing this
book and quoting example code does not require permission. Incorporating a signifi‐
cant amount of example code from this book into your product’s documentation does
require permission.
We appreciate, but do not require, attribution. An attribution usually includes the
title, author, publisher, and ISBN. For example: “Programmer’s Guide to Drupal, Sec‐
ond Edition by Jennifer Hodgdon (O’Reilly). Copyright 2016 Poplar Productivity‐
Ware, LLC, 978-1-4919-1146-4.”
Drupal is an open source project, and its code is uses the GNU Public License (GPL).
Some Drupal project code is reproduced in this book (clearly labeled); use of this
code needs to comply with the GPL. See the LICENSE.txt file distributed with Drupal
code downloads for more information.
If you feel your use of code examples falls outside fair use or the permission given
above, feel free to contact us at permissions@oreilly.com.

Safari® Books Online
Safari Books Online is an on-demand digital library that deliv‐
ers expert content in both book and video form from the
world’s leading authors in technology and business.
Technology professionals, software developers, web designers, and business and crea‐
tive professionals use Safari Books Online as their primary resource for research,
problem solving, learning, and certification training.
Safari Books Online offers a range of plans and pricing for enterprise, government,
education, and individuals.
Members have access to thousands of books, training videos, and prepublication
manuscripts in one fully searchable database from publishers like O’Reilly Media,
Prentice Hall Professional, Addison-Wesley Professional, Microsoft Press, Sams, Que,
Peachpit Press, Focal Press, Cisco Press, John Wiley & Sons, Syngress, Morgan pass:
Preface

|

xi

[Kaufmann], IBM Redbooks, Packt, Adobe Press, FT Press, Apress, Manning, New
Riders, McGraw-Hill, Jones & Bartlett, Course Technology, and hundreds more. For
more information about Safari Books Online, please visit us online.

How to Contact Us
Please address comments and questions concerning this book to the publisher:
O’Reilly Media, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
800-998-9938 (in the United States or Canada)
707-829-0515 (international or local)
707-829-0104 (fax)
We have a web page for this book, where we list errata, examples, code downloads,
and additional information. You can access this page at http://bit.ly/
prog_guide_drupal2e.
To comment or ask technical questions about this book, send email to bookques‐
tions@oreilly.com.
For more information about our books, courses, conferences, and news, see our web‐
site at http://www.oreilly.com.
Find us on Facebook: http://facebook.com/oreilly
Follow us on Twitter: http://twitter.com/oreillymedia
Watch us on YouTube: http://www.youtube.com/oreillymedia

Acknowledgments
Writing this book would not have been possible without the worldwide Drupal open
source project community. For the first edition, I would especially like to acknowl‐
edge the support of the women of Drupal and the members of the Seattle and Spo‐
kane Drupal Groups. Without their help and encouragement, I would never have
even gotten in touch with O’Reilly (thanks, Angie!), much less decided to write this
book. The daily cheerleading of my partner, Zach Carter, was also a great help in
completing it.
The second edition would not have been possible for me to write if I had not first
gained an understanding the new Drupal 8 API. This understanding came through
discussions with the Drupal community in the #drupal-contribute IRC channel and
the Drupal issue queues, especially while I was writing landing-page topics for https://
api.drupal.org. I would especially like to thank Károly Négyesi (chx), Tim Plunkett
xii

|

Preface

(tim.plunkett), and Sascha Grossenbacher (berdir) for their patient explanations and
careful patch reviews during this process, and Angie Byron (webchick) for envision‐
ing and organizing the API documentation topics effort.
I would also like to thank Will Hartmann (PapaGrande), Michelle Williamson (mic‐
nap), Melissa Anderson (eliza411), Katherine Senzee (ksenzee), and Michael J. Ross
(mjross) for providing technical reviews of the first edition of this book, and Károly
Négyesi (chx) and Michelle Cox (Michelle) for providing technical reviews of the sec‐
ond edition. Their careful reading and thoughtful comments spurred many improve‐
ments to the text and code in the book, and I am greatly indebted to them. The
careful copyediting of the second edition by Eileen Cohen was also very much appre‐
ciated.
And finally, I would like to thank my editors at O’Reilly, Meghan Blanchette for the
first edition and Allyson MacDonald for the second edition, for many valuable sug‐
gestions, and Meghan in particular for patiently guiding me through my first experi‐
ence with the publishing process. I’d also like to thank Matthew Hacker in
Production, without whom the second edition would not have come out.

Preface

|

xiii

CHAPTER 1

Overview of Drupal

This chapter contains overviews of the Drupal software and open source project, how
Drupal has evolved from its beginnings through the latest version (Drupal 8), how
Drupal performs its core function (handling HTTP requests), the Drupal cache sys‐
tem, and Drupal’s automatic class loading system. You will need to be familiar with
this information in order to understand and use the rest of the book, so you’ll proba‐
bly want to at least skim through it, if not read it in depth.

What Is Drupal?
Depending on who you talk to, you’ll hear Drupal called a content management sys‐
tem (CMS) or a content management framework (CMF), a platform that you can use
to build a custom CMS—and both are accurate. It can be called a CMS because after
installing only the base Drupal software, you can create a basic website and manage
the content online. On the other hand, it can be called a flexible CMF because most
people choose to add additional modules to Drupal in order to build more compli‐
cated websites with more features, and Drupal also allows you to create fully custom
modules.
Drupal is free and open source software (FOSS), governed by the GNU General Public
License (GPL) version 2 (or, at your option, any later version). The licensing means
that Drupal is free for you, or anyone else in the world, to download, use, and modify,
as long as you comply with the license terms.

1

If you have never read the GPL and plan to use Drupal, you would
be well advised to do so (even more so if you plan to do any Drupal
programming, for yourself or others). The GPL governs not only
what you can do with Drupal software itself, but also what you can
do with any add-ons you download from drupal.org, code you find
on drupal.org documentation pages, and any derivative work (work
that contains GPL-licensed work, verbatim or with modifications)
that you or others create. It’s also written in plain English and is
quite a good read (for programmer types anyway); you can find it
in the LICENSE.txt file distributed with Drupal core, or at
http://gnu.org.

And finally, Drupal is also a project and a community. Unlike some FOSS software
that is developed primarily by one company that later releases the source code to the
public, Drupal is continually evolving due to the efforts of a worldwide community of
individuals and companies who donate their time and money to create and test Dru‐
pal software, write the documentation, translate it into other languages, answer sup‐
port questions, keep the drupal.org web servers running, and organize get-togethers
on a local and worldwide scale.

Drupal Core
Drupal core is the name of the package you can download from https://
www.drupal.org/project/drupal, consisting of a set of PHP scripts (some with embed‐
ded HTML markup), JavaScript, CSS, and other files. This software interacts with a
web server (typically, Apache), a database (MySQL, PostgreSQL, and SQLite are sup‐
ported by Drupal core versions 7 and 8, and others are supported by add-on mod‐
ules), and a web browser to provide the basics of a CMS:
• A URL request dispatch system
• A user account management system with flexible permissions and roles
• Online content editing
• An API for Drupal programmers
• A module system, which lets you enable and disable functionality in a modular
way and download additional code to expand functionality
• A theme (template) system, which lets you override how everything from a but‐
ton to an entire page is displayed
• An installation profile system, which governs what modules and themes are
installed when you first install Drupal and allows downloading of complete Dru‐
pal distributions for special-purpose sites

2

|

Chapter 1: Overview of Drupal

• An internationalization system, which allows you to translate your site into other
languages
• A block system that allows you to place chunks of content in various regions of a
site’s pages
• A navigation menu builder
• A flexible taxonomy system that supports categories, tags, and user-defined
taxonomy vocabularies
• Optional modules supporting commenting, content fields, RSS aggregation,
search, lists of content and other site components, and site features such as
forums and polls (depending on the Drupal version, some of these may require
downloading add-on modules instead of being part of Drupal core)
• The ability to set up a site in different languages and translate content (depending
on the Drupal version, some add-on modules may be required to make a multilingual or non-English site)
• Logging of system events and errors
• An automatic class loading system
• In Drupal 8, a file-based configuration management system
• In Drupal 8, a plugin API, which is used in many Drupal systems
• In Drupal 8, the services concept and the dependency injection container, which
allow overrides of basic Drupal functionality

Drupal Add-Ons: Modules, Themes, Distributions, and Translations
Drupal is modular software, meaning that you can turn site features and functionality
on and off by enabling and disabling modules. In Drupal 7 and earlier versions, you
can disable a module to turn off its functionality while retaining its data so it can be
enabled again. However, in Drupal 8, this is not allowed: to turn off a module you
have to uninstall it completely, losing its data. Drupal core comes with a few required
modules and several optional modules; you can download thousands of additional
contributed modules from https://www.drupal.org/project/modules. Most modules have
configuration options that you can modify from the Drupal administration interface,
by logging in to the Drupal-based site using an account that has been given appropri‐
ate permissions. The permission system is flexible: you can define named roles, which
are granted specific permissions (the permissions are defined by modules), and you
can assign one or more roles to each user account.
Drupal’s theme system separates the content from the specific HTML markup and
styling. This means that if you want to redesign the site’s layout or styling, you can do
so by downloading a new theme from https://www.drupal.org/project/themes,

What Is Drupal?

|

3

purchasing a commercially available theme, or creating one yourself. Once a theme is
installed and enabled, it takes effect immediately to change the look of your site
without the necessity of editing your content pages. The theme system has a cascad‐
ing system of inheritance and overrides, including the ability to define sub-themes,
which allows you to use the default display or the display from a theme you have
downloaded for some aspects of the output, and override the parts you want to
change; the overrides can be at anything from the lowest level (e.g., the presentation
of buttons) to the full page.
You can also download Drupal in a distribution, which consists of Drupal core, an
installation profile, and a collection of contributed modules and themes that work
together to provide a more functional site for a specific purpose. Distributions are
available at https://www.drupal.org/project/distributions for ecommerce, government,
nonprofits, and many other purposes.
And finally, you can download translations for Drupal and its contributed modules,
themes, and distributions from https://localize.drupal.org. This allows you to build a
non–English language site fairly easily with both Drupal 7 and Drupal 8. Multilingual
sites can also be built using Drupal; in Drupal 7, this requires downloading several
contributed modules and is fairly complex, but Drupal 8 core has much more multi‐
lingual functionality built in and is much more streamlined. See https://
www.drupal.org/documentation/multilingual for more on building multilingual sites.

Finding Drupal add-ons
Here are the main ways to find Drupal add-ons (modules, themes, or distributions):
• To find a specific add-on that you know the name of, visit https://www.drupal.org
and type the name into the search box.
• If the add-on that you’re looking for is not in the first few results, try restricting
the search to modules or themes, using the filters in the right sidebar (there is no
way to restrict to distributions from a drupal.org search, as of summer 2015).
• Alternatively, start by navigating to https://www.drupal.org/project/modules,
https://www.drupal.org/project/themes, or https://www.drupal.org/project/distribu
tions, and searching from there. You can also search from these pages by key‐
word, Drupal version compatibility, or category (modules only).
• You can try guessing the URL, which is always drupal.org/project/, followed by
the machine name of the project. The machine name is composed of lowercase
letters, numbers, and underscores, but because the machine names are chosen by
developers, some are hard to guess and they may take a couple of tries. For exam‐
ple, the Panels module is at https://www.drupal.org/project/panels; the Omega
theme is at https://www.drupal.org/project/omega; the XML Sitemap module is at
https://www.drupal.org/project/xmlsitemap.

4

|

Chapter 1: Overview of Drupal

• Of course, you can always try your favorite web search engine, with keywords
“Drupal module” and the name of the module.

The Evolution of Drupal
Drupal started out as a message board, created by founder Dries Buytaert at the Uni‐
versity of Antwerp in 1999. Once this message board was established, its members
started exploring ideas for its improvement, and these were added to the software.
Eventually, Dries named the software Drupal, and he released it on January 15, 2001,
as an open source project so others could work on it. By June 2002, when version
4.0.0 was released, Drupal had expanded to a more flexible CMS, with new modules
handling blogs, polls, taxonomy, and generic content, and a system for add-on mod‐
ules and themes. The 4.x.y, 5.x, and 6.x versions continued the process of adding new
CMS functionality and refining the API.
Don’t worry if you don’t know what all of the terminology in this
section means yet! It will be explained later in the book, where pro‐
gramming with each of the related subsystems of Drupal is
explored.

January 2011 saw the first full release of Drupal version 7, which brought several
major changes to the Drupal software and its API:
• There was a major usability push. A group formed to do usability testing, and its
findings resulted in the redesign and reorganization of administrative pages.
• The accessibility of the administrative interface was also vastly improved, due to
the efforts of a dedicated group of people interested in accessibility, and the rec‐
ognition in the Drupal community as a whole of its importance.
• A completely new database API, based on the PHP Data Objects (PDO) library,
was added. This took Drupal 6’s concept of database portability, which was
accomplished through a set of wrapper functions, and made it object-oriented,
more robust, more secure, and more industry-standard.
• A class autoloader was created and added to Drupal core.
• The concept of entities was introduced, encompassing content items from the
Node module, user accounts, taxonomy terms, and comments. The system also
allowed add-on modules to define their own entity types.
• In conjunction with entities, a system for fields on entities was added to Drupal
core (previously, this had been in the Content Construction Kit, or CCK, contrib‐
uted module, and it only worked on node content items). Many field modules
were also added to Drupal core, including modules to handle file and image
The Evolution of Drupal

|

5

uploads, and output images in different sizes. Because nearly every Drupal site
needs this functionality, getting it into Drupal core was a key Drupal 7 milestone.
• A new Render API was introduced, with the goal of delaying rendering until later
in the page-generating process, so that the page can be altered before it is ren‐
dered. This was based on the Form API that existed in earlier versions.
• An automated testing system was introduced, which had previously been in a
contributed module. Tests were added to Drupal core for much of the basic func‐
tionality, and these tests were run on every proposed Drupal core patch, to guard
against regression. This greatly improved the quality of the Drupal core source
code and of contributed modules that made use of it.
• The API for defining installation profiles was revised and streamlined.
Drupal 8 introduces even more changes:
• The usability and accessibility teams have continued to improve the usability and
accessibility of the administrative UI.
• A mobile team was formed, which has made sure that both the administrative UI
and the core themes work well on mobile devices (phones and tablets).
• Underneath the UI, the internals of Drupal have been radically transformed from
a mostly ad-hoc, specific-to-Drupal, procedural-code-based system into a mostly
object-oriented system that incorporates code from many outside open source
projects. To do this, the Drupal 8 core development community has had to over‐
come the “not invented here” attitude seen in many open source projects; the idea
is that the Drupal project should use code from other projects where possible,
and improve that code if necessary (to the benefit of other open source projects),
rather than trying to invent everything itself. In addition, some of the core sys‐
tems of Drupal were rewritten as portable PHP classes without Drupal-specific
dependencies, with the idea that they could be used by other open source
projects.
The internal systems and APIs that are radically changed in Drupal 8 include:
• The system for handling URL requests, which was native to Drupal in previous
versions, has been replaced by the URL request handler from the Symfony
project. Symfony is an open source PHP web development framework.
• A system for swappable services has been added, and many aspects of Drupal
core functionality have been converted to services (allowing contributed modules
to replace them). The service framework also comes from the Symfony project.
• An object-oriented plugin system has been added, and many of the hooks from
previous versions of Drupal have been converted to plugins. This means that
modules have increasing amounts of object-oriented code and reduced amounts
6

| Chapter 1: Overview of Drupal

of procedural code. The plugin framework comes from the Symfony project and
the Doctrine project.
• Some informational hooks from previous versions of Drupal have been con‐
verted to using YAML-formatted files to provide the information.
• The system for storing configuration information, which was interconnected
with other data and somewhat ad hoc in Drupal 7 and previous versions, has
been replaced by a new API. Configuration is now easy to export and import,
making it fairly straightforward to share configuration among sites or store it in a
revision control system.
• The theme system has been converted to use the templating system from the
Twig project, instead of the Drupal-specific PHP-based templates and functions
used in Drupal 7 and previous versions.
• The class loader from the open source Composer project replaces the Drupalspecific class loader from Drupal 7.
• Some functionality previously in contributed modules, such as the Views mod‐
ule, has been added to Drupal core. On the other hand, some of the less-used
Drupal core modules, such as the Poll module, have been converted to contrib‐
uted modules and removed from Drupal core.

How Drupal Handles HTTP Requests
When Drupal is installed properly and the web server receives an HTTP request that
corresponds to the Drupal site, the main Drupal index.php file is loaded and executed
by the server to handle the request. It is important for Drupal programmers to under‐
stand how Drupal handles such requests. This process changed significantly between
Drupal 7 and Drupal 8, so it is discussed in two separate sections.
Further reading and reference:
• “The Drupal Cache” on page 10
• “Registering for URLs and Displaying Content” on page 88
• “Where to Find More Information” on page 199 (web technology section—to
find resources for learning about how web servers process requests in general)
• Symfony framework: http://symfony.com
• Composer project: https://getcomposer.org/

How Drupal Handles HTTP Requests

|

7

HTTP Request Handling in Drupal 7
Here is an overview of the HTTP request processing sequence in Drupal 7:
1. Drupal determines which settings.php file to use for the HTTP request (you can
set up Drupal to serve multiple sites, each with its own settings.php file), and this
file is loaded and executed.
2. The database connection and configuration/variable system are initialized.
3. If a request is coming from an anonymous user (a site visitor who is not logged
in), the page cache is checked to see if output has previously been cached for the
same requested URL. If so, the cached output is returned to the web server, and
Drupal is done. Drupal page caching does not apply to authenticated (logged-in)
users.
4. PHP session variables are initialized.
5. The language system is initialized, and various files are loaded and executed
(core include files and enabled modules’ .module files).
6. Drupal determines whether the site is offline (also known as being in mainte‐
nance mode) or online.
7. If the site is offline, Drupal retrieves the offline message stored by an administra‐
tor as the page content. Other functions are called to generate some sections of
the page content.
8. If the site is online, or if an authorized user is accessing a page while the site is
offline, Drupal determines which functions need to be called to generate the con‐
tent for the request, and it calls these functions. They ideally return render arrays
that include the page data and other information necessary to render the page,
but they could also return rendered or partially rendered content. During this
process, if the page request includes a Drupal Form API form submission, the
submitted data is processed and sent to form validation and/or submission han‐
dler functions.
9. Drupal determines what delivery method to use for the page and calls the appro‐
priate delivery function.
10. For HTML page requests, the default page-delivery function prints HTTP head‐
ers, uses the theme to render the render array elements into HTML, prints the
HTML output (which effectively sends it to the web server), saves user session
information to the database, and exits. The Ajax request-delivery function is sim‐
ilar, but it renders into JavaScript Object Notation (JSON) output instead of
using the theme system to render to HTML. Modules can also define custom
page-delivery methods.

8

|

Chapter 1: Overview of Drupal

HTTP Request Handling in Drupal 8
The major change in Drupal 8’s HTTP request handling from all previous versions of
Drupal is that Drupal 8 uses the Symfony framework to handle HTTP requests. Here
is an overview of the HTTP request processing sequence in Drupal 8:
1. The dynamic class loader and error handlers are started. At any time after this
point, if an operation requires a connection to the database, it will be initialized.
2. Drupal determines which settings.php file to use for the HTTP request (you can
set up Drupal to serve multiple sites, each with its own settings.php file), and this
file is loaded and executed. Note that the setup for multiple sites in Drupal 8
requires a sites.php file; in Drupal 7, you could get by with special names for the
site directories.
3. The Drupal kernel is initialized, which starts up the dependency injection con‐
tainer (which defines services provided by classes). At any time after this point, if
an operation requires the configuration system, it will be initialized.
4. PHP session variables and cookies are initialized.
5. The page cache is checked to see if output has previously been cached for the
same request conditions (known as context) and URL. If so, the cached output is
returned to the web server, and Drupal is done.
6. Various files are loaded and executed (core include files and enabled mod‐
ules’ .module files).
7. Symfony’s HTTP request system handles the request and returns the result to the
browser. In this system:
• Modules can register routes, which map URLs and context to controller classes.
Controller classes come in several flavors, including those that produce generic
page output, and those that present forms and process form submissions.
• Symfony determines the best match to the context and chooses the controller
method to execute.
• The controller either returns a Symfony response object containing HTTP
header and content information, or a Drupal render array. Symfony response
objects are handled directly by the Symfony framework; render arrays are han‐
dled by a Drupal subsystem that converts the array into the main content for
the page or request, optionally decorates it with blocks or other markup, and
wraps the result in a Symfony response object.
• Symfony returns the response information to the browser.
• Modules can also register event subscribers to intercept and override various
steps in the response process. Core modules use event subscribers for many
purposes, including translating URL aliases to system URL paths, enabling
How Drupal Handles HTTP Requests

|

9

maintenance mode, handling language selection, and providing dynamic URL
routes.

The Drupal Cache
Drupal has a cache system, which allows modules to precalculate data or output and
store it so that the next time it is needed it doesn’t have to be calculated again. This
can save a lot of time on page loads, at the expense of some added complexity: any
module that uses caching needs to take care to clear its cached data whenever the data
is invalidated due to changes in dependent data.
Both Drupal core and add-on modules cache information using this system. Here are
a few examples (not all of them apply to all versions of Drupal):
• Page output (page caching can be turned off from the Performance configuration
page)
• Block output (block caching can be turned off from the Performance page in
Drupal 7 but is always on in Drupal 8 for cacheable blocks)
• Information collected from hooks; for example, Drupal 7 entity type and field
definitions
• Lists of available plugins in Drupal 8 plugin managers
• Theme information, including the list of theme regions and theme-related infor‐
mation from modules and themes
• Form arrays
Programmers and site builders new to Drupal quickly learn that the first thing to try,
if they are having trouble with a site or if programming changes they have recently
made are not being recognized, is to clear the cache. You can clear the cache by visit‐
ing the Performance configuration page and clicking the cache clear button, or by
using Drush. The Drush command to clear the cache in Drupal 7 is drush cc all; in
Drupal 8, use drush cr (which clears the cache and also rebuilds the dependency
injection container; more about that later in the book).
The rest of this section provides details of the cache API, which
you can skip for now and return to when you need them.

10

|

Chapter 1: Overview of Drupal

Further reading and reference:
• See https://api.drupal.org to find full documentation of the cache functions men‐
tioned in the following sections (there is a “Cache API” topic for Drupal 8; in
Drupal 7, look up the individual functions).
• See “Principle: Drupal Is Alterable” on page 17 to learn more about hooks and
plugins in general, “Using the Drupal Form API” on page 109 to learn about
forms, and “Drupal 8 Services and Dependency Injection” on page 35 to learn
about services and the dependency injection container.
• For more information on Drush, see “Drupal Development Tools” on page 204.
• “Rebuilding the container” on page 38.
Examples—using the cache:
• The Cache example in Examples for Developers illustrates how to use the Cache
API.

Drupal 7 Cache API
The Drupal 7 cache system has a fairly simple API, consisting of the cache_set() and
cache_get() functions (with a few variations), as well as cache_clear_all() and
drupal_flush_all_caches() to clear cache information. Modules can register to
have their caches cleared by implementing a hook (hooks are module entry points to
altering Drupal) called hook_flush_caches(). All cached data is stored in the data‐
base, usually in tables whose names start with cache_.

Drupal 8 Cache API
The Drupal 8 cache API uses services, so that different sets of cached data, or bins, can
use different storage mechanisms. In Drupal 8, all cache functionality starts by calling
\Drupal::cache($bin), passing in the name of the cache storage bin you want to
use, to get an instance of the correct cache class; this class will implement \Drupal
\Core\Cache\CacheBackendInterface. Many cache applications use the default
bin. Alternatively, if you have a $container variable in a class method, you’ll call:
// The name of the service for a particular cache bin is
// 'cache.' . $bin.
$cache_class = $container->get('cache.' . $bin);

Once you have a cache class, your module can call methods such as set(), get(), and
invalidate() to store, retrieve, and invalidate your cached data. If your module uses
a custom cache system, it should implement hook_cache_flush() to flush the data
when a cache clear is requested via the drupal_flush_all_caches() function, and
The Drupal Cache

|

11

you may need to implement hook_rebuild() as well. However, if you store your data
in cache bins that Drupal core defines, your cached data will automatically be flushed.
For efficiency, the Drupal 8 cache system also has a tagging mechanism, which is used
to flush parts of the cache (rather than the whole cache) when data they’re related to
is changed or removed. For instance, if your module is caching data related to a par‐
ticular node content item, you should tag it with the node ID when you add it to the
cache, by calling:
// $cid is a unique cache ID key for your data, which is $data.
// $nid is the ID of the node whose data is cached.
$cache_class->set($cid, $data, CacheBackendInterface::CACHE_PERMANENT,
array('node:' . $nid));

Tags are passed in as the fourth argument; here, the node tag is given a value equal to
the node ID related to this cached data. Then, when calls to the core Node module
modify this node content item, it will call cache methods to invalidate all cached data
that was tagged with this node ID. There is also a node_list cache tag, which indi‐
cates your data is a list of node content items; this tag is invalidated whenever any
node is changed, added, or deleted.
You can define custom tags for your module’s caches and use them to invalidate
groups of cached data when appropriate by calling:
\Drupal\Core\Cache\Cache::invalidateTags($tags);

There is one more concept in caching, which is used in the request-rendering pipe‐
line. The idea is that the content of a page (or other request result) may depend on
various variables, such as the user who is viewing the page, the user’s roles, the page
language, the time zone, or other factors; these factors are collectively known as the
cache context. For details on how to add cache information to a render array, see the
“Render API Overview” topic page on https://api.drupal.org.

Automatic Class Loading in Drupal
Drupal core versions 7 and 8 both include systems for automatic loading of files con‐
taining PHP class, interface, and trait declarations (in the rest of this section, class
refers to either a class or an interface, or a trait in Drupal 8). The basic idea of both
systems is the same:
• Integrate with PHP’s native class-autoloading system, in which functions can be
registered as class loaders.
• Reduce the load on the server by loading files containing class definitions only if
those classes are being used in a particular page request.

12

|

Chapter 1: Overview of Drupal

• Reduce the burden on developers by automatically loading files containing class
definitions, instead of making them load the classes explicitly in code.
Drupal 7’s class-loading system is a homegrown (specific to Drupal) system called the
registry. Here is how it works:
1. Drupal determines when the class registry needs to be updated; for example,
when you enable or disable modules, update a module version, or clear the Dru‐
pal cache.
2. When the registry needs to be updated, Drupal makes a list of files it needs to
scan. These include files explicitly listed in enabled modules’ .info files, as well
as .inc files in the Drupal core includes directory.
3. Each of these files is scanned for PHP class declarations.
4. The resulting list of which files contain which class declarations (the registry) is
saved in the database.
5. During start-up, Drupal registers its drupal _autoload_class() and drupal_
autoload_interface() functions as PHP autoload functions. When an unde‐
fined class is accessed in PHP code, the PHP engine calls these functions, which
read the registry database to locate the file the class is defined in, and then
include the file so that the class is defined.
Drupal 7 class names have to be globally unique. Always name
your classes with a prefix that makes them unique, such as your
module name.

Early in the development cycle for Drupal version 8, a decision was made to stop
using the homegrown registry system developed for Drupal 7, and instead incorpo‐
rate the class loader from the Composer open source project, which is based on the
PSR-0 and PSR-4 standards, and PHP namespaces. Here is how this system works:
1. Classes are declared to be in a PHP namespace with a PHP namespace declara‐
tion. Drupal uses namespaces beginning with the name \Drupal, and the name‐
space(s) for a particular module mymodule should begin with \Drupal\mymodule.
2. Files that depend on classes outside their own namespace have PHP use declara‐
tions for the outside classes.
3. Each class declaration is in its own file.
4. The directory that each class file goes in depends on its namespace. Examples:

Automatic Class Loading in Drupal |

13

• An add-on module’s \Drupal\mymodule\subnamespace\Foo class would go in
the src/subnamespace/Foo.php file under the main module directory.
• Classes that are for Drupal core broadly, and not part of a particular Drupal
core module, are found under the core/lib directory; for example, the \Drupal
\Component\Datetime\DateTimePlus class is located in the core/lib/Drupal/
Component/Datetime/DateTimePlus.php file.
• Classes that have been adopted from outside projects are found under the ven‐
dor directory; for example, the \Symfony\Component\DependencyInjection
\Container class is located in the vendor/symfony/dependency-injection/
Container.php file.
5. Drupal provides information about its namespace and directory conventions to
the Symfony class-loading system. For instance, it tells Symfony to look in
enabled modules’ src directories for class files, but not in disabled modules’
directories.
In PHP, namespaces in namespace and use declarations do not start
with a backslash, although the fully qualified namespace, used in
other places in code, always starts with a backslash. In this book,
Drupal 8 classes are always referred to with their fully qualified
namespace, to avoid ambiguity.

6. When a class is needed, the Symfony class loader automatically locates and loads
the include file so that the class is defined.
As a side effect of the Drupal 8 class-loading system, each module
needs to be in its own directory or subdirectory in Drupal 8 (in
Drupal 7, you could put several modules into the same directory).
The name of the directory that directly contains the main module
files needs to match the module’s machine name exactly (in Drupal
7, this did not need to be the case).

Further reading and reference:
• Namespaces in PHP: http://php.net/manual/language.namespaces.php
• Composer project: https://getcomposer.org/
• PSR-0 standard: http://bit.ly/psr-0_standard
• PSR-4 standard: http://bit.ly/psr-4_autoloader

14

|

Chapter 1: Overview of Drupal

www.allitebooks.com

Examples—class loading:
• Code examples in this book involving classes include information on what to
name the file, where to put it, and what needs to go into the module information
file (in Drupal 7 only) so that the class-loading system can locate it.

Automatic Class Loading in Drupal

|

15

CHAPTER 2

Drupal Programming Principles

Experienced programmers learn, through training and experience, a set of principles
and best practices to apply whenever they approach a problem they want to solve
with programming. These include general practices such as “comment your code”
and “choose clear variable names,” which apply to all programming languages and sit‐
uations, and some that are specific to a particular domain. Drupal has its own set of
programming principles (covered in this chapter); learning them and following them
should help you be a more effective Drupal programmer.
If you are completely new to Drupal programming, you might find
it useful to look at some Drupal code before or while reading this
chapter. One suggestion would be to download Examples for
Developers. You could try out the Page and Block examples, and
take a look at their source code. Or, you might consider browsing
through one of the Drupal core modules in the Drupal 7 modules
directory or Drupal 8 core/modules directory, although they are not
documented as tutorials. The Statistics module for Drupal 7 and 8
(which tracks view statistics and provides a block showing popular
content) or the Email module for Drupal 8 (which provides a field
for storing email addresses) would be good choices.

Principle: Drupal Is Alterable
Drupal is intended to be used as a platform for building web applications, so one of
its fundamental principles is that nearly everything about it needs to be customizable,
and it needs to be customizable without having to edit the Drupal core source code.
Because you’re not supposed to need to edit the base code to build any type of web
application with Drupal, both Drupal core and contributed modules are (ideally) fully

17

alterable, meaning that they provide mechanisms that you can use to customize and
add to their behavior and output.
The basic idea is that instead of editing code you downloaded, you should instead
create an add-on module or theme using the standard Drupal alteration mechanisms.
In some cases, you will have a choice of using either a module or a theme, but a good
rule of thumb is that if your aim is to change the presentation of data, such as the
exact HTML markup and CSS, you should create a theme, and if your aim is to
change the way Drupal behaves, add new features, or change what data is being out‐
put, you should create a module. If you need to do both, you probably will be best off
separating out the presentation changes from the behavior and data changes, and cre‐
ating both a theme and a module.
You’ll also need to figure out what type of alteration you need to do. One type of
alteration mechanism available in Drupal is a hook. A hook is a PHP file or function
you can put into a module or theme, which will be invoked (called or included) at an
appropriate time by a Drupal core or contributed module in order to let your module
or theme alter (or add to) behavior and output. All versions of Drupal offer several
different types of hooks:
• Generic hooks, which allow modules to define additions to Drupal behavior by
defining a function.
• Alter hooks, which allow modules to make modifications to existing Drupal
behavior by defining a function.
• Theme hooks, which allow themes to modify the output that is sent to the
browser. Theme hooks come in two varieties: some use theme functions (the
theme defines a function to modify the output), and some use theme template
files (the theme has a file that modifies output); in Drupal 8, nearly all are tem‐
plate files. Drupal 7 template files are PHP files with extension .tpl.php, and Dru‐
pal 8 template files are Twig files with extension .html.twig.
Theme hooks come with theme preprocessing hooks and theme processing hooks,
which modules and themes can both use to alter the information that is sent to
the theme function or template file for output.
Drupal 8 offers several additional mechanisms for altering and adding to Drupal
behavior:
• A plugin system, which has replaced many of the generic hooks from Drupal 7
with an object-oriented system whereby modules can add classes that define
additions to Drupal behavior.
• The dependency injection system, which allows modules to replace fundamental
core systems of Drupal (or services), such as the mechanism for storing cached
data.
18

| Chapter 2: Drupal Programming Principles

• An event system, which allows modules and core components to define and dis‐
patch events, and other modules to subscribe and respond to them.
• A new Symfony-based routing system, which allows modules to define URLs and
their output by defining routes and controllers, and to alter what happens at vari‐
ous points during an HTTP request using the event system.
• A new YAML-based system for defining default menu links, contextual links, and
other data for various Drupal core systems.
So, in Drupal 7 and earlier versions (at least, from the point of view of a PHP pro‐
grammer), modules are mostly collections of hook implementations (PHP functions
that define the hook’s output or behavior), and themes are mostly collections of
theme hook overrides (PHP functions or template files that define how output is pre‐
sented); both themes and modules can also contain supporting code, CSS, JavaScript,
images, and other files. In Drupal 8, most modules will contain fewer hook imple‐
mentations, as many of these are replaced by classes and YAML-based files.

Why Not Just Hack (Edit) the Code?
Unlike in some other programming communities, the word “hack” in the Drupal
community has definite negative connotations: hacking specifically means editing
code that you downloaded from drupal.org, in order to make a change that you need
for a site. Hacking is highly discouraged; “hacking core,” or editing the Drupal core
files, is considered to be the worst offense. There are several reasons:
• Hacking is usually unnecessary, as there should be a hook or other mechanism
available that will let you accomplish your goal without hacking.
• If you have hacked Drupal core or other downloaded code, updates will be much
more difficult, because you will need to reapply your hack after downloading a
new version of the code. All downloaded code is at least occasionally updated
with security fixes, bug fixes, and new features.
• If you program with hooks, plugins, and other alteration mechanisms instead of
hacking, you can turn off your changes by disabling your module. If you hack
code, you will need to “unhack” it to turn off your changes.
• You may find that there isn’t a mechanism available for altering the behavior you
want to alter, or you may be tempted to hack in order to fix a bug or add a new
feature to a module. If you instead turn your hack into a patch, it becomes a ben‐
efit to you and the Drupal community if it is added to the module or to Drupal
core, and when you next update, your fix will be included.

Principle: Drupal Is Alterable

|

19

Further reading and reference:
• The following sections explain how to set up a module or theme so Drupal will
recognize it, and the basics of programming with Drupal’s generic altering mech‐
anisms: hooks, plugins, events, and dependency injection.
• Routes and routing events for Drupal 8 are covered in “Registering for a URL in
Drupal 8” on page 92 and “Altering Routes and Providing Dynamic Routes in
Drupal 8” on page 98.
• Patches: “Reporting Issues and Contributing Code to the Drupal Community” on
page 83.

Setting Up a Module or Theme
To set up a module or theme, follow these steps:
1. Pick a machine name or short name for the module or theme. This is usually a
sequence of letters and underscores, sometimes with numbers, that must follow
PHP’s function-naming conventions, as it will be used as a function and/or
namespace prefix (use the machine name as a prefix for all functions you define
in your module or theme). Pick a name that is not already in use by a project on
drupal.org, to avoid later conflicts. Throughout this book, the convention is that
you are creating a module called mymodule or a theme called mytheme. The maxi‐
mum length of a module or theme machine name is 50 characters.
2. Create a directory for your theme or module. See “Where to put modules and
themes” on page 22 to figure out where to put this directory. In Drupal 7, you
would normally name the directory the same as your module or theme’s machine
name; in Drupal 8, this is required.
3. For Drupal 7, create a file called mymodule.info or mytheme.info inside your
directory, which is a plain text file that contains information about the module or
theme. In Drupal 8, create a file called mymodule.info.yml or mytheme.info.yml.
The exact syntax of this file tends to vary from version to version of Drupal and
is slightly different for modules and themes, so check the online documentation,
or copy a file from a module or theme provided in Drupal core or the Examples
for Developers project to use as a starting point. Here are minimal examples for
Drupal 7 and Drupal 8 modules (the syntax of this portion is the same for
themes):
; Drupal 7 module .info file
; Comments start with ;
; The name displayed on the Modules or Themes list.
name = My Module
; The longer description displayed on the Modules or Themes list.

20

|

Chapter 2: Drupal Programming Principles

description = Longer description of this module.
; The Drupal core version this module or theme is compatible with.
core = 7.x
# Drupal 8 module .info.yml file
# Comments start with #
name: 'My Module'
description: 'Longer description of this module.'
# A required line telling Drupal this is a module.
type: module
core: 8.x

4. Create a PHP base file for your module or theme, inside the same directory. For
Drupal 7, the base file is called template.php for a theme (which is where your
theme function overrides will go) or mymodule.module for a module (which is
where your hook implementations and other functions go), and the file can be
completely empty. For Drupal 8, the base file can be omitted if you do not need
to define any functions or implement hooks. If you do need it, the base file is
called mytheme.theme for a theme or mymodule.module for a module.
You are now ready to put implementations and overrides of hooks, plugins, etc. into
your module or theme.
Further reading and reference:
• Documentation of .info file syntax for Drupal 7 modules: http://bit.ly/
module_info.
• Documentation of .info.yml file syntax for Drupal 8 modules: http://bit.ly/
info_yml.
• Documentation of .info file syntax for Drupal 7 themes: http://bit.ly/theme_info.
• Documentation of .info.yml file syntax for Drupal 8 themes: http://bit.ly/
theme_info_yml.
• Check if a proposed machine name for your module or theme is already in use by
trying the URL drupal.org/project/your_proposed_name.
Examples:
• The code download for this book consists of a module and theme for Drupal 7
and 8. See “How to Contact Us” on page xii.
• There are theme info file examples in “Defining Theme Regions for Block Place‐
ment” on page 77.

Principle: Drupal Is Alterable

|

21

Where to put modules and themes
In Drupal 7 and earlier versions, modules and themes that you download or create
should generally go into the sites/all/modules and sites/all/themes directories. Each
module or theme project should be in its own subdirectory, such as sites/all/modules/
views for the Views project. You can also organize modules into subdirectories; for
example, you could create sites/all/modules/contrib and sites/all/modules/custom
directories for downloaded contributed modules and your own custom modules,
respectively.
If you have a multisite installation and want a module or theme to be available for
only one of the sites, you can put it in sites/specific_site/modules or sites/specific_site/
themes. See https://www.drupal.org/documentation/install/multi-site for more infor‐
mation on this topic.
In Drupal 8, all of the Drupal core code (include files, modules, themes, etc.) has been
moved to the core directory, and while you can still put your downloaded and custom
modules into sites/all, there are also top-level modules and themes directories avail‐
able. The use of these top-level directories is recommended, except for site-specific
modules and themes in a multisite installation. Within these top-level directories, you
are still free to organize your modules into subdirectories.

Creating a sub-theme
Drupal allows you to define a theme that inherits or derives from another theme. The
terminology is that the sub-theme derives from a base theme; the base theme can be
part of Drupal core or can be a contributed or custom theme, and there can be an
arbitrarily long chain of inheritance in sub-themes. To indicate that a theme you are
creating is a sub-theme of another theme, add this line to your mytheme.info (Drupal
7) or mytheme.info.yml (Drupal 8) file:
# Drupal 7 mytheme.info file:
base theme = machine_name_of_base
# Drupal 8 mytheme.info.yml file:
base theme: machine_name_of_base

The main thing that a sub-theme inherits from its base theme is the template files.
Drupal 8 core provides a special base theme called Classy (machine name: classy)
that contains templates whose HTML markup is peppered with many class attributes,
allowing for very specific CSS styling in your sub-theme. Also, by design, the Classy
theme has only a minimal amount of CSS, so you won’t have to counteract or over‐
ride its choices much in your own theme. The core themes Seven (the administration
theme) and Bartik (the user-facing theme) both derive from Classy, which allows
them to define a very small set of templates and do most of their styling work with
CSS. If you are defining a Drupal 8 theme, you may find it useful to use Classy as a
base theme, and you can use these two core themes as a model. In Drupal 7, you’d
22

|

Chapter 2: Drupal Programming Principles

need to sub-theme Bartik to get similar behavior, but there is much more CSS to
override in Bartik than in the Drupal 8 Classy theme.
If you are not fond of the class attributes that Classy and Bartik have in their markup,
you might prefer to use the Stark theme (machine name: stark, available in both
Drupal 7 and 8) as your base theme. The Stark theme is a more basic starting point
than Classy or Bartik, as it does not have any templates of its own—it just uses the
base templates provided by modules. In Drupal 7, you will find that there are still
class attributes in the markup, but in Drupal 8, nearly all of them have been removed
from the base templates, in order to give themers better control over what classes
appear in their HTML markup.
There are also many contributed base themes available. Many theme developers
choose to use a base theme that provides responsive behavior (the core themes in
Drupal 7 are not responsive to mobile device sizes, since they were developed before
responsive design was common) or integrates with a JavaScript and/or CSS web
framework they are familiar with.

The Basics of Module and Theme Hook Programming
To implement a hook or override a theme hook, follow these steps:
1. Set up a module or a theme, following the instructions in “Setting Up a Module
or Theme” on page 20.
2. Figure out which file your hook override or implementation needs to go in. Most
hook implementations and theme function overrides go in the base module or
theme file. However, theme templates are their own files, and there are also a few
hook implementations that belong in other files. For instance, implementations
of install-related hooks such as hook_schema() and hook_update_N() go into the
mymodule.install file, and the contributed Views module for Drupal 7 uses two
separate files for its hooks. Always read the reference documentation for the
hook you are implementing to find out where its implementation belongs.
3. Within your base module or theme file, or the alternative file where your particu‐
lar hook implementation belongs, define a function to implement the hook or
override the theme function (skip this step for theme templates). For a generic
module hook called hook_foo(), the function must be named mymodule_foo().
For a theme hook function called theme_foo(), the function must be called
mytheme_foo(). For the function body, often there is a good starting point in the
hook documentation or the theme hook function you are overriding.
4. To override a theme hook template file, copy the template file you are overriding
to your theme directory, or preferably to a templates subdirectory (this is
required in Drupal 8), keeping the same filename.

Principle: Drupal Is Alterable

|

23

5. Edit the function or file to make the desired changes.
6. Enable your module or theme.
7. As you are programming and testing, if you add a new hook implementation or
theme override to an enabled module or theme, you will need to clear the Drupal
cache so that Drupal will recognize your change. For some informational hooks,
you will also need to clear the cache when you make a change to the function
body, to force Drupal to call your hook implementation function again and read
the new information. In general, it’s always a good idea while you are developing
for Drupal to clear the cache after any change, or if Drupal is not behaving the
way you expect.
Further reading and reference:
• “The Drupal Cache” on page 10.
• If you know the name of a hook, documentation can be found by searching for
the hook function or template filename on the Drupal API site (https://
api.drupal.org), such as hook_block_info, theme_table, block.tpl.php, or
block.html.twig. When searching, do not put () after function names.
• If you do not know the name, there are lists of hooks on the API site: the
“Hooks” topic lists all Drupal core generic and alter hooks, and the “Default
theme implementations” (Drupal 7) or “Theme system overview” (Drupal 8)
topic lists all Drupal core theme hooks.
• To learn about universal and specific theme preprocess and process hooks that
you can define, check the documentation of the theme() function in Drupal 7,
and the “Theme system overview” topic in Drupal 8.
• Hook documentation pages on the API site give you documentation about the
purpose of the hook, parameters, return value, which file the hook should be
located in, a list of places where the hook is invoked (that is, which Drupal sys‐
tem will call your hook implementation function), and a list of other implemen‐
tations in Drupal core. The function body for a hook function is a sample
implementation.
• Theme hook and template documentation pages on the API site tell you where
the theme hook is used and document the variables you can use in your output.
The theme function body or file content is the default implementation, which
you can copy as a starting point for your override. There should also be links to
the default preprocessing and processing functions, which you can add to by
implementing hooks.

24

|

Chapter 2: Drupal Programming Principles

Examples—implementing hooks:
• “Making Your Module Output Themeable” on page 25
• “Setting Up Database Tables: Schema API and hook_update_N()” on page 55
• “Drupal core’s main permission system” on page 64
• “Registering for a URL in Drupal 7” on page 89
• “Altering a URL Registration in Drupal 7” on page 91
• “Providing Administrative Links” on page 95
• “Registering a Block in Drupal 7” on page 100
• “Altering Forms” on page 121
• “Defining an Entity Type in Drupal 7” on page 132
• “Defining a field widget in Drupal 7” on page 164
• “Defining a field formatter in Drupal 7” on page 168
• “Repurposing an existing field widget” on page 167
• “Setting Up Your Module for Views in Drupal 7” on page 174
• “Providing a New Views Data Source” on page 175
• “Adding Fields and Relationships to an Existing Views Data Source” on page 179
• “Providing a Style or Row Plugin to Views” on page 180
• “Providing Default Views” on page 183
• “Providing Custom Actions to Rules” on page 186

Making Your Module Output Themeable
Drupal’s theme system is designed to separate the data and content from the styling
and presentation: the module has control over the data and content, and the theme
that is in use should have full control over styling and presentation, including HTML
markup. The basic principle that makes this work is that all data that is rendered into
HTML should be passed through the Drupal theme system.
In Drupal 7, data can be rendered by calling the theme() function directly, or by pass‐
ing a render array (an array containing the data to be output and formatting parame‐
ters) into the drupal_render() function (which calls theme() internally). Render
arrays that are returned from page- or block-generating functions are also rendered
internally by a call to drupal_render().
In Drupal 8, as much as possible, render arrays should be returned from all functions
that return output, and these will be rendered by calls to internal functions; the

Principle: Drupal Is Alterable

|

25

theme() function does not exist in Drupal 8 for modules to call directly, and calling
drupal_render() directly is also discouraged (and probably will not work).

As an example, whenever a block from the core Block module is rendered in Drupal
7, a call is made to theme('block', $block_data), rather than having the Block
module simply output an HTML 
containing the data. This allows your theme to override the default block.tpl.php theme template file, replacing the default
with different HTML if desired. In Drupal 8, the process is slightly different and the default template file is block.html.twig, but eventually, the same theme call is made internally, and your theme can override the Twig template file. Theme Functions and Templates Versus Render Arrays There is often confusion around the relationship between theme functions or templates and render arrays. The basic idea is that each element in a render array is rendered (converted into an HTML string) by a theme function or theme template. Modules that you write should follow the same principle: they should generally return render arrays from functions that provide block and page output, and define custom theme functions or templates as needed (as described in this section), to ren‐ der the data into HTML, rather than defining the HTML markup directly. This way, the theme system can override your module’s theme functions or templates and retain control of the markup. Here is an outline of the steps: 1. See if there is already a theme hook or render element for the type of output you are generating. For instance, if you are generating an HTML table, you should use the Drupal core 'table' theme hook instead of defining your own hook. If a theme hook or render element already exists, skip the rest of this section; a good way to discover this is to find a function that generates similar output in Drupal core and see how it’s being done. 2. If there is not already an appropriate theme hook or render element, define a cus‐ tom theme hook by implementing hook_theme() in your mymodule.module file. For example (for either Drupal 7 or 8): function mymodule_theme($existing, $type, $theme, $path) { return array( // The array keys are names of the theme hooks you are defining. 'mymodule_hookname' => array( // Input variables. 'variables' => array( // These are passed as an array to theme(), which passes them on // to your theme function or template. Here, provide default values. 'input1' => '', ), 26 | Chapter 2: Drupal Programming Principles // If you want to use a template, include this line; for a theme // function in Drupal 7, leave it out. 'template' => 'mymodule-hookname', ), ); } 3. Define a default implementation of your theme hook in your module. If your theme hook is named 'mymodule_hookname', this is either a theme function called theme_mymymodule_hookname() or a theme template file called mymodulehookname.tpl.php in Drupal 7, with templates being preferred. In Drupal 8, it should be a Twig template file mymodule-hookname.html.twig, which needs to go in the templates subdirectory of your module. Template files should print the data in appropriate HTML markup; theme functions should compose a string con‐ taining the data and HTML markup and return that. For example: // Sample theme function (avoid theme functions in Drupal 8): function theme_mymodule_hookname($variables) { return '
' . $variables['input1'] . '
'; } // Same output as a PHP template file for Drupal 7:
// Same output as a Twig template file for Drupal 8:
{{ input1 }}
4. If you are using a PHP theme template in Drupal 7, declare it in your mymod‐ ule.info file with a files[] = mymodule-hookname.tpl.php line. This is not nec‐ essary in Drupal 8. 5. Try to keep the programming in your theme function or template file to a minimum—it should just be printing the output inside some HTML markup. Any programming logic should ideally be done before the theme function is called, or if it is only related to formatting, it should be put into a function called template_preprocess_mymodule_hookname(), defined in your mymodule.mod‐ ule file. This function will automatically be called to preprocess the input data into the variables that can be printed by the template file. 6. Call theme('mymodule_hookname', $data) directly in your module (Drupal 7 only), or set up a render array (preferable) to render the data. If you are calling directly, $data is an associative array of the variable inputs defined in your hook_theme() implementation; if calling indirectly, you can set the #theme ele‐ ment in the render array to 'mymodule_hookname': // Direct call of theme() -- Drupal 7 only. $data = array('input1' => t('Hello World!')); $output = theme('mymodule_hookname', $data); Principle: Drupal Is Alterable | 27 // Indirect call of theme. $build['hello'] = array( '#input1' => t('Hello World!'), '#theme' => 'mymodule_hookname', ); $output = drupal_render($build); // Better to return $build instead. Further reading and reference: • Overriding theme hooks and implementing generic hooks: “The Basics of Mod‐ ule and Theme Hook Programming” on page 23. • The “Default theme implementations” (Drupal 7) or “Theme system overview” (Drupal 8) topic on https://api.drupal.org lists all Drupal core theme functions and template files. • “Creating Render Arrays for Page and Block Output” on page 102. • For more advanced theming, you can also define your own render element type, which would include a theme hook. In Drupal 7, this is done with hook_element_info(). Drupal 8 uses render element plugins; see the \Drupal \Core\Render\Element\ElementInterface documentation for details. Examples—setting up theme hooks: • “Defining a Content Entity Type in Drupal 8” on page 139 • The Theming example in Examples for Developers The Basics of Drupal 8 Plugin Programming The plugins concept was introduced to Drupal in Drupal 8. Plugins allow modules to define things like entity types, fields, blocks, text filters, etc.; in Drupal 7 and earlier versions, the hook system was used for these purposes. Here is an overview of how the plugin system works: • A Drupal core or contributed module can define a plugin type. The plugin type consists of (usually) a PHP interface, a discovery mechanism, and (usually) a PHP base class. In most cases, the discovery mechanism is based on annotation that is added to the documentation header of plugin classes. • The module defining the plugin type uses a plugin manager class to collect plugin implementations from other modules and instantiate their classes. • Other modules can define implementations of the plugin type by creating a PHP class that implements the plugin interface, is annotated using the annotation scheme, and (optionally) extends the base plugin class. The class needs to have a namespace and be properly placed for autoloading. If the discovery mechanism 28 | Chapter 2: Drupal Programming Principles uses a hook or other mechanism, the plugin module will also need to implement a hook or perform other steps, and class annotation will not be necessary. The remainder of this section describes in more detail these three parts of the plugin system. Even if you only intend to implement plugin types defined by other modules, read through the sections on defining plugin types and plugin managers, because there are concepts and data defined there that you will need when you implement your plugin. Further reading and reference: • “Automatic Class Loading in Drupal” on page 12. • “Setting Up a Module or Theme” on page 20. • This section concentrates on the annotation-based plugin system, which is the system that most Drupal plugins use. You can read about other discovery mecha‐ nisms in more depth in the Plugin API documentation section at http://bit.ly/ plugin_api. • Find documentation for classes and interfaces mentioned in this section on https://api.drupal.org. Also, the “Annotations” topic for Drupal 8 lists all of the Drupal core plugin types that use annotations and describes how to annotate them. Defining a plugin type To define a plugin type, assuming you want to use the annotation-based discovery mechanism, your module will need to define a PHP interface and an annotation class. As an aid to modules that are implementing your plugin type, it is also desirable to define a base PHP class (which is often abstract) that provides helper methods and implements the plugin interface’s methods in the manner that most implementing plugins would want to use. The purpose of the interface is to define how your plugin-defining module will inter‐ act with the implemented plugin classes. As an example, the Drupal core Block sys‐ tem defines a plugin type that other modules can use to define blocks that an administrator can display on the site. An implementing block plugin needs to define who can see the block, what the block should display, and optionally, a configuration form. Accordingly, the Block system defines the following interface for block plugins (slightly simplified): namespace Drupal\Core\Block; interface BlockPluginInterface { // Define who can see the block. public function access(); Principle: Drupal Is Alterable | 29 // Output the block. public function build(); // There are also methods related to configuring the block, // returning the block title, and suggesting a machine name. } The \Drupal\Core\Block\BlockBase abstract class defines a default constructor, a default access() method that checks the visibility settings on the block, and a few more helper methods, but leaves the build() method undefined (there is no point to defining a default block output, because each block plugin presumably would have different output). Plugins are not required to extend the base abstract class, but they are required to implement the interface. The annotation for a plugin is a set of static data that the plugin-defining module delineates in an annotation class, and that implementing plugins provide in a docu‐ mentation header. This data must include a unique machine name or ID for the plu‐ gin implementation. If that is the only data a plugin type needs, the annotation class can just be a one-line extension of the simple \Drupal\Component\Annotation \PluginId class. If you need more data in your annotation, extend \Drupal \Component\Annotation\Plugin and include member variables for the other data. In the case of Block plugins, the annotation class is \Drupal\Core\Block\Annotation \Block, and it looks like this: /** * Defines a Block annotation object. * * @Annotation */ class Block extends Plugin { /** * The plugin ID. * * @var string */ public $id; /** * The administrative label of the block. * * @var \Drupal\Core\Annotation\Translation */ public $admin_label; // There are a couple of additional member variables. } 30 | Chapter 2: Drupal Programming Principles Note that if you are defining your own plugin type, you will need to be sure to include the documentation block before the class, with a line saying @Annotation. This tells Drupal that your class is an annotation class. It is also a good idea to document each member variable in your annotation class, in a manner similar to the example shown here, so that modules implementing your plu‐ gin will know what data type is expected, and whether it is translatable data or not. Any string information that will be presented to site administrators or other users should be translatable; machine names should not be translatable. Creating a plugin manager Once your module has defined a plugin type, you need to define a plugin manager class to discover and list the plugin implementations from other modules. Here are some examples of how Drupal core uses plugin managers: • The Block system uses a plugin manager that lists available blocks defined by modules, so that administrators can configure which blocks to display where on the site. • The Field UI module uses a plugin manager that lists available field types defined by other modules, so that administrators can attach these fields to content types and other entities, and configure their data, forms, and display. • The Image module uses a plugin manager that lists available image effects, so that administrators can define image styles that apply effects to images. The steps to define and use a plugin manager are as follows, assuming an annotationbased plugin discovery method. First, in your mymodule.services.yml file, define a service for managing your type of plugin. Defining a service means that you are declaring a class that you would like to use for managing this type of plugin and declaring the arguments that will be used to instantiate this class (which are usually other services). For example, the core Block system has this in core/core.services.yml: services: plugin.manager.block: class: Drupal\Core\Block\BlockManager parent: default_plugin_manager default_plugin_manager: abstract: true arguments: ['@container.namespaces', '@cache.discovery', '@module_handler'] Note that arguments starting with @ are names of other services that are defined by the Drupal core or modules. Principle: Drupal Is Alterable | 31 The second step is to define the manager class that you declared in your services file, usually extending \Drupal\Core\Plugin\DefaultPluginManager. Most plugin man‐ ager classes have only a constructor method and leave the rest to the base class; some plugin manager classes have other methods that they need for their particular appli‐ cation. The Block plugin’s manager class looks like this (slightly simplified): class BlockManager extends DefaultPluginManager { public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) { // Call the parent constructor to set up plugin discovery. parent::__construct('Plugin/Block', $namespaces, $module_handler, 'Drupal\Core\Block\Annotation\Block'); // Invoke hook_block_alter() to let modules alter the list of // discovered plugins. $this->alterInfo('block'); $this->setCacheBackend($cache_backend, 'block_plugins'); } // There are several other methods and member variables. } Plugin Data Needed by Plugin Implementers If you are implementing a plugin, you will need to figure out the annotation class, plugin interface, plugin base class, and plugin namespace. The Drupal core plugin types all have this information in the documentation header of the annotation class, so start there; if you are defining your own plugin type, find a Drupal core example and put similar documentation in your annotation class for developers to use. If you can’t locate the information you need in the annotation class’s documentation header, the next starting point is to find the plugin manager class, whose constructor method contains most of this information. The first code line of the plugin manager constructor method is particularly rich in information: parent::__construct('Plugin/Block', $namespaces, $module_handler, 'Drupal\Core\Block\Annotation\Block'); The first argument to the parent constructor gives the plugin subdirectory where you will need to put your plugin, and by implication, the namespace. In this example, the subdirectory is Plugin/Block, meaning that block plugins need to be in the src/Plugin/ Block subdirectory under the main implementing module directory, and correspond‐ ingly, their namespace needs to be \Drupal\mymodule\Plugin\Block. The last argument gives the name of the annotation class for this type of plugin. 32 | Chapter 2: Drupal Programming Principles Also, some modules may want to alter other modules’ plugin definitions. They can do that by implementing an alter hook, which you’ll also find in the constructor: $this->alterInfo('block'); This says that the hook is called hook_block_alter(), and it will be passed an array of discovered block plugin definitions, whose keys will be the block plugin IDs and whose values will be the annotation information from each defined plugin. Once you have defined your plugin manager class, the last steps are to instantiate and use the class. To instantiate a plugin manager class, call the get() method on the Drupal container object, passing in the name of the service that you defined. For example, in \Drupal\block\BlockListBuilder::createInstance(), the container object is in local variable $container, and there is a call to $container->get('plugin.manager.block') to instantiate a block manager class, which is then stored in the $blockManager class property in the constructor. Then you can call methods on the plugin manager. For instance, on the same class, the buildForm() method calls: $plugins = $this->blockManager->getSortedDefinitions(); foreach ($plugins as $plugin_id => $plugin_definition) { // ... } And several other places in Drupal core, you can find calls to: $this->blockManager->clearCachedDefinitions(); This tells the block plugin manager to clear its cache of plugin definitions, because something has happened that affects the plugin definition data (such as when the \Drupal\block_content\Plugin\Block\BlockContentBlock plugin is used by an administrator to add or update a custom block). Further reading and reference: • “Drupal 8 Services and Dependency Injection” on page 35 Implementing a plugin in a module A module that implements a plugin type needs to define a class that implements the defined plugin interface, with the defined plugin annotation added as a documenta‐ tion block immediately before the class declaration. Here are the steps to follow: 1. You’ll need some information before you start—see “Plugin Data Needed by Plu‐ gin Implementers” on page 32. You’ll want to start by finding the annotation class or the plugin manager class. You can discover available annotation classes by Principle: Drupal Is Alterable | 33 looking at the “Annotations” topic on https://api.drupal.org, or searching the code base for classes with @Annotation in their documentation header. Plugin manag‐ ers generally extend \Drupal\Core\Plugin\DefaultPluginManager, which should help you find them if necessary. 2. In the example of a Block plugin, the interface is \Drupal\Core\Block\Block PluginInterface, the base class is \Drupal\Core\Block\BlockBase, the annota‐ tion class is \Drupal\Core\Block\Annotation\Block, and the namespace sub‐ directory is Plugin\Block. For most plugins, the namespace is something like Plugin\(type), but different plugins use different conventions for (type), and some use subdirectories. 3. Define a class in your module’s namespace that implements the interface (the easiest way is usually to extend the base class) and is annotated using the defined annotation scheme, in the correct namespace. The annotation goes into a docu‐ mentation block directly before the start of your class, and looks like this: /** * Provides a block for (whatever your block does). * * @Block( * id = "mymodule_myblockname", * admin_label = @Translation("Admin label for my block"), * ) */ class MyModuleMyBlockName extends BlockBase { ... Notes: • Keys in the annotation array that match the member variable names in the anno‐ tation class are providing overrides of default values. • Annotation syntax is similar to PHP array syntax, but it has a few differences: — In place of => for a PHP array key/value mapping, use =. — The keys are not quoted. However, if an annotation value is itself an array, the keys of that array are quoted, and the array is enclosed in curly brackets. — Only use double-quotes in values. Single quotes will not work. • If you are providing a translatable string in annotation, wrap it in @Translation(). Examples—implementing plugins: • “Registering a Block in Drupal 8” on page 101. • “Defining a Content Entity Type in Drupal 8” on page 139 (has example of arrays in annotation). 34 | Chapter 2: Drupal Programming Principles www.allitebooks.com • “Defining a Configuration Entity Type in Drupal 8” on page 149 (has example of arrays in annotation). • “Defining a field widget in Drupal 8” on page 165. • “Defining a field formatter in Drupal 8” on page 169. • In Drupal core, class files under core/modules/*/src/Plugin/* are either plugin implementations or plugin type definitions. Drupal 8 Services and Dependency Injection Basic concepts: Services, Containers, and Dependency Injection Drupal version 8 adopted the concept of services from the Symfony project. The basic idea is that the functionality of Drupal should be separated into separate classes, each providing a self-contained chunk of functionality known as a service. For instance, Drupal core has a service for translating user interface text, several services for caching different types of information, and a service for providing aliases for internal URL paths. Each service has a machine name, such as string_translation, cache.default, or path.alias_manager. Closely allied with the services concept is the dependency injection container, or con‐ tainer. Again, this comes from the Symfony framework, which defines the \Symfony \Component\DependencyInjection\ContainerInterface interface. The container is the central place for retrieving the classes that provide services, via its get() method. The top-level \Drupal class has a getContainer() method to retrieve the container class, a service() method to retrieve a single service, plus several helper methods for retrieving commonly used Drupal services. If you write code that uses functionality provided by a service, you should always make sure to get the service via either service location (calling ContainerInterface::get() or methods on the \Drupal class), or dependency injec‐ tion. Dependency injection can be done in two ways: • If your code is itself defining a service, you can inject dependencies on other services via the arguments component of the service definition in the *.serv‐ ices.yml file. The next section shows how to do this. • Many base classes and interfaces in Drupal have either create() or createInstance() methods that are called by default manager and factory classes to create instances of these classes. The methods have a $container argu‐ ment passed in; you can call $container->get() in your override of this method to obtain a service class and store it in a member variable on your class. Principle: Drupal Is Alterable | 35 Examples—using services and dependency injection: • “The Drupal Cache” on page 10 • “Simple configuration API in Drupal 8” on page 45 • “State API in Drupal 8” on page 47 • “Internationalizing User Interface Text” on page 49 • “Drupal core’s main permission system” on page 64 • “Registering for a URL in Drupal 8” on page 92 • “Generating Paged Output” on page 107 • “Basic Form Generation and Processing in Drupal 8” on page 114 • MyEntityTypeDeleteForm from “Defining a Configuration Entity Type in Drupal 8” on page 149 • “Querying and Loading Entities in Drupal 8” on page 160 Defining a service In your modules, you can define services by putting them in a mymodule.services.yml file in your module’s main directory. Each service definition starts with the machine name, and subsequent lines contain data about the service, such as the default class that provides the service, and arguments (usually other services) for the class con‐ structor. For example (from core/core.services.yml, with some arguments omitted): services: path.alias_manager: class: Drupal\Core\Path\AliasManager arguments: ['@path.alias_storage', '@path.alias_whitelist'] The other part of providing a service is to define the default class, and normally also an interface that defines the methods that overriding classes need to implement. Examples—service defining: • “Creating a plugin manager” on page 31 • “Altering Routes and Providing Dynamic Routes in Drupal 8” on page 98 • The classes listed in the central core/core.services.yml file or various Drupal core modules’ *.services.yml files Service tags Some services are tagged with service tags in their service definition. For instance, all of the cache bin services are tagged with cache.bin: 36 | Chapter 2: Drupal Programming Principles # From the core/core.services.yml file: cache.default: class: Drupal\Core\Cache\CacheBackendInterface tags: - { name: cache.bin } In this case, the tag allows Drupal’s cache mechanism to locate available cache bins and use them appropriately; if your module adds a new cache bin, it will need to tag its service definition. See “Drupal 8 Cache API” on page 11 for more on the cache system. Overriding services The final concept around services is that they are alterable: the services.yml file defines the default class for a service, but a module can override this choice by pro‐ viding a different class to be used instead. If you want to do this, here are the steps. First, you will need to define a class to use as your alternative service provider. You will probably want to extend the default service provider class and override methods whose functionality you want to change, so that you can be sure that all needed meth‐ ods are present. Put this class into your module’s namespace. Next, define a class that implements the \Drupal\Core\DependencyInjec tion\ServiceModifierInterface interface. This class must have a specific name to be automatically recognized by Drupal as a service modifier: the name must start with a CamelCase version of your module’s machine name, followed by ServicePro vider. For instance, if your module’s machine name is foo_bar, then your service modifier class must be called FooBarServiceProvider. Also, it must be in the toplevel namespace for your module (in this case, \Drupal\foo_bar), and therefore it must be located in the src/FooBarServiceProvider.php file under your main module directory. (In contrast to the other examples in this book, I used foo_bar instead of mymodule for the machine name of the module, to illustrate what happens in the com‐ mon case of a module with an underscore in its machine name.) There is only one method in this interface: alter(). Here is a simple example: public function alter(ContainerBuilder $container) { $definition = $container->getDefinition('name.of.service'); $definition->setClass('Drupal\mymodule\MyNewServiceClass'); } Further reading and reference: • “Automatic Class Loading in Drupal” on page 12. • If you’re interested, here are some additional details on the internals of this pro‐ cess. First, \Drupal\Core\DrupalKernel::discoverServiceProviders() is what actually makes a list of classes with the right names and namespaces, Principle: Drupal Is Alterable | 37 as well as the list of services.yml files. Then \Drupal\Core\DependencyInjection \Compiler\ModifyServiceDefinitionsPass::process() is what checks to see if the classes implement ServiceModifierInterface, and if so, calls their alter() methods. Rebuilding the container As you are developing code for Drupal 8, if you make changes to plugin annotations, routing definitions, administrative links, and other similar aspects of your PHP code and YAML files, you’ll need to do an operation known as “rebuilding the container” in order to get Drupal to recognize your changes. The reason is that Drupal parses this information and caches it in PHP files or the database; this information isn’t cleared during a normal cache clear. There are two ways to trigger a container rebuild: • At the command line, use the drush cr command, if you have the Drush devel‐ opment tool installed. • From the browser, navigate to example.com/core/rebuild.php. Further reading and reference: • “The Drupal Cache” on page 10 • “Drupal Development Tools” on page 204 Interacting with the Drupal 8 Event System The Symfony framework defines the concept of events. Each event is identified with a unique name; events are dispatched by the Symfony framework during the HTTP response process, as well as by other Drupal components and modules. Drupal mod‐ ules can set up event subscribers, which are classes that register to take action when particular events are dispatched. To define and dispatch an event: 1. Choose a name for your event, which must be unique. 2. Define a class constant whose value is the name of your event. 3. Define a class to contain the data relevant to the event, which must extend \Symfony\Component\EventDispatcher\Event. 4. In your event-dispatching class or code, call the dispatch() method on the event_dispatcher service. The first argument is your event name, and the sec‐ 38 | Chapter 2: Drupal Programming Principles ond is an event data object, which you’ll need to instantiate with the necessary data. To respond to an event: 1. Create an event subscriber class, which implements \Symfony\Component\Event Dispatcher\EventSubscriberInterface. 2. Define the getSubscribedEvents() method in your class, which specifies which events you are subscribing to, and which methods on your class to call for each one. Alternatively, there are base classes available for some specific types of event subscribers, which may implement this method for you but require you to imple‐ ment other methods. 3. Write the individual methods that will respond to the events. Their single param‐ eter will be the data object from the event dispatch. 4. Define a service in your mymodule.services.yml file, event_subscriber and referencing your class. For example: tagged with services: mymodule.subscriber: class: Drupal\mymodule\My\Class\Name tags: - { name: event_subscriber } Further reading and reference: • “Drupal 8 Services and Dependency Injection” on page 35 • The “Events” topic on https://api.drupal.org, which includes a list of defined events • Symfony’s documentation on events: http://bit.ly/eventdispatcher Examples—event responding: • “Altering Routes and Providing Dynamic Routes in Drupal 8” on page 98 Principle: Drupal Separates Content, Configuration, and State Data Any website built with a content management system has data that can (at least, in principle) be separated into three types of information: Principle: Drupal Separates Content, Configuration, and State Data | 39 Content The text, images, files, and other information that is being presented to the site visitor. Content information can be further separated into types (e.g., the text of sidebar blocks, the text and images in the main body area of site pages, and the comments on a blog), each of which has its own set of properties (or fields). On many sites, some content information is open to editing and creating by nonex‐ pert or even anonymous site users. Content tends to change often and grow over time. Configuration Information about the site structure and settings; defines how to collect the con‐ tent information and present it to the site visitor. In Drupal, this includes your content type settings, field settings, views, and so on. Configuration information tends to be fairly stable over time and only change when new features or func‐ tionality are being added to the site; it is usually managed by a site administrator. Some configuration data can be separated into types with properties in the same way as content (e.g., content type settings are one type, and views are another); other configuration data is more atomic (e.g., the name of your site). State Information about the current state of the site. A good example of this type of information in Drupal is the time of the last cron job run. State information changes often. Separating the storage of configuration, state, and content information on a site is very useful, because it allows you to set up strategies for deployment, staging, and sharing of data among sites—generally, the information flows for content and config‐ uration are not the same, and state information is not desirable to share at all. So, in your programming with Drupal, it is a good idea to figure out whether the informa‐ tion you will be storing should be classified as content, configuration, or state before you start. The distinction is open to opinion in many cases, but you should make a determination and then use the appropriate mechanism to store your data. Information Storage in Drupal 7 Drupal 7, unfortunately, does not have a perfect system for separating configuration, state, and content information. It is mixed together in the database, so the system does not lend itself easily to deployment strategies: • Simple configuration information and most state information is serialized and stored in the variable table in the database, using the variable_set() and variable_get() functions. • The simple configuration information in the variable table can be overridden by $conf entries in the settings.php file. 40 | Chapter 2: Drupal Programming Principles • Complex configuration information is stored by individual modules in the data‐ base in a custom manner of their choosing. • Content is ideally stored using entities and fields; however, many modules instead use a custom database schema to store content information. Content does not generally have a universal ID that would facilitate synchronizing and sharing content among sites. • A site builder can export most configuration information to PHP code by using the contributed Features module. The Strongarm module also allows you to export simple configuration variable values using Features, if you prefer not to put them in your settings.php file. In principle, this allows site configuration to be shared among sites or deployed from staging to the live site. In practice, however, not all configuration information can be managed using this method, and this solution is not perfect. • Content information can be synchronized and shared among sites using the con‐ tributed Migrate module, or a combination of Views and Feeds modules; both methods require a significant investment in time and/or coding to set up. Further reading and reference: • See https://api.drupal.org for documentation of variable_* functions. • “Principle: Drupal Is Database-Independent” on page 54. • “Programming with Entities and Fields” on page 129. • Features module: https://www.drupal.org/project/features. • Strongarm module: https://www.drupal.org/project/strongarm. • Migrate module: https://www.drupal.org/project/migrate. • Feeds module: https://www.drupal.org/project/feeds. • Views module: https://www.drupal.org/project/views. Information Separation in Drupal 8 Although Drupal 8 still stores most of its information in the database, the configura‐ tion, state, and content information has a much cleaner separation than in previous versions of Drupal. At least, that is true if you use the Drupal APIs to access informa‐ tion, instead of attempting direct database queries. Here is an outline of the Drupal 8 information separation system: • The current configuration information for a site is stored in the site’s active con‐ figuration storage, either in the filesystem or in the database (database by default). Principle: Drupal Separates Content, Configuration, and State Data | 41 • Configuration information can be exported and imported via files, so you can share it with other sites. Configuration you are importing goes in the site’s stag‐ ing configuration directory; the location of this directory is defined in your set‐ tings.php file. • If file storage is being used for configuration information, it will be read from the active configuration directory (as configured in settings.php) and cached for effi‐ ciency (see “The Drupal Cache” on page 10). • There is a Configuration API in Drupal for modules to use to define, create, update, and read configuration data. This is described in “Configuration API in Drupal 8” on page 42. • State information is stored in the database, and there is a State API that modules can use to create, update, and read state data. This is described in “State API in Drupal 8” on page 47. • Content information is stored using entities and fields. See “Programming with Entities and Fields” on page 129 for more information. • Each content entity item has a universally unique identifier (UUID) which is unique across sites. Storing content in entities keeps it separate from configura‐ tion and state information, and this fact and the presence of UUIDs facilitate import, export, staging, and deployment strategies. Configuration API in Drupal 8 Drupal 8’s Configuration API consists of the following parts: • The native format for configuration data is YAML files, with defined schema. • Simple global configuration information, such as the name of the website, is stored and retrieved using Config objects. • Configuration information that has multiple copies, such as views, content types, and other definitions, is stored and retrieved using configuration entities. The following sections describe each of these parts of the configuration system. Configuration file format and schema in Drupal 8 Drupal 8’s configuration files are written in YAML format. Your extension (module, theme, or installation profile) can provide default configuration data in files in its config/install directory under the main extension directory. Some extensions put all their settings in a single myextension.settings.yml file; if it makes more sense to sepa‐ rate the settings into separate files, you can also use multiple subsystem files named myextension.subsystem_name.yml. Configuration files for configuration entities (views, content types, etc.) have specific naming conventions, which must be fol‐ 42 | Chapter 2: Drupal Programming Principles lowed. The configuration files in config/install will be imported when your extension is first enabled; changes to these files have no effect after this point. Some configuration has outside dependencies. For instance, configuration for a node content type depends on the Node module, and configuration for a view depends on the Views module. If your module provides configuration that depends on other modules, you have two choices: • You can make your module depend on the other module. In this case, put the dependent configuration in the config/install directory along with the rest of your configuration. • You can provide the configuration in the config/optional directory and avoid hav‐ ing a module dependency. When your module is installed, if the modules this configuration depend on are already installed, your configuration will be impor‐ ted into the site. Or, if those modules are installed later, your module’s config/ optional directory will be scanned again for configuration whose dependencies have now been met, and the configuration will be imported then. Within each YAML file, the configuration data is essentially an associative array, with keys and values, which can be flat or nested. For instance, your mymodule.settings.yml file might look like this: submit_button_label: 'Submit' name_field_settings: field_label: 'Your name' field_size: 50 You also need to provide a YAML-formatted configuration schema file, config/ schema/mymodule.schema.yml under your main module directory, which describes the data types of the configuration data items, as well as the labels they should have when configuration is being translated. In this example, the schema file would contain: # Schema for My Module configuration. mymodule.settings: type: config_object label: 'My module settings' mapping: submit_button_label: type: label label: 'Label for submit button' name_field_settings: type: mapping label: 'Settings for name field' mapping: field_label: type: label label: 'Label for name field' Principle: Drupal Separates Content, Configuration, and State Data | 43 field_size: type: integer label: 'Size of name field' Notes: • The top-level data type needs to be config_object for simple configuration like this. For configuration entities, it would be config_entity. • There are many other data type options, such as the boolean and float scalar types, and the path (a Drupal path) and text (a translatable string) complex types. • The label type holds a single-line plain-text translatable string. • The text type can contain multiple lines and HTML, and is also translatable. • The string type is also available, for nontranslatable single-line text. You can use this type for machine names. • The labels in the YAML schema file are used as an aid to translation. While it is not technically necessary to provide labels for nontranslatable configuration data, it is still a good idea for consistency and documentation. • Although the native format for configuration is YAML files, configuration is nor‐ mally stored in the Drupal database. • You can import and export YAML configuration information using the core Configuration Manager module UI. If you use the UI to export a single configu‐ ration, it will tell you the correct filename to use. This is useful in developing the configuration files to put in your module’s config/install or config/optional directory. • There is a hard limit on the length of a configuration filename: without the extension, it cannot exceed 250 characters. Further reading and reference: • Drupal configuration YAML files: http://bit.ly/drupal8_config_file • Drupal configuration YAML schema: http://bit.ly/config_schema • YAML: http://yaml.org • Another way to manage configuration www.drupal.org/project/config_devel during development: Examples—configuration schema files: • “Defining a Configuration Entity Type in Drupal 8” on page 149 44 | Chapter 2: Drupal Programming Principles https:// Simple configuration API in Drupal 8 Once you have a configuration file defined for your module, as described in “Config‐ uration file format and schema in Drupal 8” on page 42, you can use the simple con‐ figuration API to store and retrieve configuration information in code. When your module is installed, the values will be the defaults defined in your module’s config/ install and/or config/optional YML file or files; these can be overridden by an admin‐ istrator, presumably using a settings form supplied by your module. To use the configuration API, you start by getting a \Drupal\Core\Config\Config object from the config.factory service, and then you can use its methods to get, set, and save data. You’ll need to distinguish between getting an editable configuration object without overrides (the raw version), or an object with overrides suitable for display in the UI (read more about overrides later in this chapter). For example, assuming that mymodule.settings.yml is the name of your module’s configuration file, and it contains the data shown in the example in “Configuration file format and schema in Drupal 8” on page 42: // Code without dependency injection or $container variable: // - Get a non-editable version (with overrides): $config = \Drupal::configFactory()->get('mymodule.settings'); // - Get an editable version (without overrides): $config = \Drupal::configFactory()->getEditable('mymodule.settings'); // With dependency injection, or if you have a $container variable: $config = $container->get('config.factory')->get('mymodule.settings'); $config = $container->get('config.factory')->getEditable('mymodule.settings'); // Get the whole config array, and then extract one component: $all = $config->get(); $button_label = $all['submit_button_label']; // Or, if you only need this one component, one step: $button_label = $config->get('submit_button_label'); // Get another component: $name_field_info = $all['name_field_settings']; // Or, in one step: $name_field_info = $config->get('name_field_settings'); // Drill down: $name_label = $name_field_info['field_label']; // Or, in one step: $name_label = $config->get('name_field_settings.field_label'); Each of the preceding get() operations has a corresponding set() operation, such as: // Store changes locally in your editable config object: $config->set('submit_button_label', $new_label); Principle: Drupal Separates Content, Configuration, and State Data | 45 If you are changing settings, you also need to call the save() method at the end of the operation, in order to make your changes permanent: // Make the changes permanent: $config->save(); Further reading and reference: • Services background: “Drupal 8 Services and Dependency Injection” on page 35 Configuration entities in Drupal 8 As discussed in “Principle: Drupal Separates Content, Configuration, and State Data” on page 39, some configuration data can be separated into types. For example: • Content types, managed by the Drupal core Node module, which are a special case of content entity bundle configuration entities • Views, managed by the Drupal core Views module • Date formats, managed by the Drupal core System module This type of configuration data is managed by defining a configuration entity, rather than using the simple configuration API discussed in the previous section. The pro‐ cess of defining a configuration entity is described fully in “Defining a Configuration Entity Type in Drupal 8” on page 149. Configuration overrides in Drupal 8 The configuration system in Drupal 8 also has an override system, which allows con‐ figuration values to be overridden and translated. There are three types of overrides: global, language, and dynamic. When you are retrieving configuration values, if you use the get() method on a configuration object, you will get the value with overrides, and if you use the getEditable() method, you will get the value without overrides. The general principle is that when editing configuration, you should use the nonover‐ ridden value, and when using or displaying configuration, you should use the over‐ ridden value. Global overrides can be provided for a site by adding entries to the global $config variable in your settings.php file. For example, to override the submit button label in your main module settings from the example earlier in this section: $config['mymodule.settings']['submit_button_label'] = 'New label text'; Language overrides are provided by the Language module, and the Configuration Translation module allows users to translate their configuration. Dynamic overrides can be provided by modules by defining a service tagged with config.factory.override. 46 | Chapter 2: Drupal Programming Principles Further reading and reference: • More details about configuration overrides: http://bit.ly/config_override • Services background: “Drupal 8 Services and Dependency Injection” on page 35 State API in Drupal 8 To use the Drupal State API, start by getting the state object, which implements \Drupal\Core\State\StateInterface, from the state service: // Code without dependency injection or $container variable: $state = \Drupal::state(); // Dependency injection, or $container variable: $state = $container->get('state'); Next, if you have some module-specific state information to save, you need to choose a unique machine name for it; the convention is to prefix it with your module’s machine name. Then, you can save your state information by calling: $state->set('mymodule.my_state_variable_name', $value); To retrieve the value: $value = $state->get('mymodule.my_state_variable_name'); And if for some reason you want to completely remove the state information for that variable, perhaps because it is not relevant anymore due to a configuration change: $state->delete('mymodule.my_state_variable_name'); You can also set and get other modules’ state information, after locating the correct machine name of the state variable. For instance: • Setting node.node_access_needs_rebuild to TRUE indicates that the node per‐ missions tables need to be rebuilt. • A TRUE value for system.maintenance_mode indicates that the site is currently in maintenance mode. • The system.cron_last variable stores the time of the most recent cron run. Further reading and reference: • Services background: “Drupal 8 Services and Dependency Injection” on page 35 Principle: Drupal Separates Content, Configuration, and State Data | 47 Principle: Drupal Is International Drupal core and Drupal contributed modules and themes are, ideally, constructed so that their user interface elements use English by default but can be translated into other languages. Less universally (but still ideally), modules and themes are also constructed so that any user-entered text for configuration or content can be trans‐ lated. If both of these principles are followed: • You can build an English-language site. • You can build a site whose language is not English. • You can build a multilingual site. • You can use the translation mechanism to change the default English user inter‐ face text supplied by a module (for instance, changing the text on a button or link), without altering the module code. When you program for Drupal, even if you don’t think you will ever need to translate your site, it is still a good idea to follow this principle, because: • Markets and the reach of websites expand, and you might eventually need to translate your site. • You might decide to contribute the module on drupal.org so that others can use it, and some users will speak other languages. • It’s a good Drupal coding habit to get into, in case you ever want to contribute code to the Drupal project. • At least for built-in user interface text, it’s not very difficult anyway. Unicode Strings When programming with an international audience in mind, it is important to remember that not all text is ASCII—character sets for much of the world are instead multibyte Unicode characters. Some of the standard PHP string functions, such as strlen(), strtolower(), etc., do not take this into consideration and are not safe to use for multibyte characters. Instead, you can use Drupal’s multibyte-safe equivalent functions: drupal_strlen(), and so on in Drupal 7; these functions are wrappers to static methods on the core \Drupal\Component\Utility\Unicode class in Drupal 8. 48 | Chapter 2: Drupal Programming Principles Internationalizing User Interface Text The basic tool for internationalizing user interface text in the modules and themes you create is Drupal’s t() function. Any text that will be shown to an administrative user or a site visitor should be enclosed in t(); before the text is printed, Drupal will translate it to the appropriate language. For instance: // Bad: $button_text = 'Save'; // Good: $button_text = t('Save'); For translation to work properly, the first argument of t() must always be a literal string—it cannot contain variables. (This is because the first arguments to t() are extracted from code to build the database of strings that need translation.) If you need to substitute variable information into your string, t() has a mechanism: // Bad: $message_string = t("Hello $user_name"); // Good: $message_string = t('Hello @user_name', array('@user_name' => $user_name)); As you can see, it doesn’t really take much effort to make basic module-defined or theme-defined user interface text translatable. Notes on internationalizing user interface text: • There are additional Drupal functions you can use to internationalize numbers, dates, and JavaScript text; these are collectively known as the Drupal Localization API. There is an “Internationalization” topic on https://api.drupal.org for Drupal 8 that gives examples of these functions, plus some other strings that are auto‐ matically internationalized. • There is a Drupal.t() function that is the equivalent to t() for use in JavaScript code, in both Drupal 7 and Drupal 8. • In Drupal 7, there is an st() function for use in contexts where the full Drupal localization system is not available (such as during installation). • In Drupal 8, string translation is technically provided by the string_translation service; many classes have a t() method, which supports dependency injection. If a base class you are using has a t() method, use it rather than calling the t() function. There is no need in Drupal 8 for an st() function. • If you are defining a class in Drupal 8 and the base class does not have a t() method, use \Drupal\Core\StringTranslation\StringTranslationTrait to add it. Principle: Drupal Is International | 49 • In Drupal 8, you will also need to use the @Translation annotation when defin‐ ing a plugin, in order to make its text strings translatable. Further reading and reference: • Find Drupal localization functions on https://api.drupal.org listed in the “Format‐ ting” and “Sanitization functions” topics. The “Internationalization” topic for Drupal 8 also has some useful information. • Read more about the Localization API at https://www.drupal.org/developing/api/ localization. • Annotation: “The Basics of Drupal 8 Plugin Programming” on page 28. • “Drupal 8 Services and Dependency Injection” on page 35. • Traits are new to PHP 5.4. See http://php.net/manual/language.oop5.traits.php. Internationalizing User-Entered Text in Drupal 7 If you are building a module that has user-entered settings or user-entered text that is then displayed to other users, and you want your module to be fully internationalized so that it can be used on a multilingual site, you need to provide a way for the user-entered text to be translated. The t() function only works for literal text strings, so it cannot be used for this purpose. Unfortunately, in Drupal version 7 there is no Drupal core API for translating userentered text in a uniform way. But there are several methods that can be used to make your user-entered text translatable: • Store user-entered text in individual Drupal variables, using the variable_get() and variable_set() functions. If each string of user-entered text is stored in its own variable, and the variables are declared using the contributed Variable mod‐ ule’s hook_variable_info(), site builders can use the contributed Internationali‐ zation module to translate this text. This is most appropriate if you just have a few administrator-entered text strings that need to be translated. • For settings that form a list, use a Drupal core taxonomy vocabulary to manage the list instead of managing it in your own module. The taxonomy terms can then be translated using the contributed Internationalization module. To set up a vocabulary, you will need to add the following code to your module’s hook_enable() implementation in the mymodule.install file (mymodule_enable() function): // Create the vocabulary if it doesn't already exist. $vocabulary = taxonomy_vocabulary_load(variable_get('mymodule_vocabulary', 0)); if (!$vocabulary) { 50 | Chapter 2: Drupal Programming Principles $vocabulary = (object) array( 'name' => t('Some appropriate name'), 'machine_name' => 'mymodule_appropriate_name', 'description' => t('Some appropriate description'), 'module' => 'mymodule', ); taxonomy_vocabulary_save($vocabulary); variable_set('mymodule_vocabulary', $vocabulary->vid); } • For more complicated collections of settings, or content to be displayed on the site, use a node with fields to store the information, or define a custom Drupal entity with fields to store your settings. The collections of settings can then be translated using the Entity Translation module. Further reading and reference: • General information about programming with hooks: “The Basics of Module and Theme Hook Programming” on page 23 • Defining entity with fields: “Defining an Entity Type in Drupal 7” on page 132 • Internationalization module: https://www.drupal.org/project/i18n • Variable module: https://www.drupal.org/project/variable • Entity Translation module: https://www.drupal.org/project/entity_translation Internationalizing User-Entered Text in Drupal 8 The situation for internationalizing user-entered text in Drupal 8 is much better than in Drupal 7. There are two methods, depending on the nature of the text: whether it is considered configuration or content information. Content text in Drupal 8 should be stored in fields on entities; either use an entity type provided by Drupal core (node, taxonomy, etc.), use an entity type provided by a con‐ tributed module, or define your own custom entity type. Once you have stored your user-entered data in an entity, it can be translated using the Content Translation mod‐ ule, which is part of Drupal 8 core. Configuration text in Drupal 8 can also be translated, as long as your module uses the core configuration system to store its configuration data. Configuration components of label and text types are translatable, and the core Configuration Translation module provides a user interface for translating this data. Further reading and reference: • “Principle: Drupal Separates Content, Configuration, and State Data” on page 39 Principle: Drupal Is International | 51 • “Programming with Entities and Fields” on page 129 • “Configuration API in Drupal 8” on page 42 Principle: Drupal Is Accessible and Usable The World Wide Web Consortium (W3C) defines accessibility as meaning that people with disabilities can perceive, understand, interact with, and contribute to a website, and its Web Accessibility Initiative (WAI) has many resources for learning about accessibility and testing websites for accessibility. The Drupal project has an active accessibility team, and one of Drupal’s guiding principles is that its administrative backend and its visitor-facing output should both be as accessible as possible. The Drupal accessibility team has helped to improve the accessibility of Drupal by maintaining documentation on accessibility, testing Drupal for accessibility prob‐ lems, and pushing the Drupal project to adopt industry-wide accessibility standards. Another principle that ideally guides web design is usability: the idea that websites’ user interfaces should be easy to use and learn. The Drupal project has an active usa‐ bility team, and a commitment to high usability of the Drupal administrative inter‐ face is one of Drupal’s guiding principles. The Drupal usability team has done usability studies and proposed changes that have greatly improved the usability of Drupal—there was a large usability push for Drupal version 7, and another for Dru‐ pal 8. Related to usability is a focus on mobile platforms: increasingly, websites are accessed through mobile phones, tablets, and other small devices rather than through desktop browsers, so for a website to be truly usable, all of its content and functionality needs to be mobile-friendly. Accordingly, Drupal 8 adopted a “mobile first” design philoso‐ phy for both its administrative interfaces and its public-facing output, with the aim of making sure that an administrator or site visitor on a mobile device would have a rich and fully functional experience. At times, usability and accessibility can be at odds; a mobile-first orientation can also be at odds with optimal desktop browser usability. However, there are usually designs that can be adopted that are reasonably good for all users. For instance, usability studies might show that a drag-and-drop interface for ordering menu items is much faster and more intuitive than an interface where you assign numerical weights to define the order, but blind users and users who have mobility limitations that pre‐ clude use of a mouse cannot use drag-and-drop interfaces, so the drag-and-drop interface has accessibility problems. To satisfy both usability and accessibility princi‐ ples, you can supply the drag-and-drop interface along with a link that lets users switch to the numerical weight interface if needed. In the mobile versus desktop realm, an interface that provides more information when you hover your mouse over an area might be quite usable for someone using a mouse on a standard computer, 52 | Chapter 2: Drupal Programming Principles but how do you “hover” on a mobile phone interface? In this case, the design philoso‐ phy would be to make sure this information is available in some other way for mobile device users. So, instead of thinking that usability and accessibility are incompatible, the guiding principle of combining usability and accessibility should be to make the user interface for a task as easy to use and learn as possible for users without mobility, sight, or other limitations, while providing alternatives that allow users with these limitations to be able to accomplish the task. In addition, when thinking about and testing for usability, different types and sizes of devices and browsers should be considered. Dru‐ pal aims to follow these principles, and you should also try to adopt them in your own programming. Here are some things you can do to improve accessibility and usability in your programming: • Familiarize yourself with the usability and accessibility guidelines of the Drupal project. • When providing a user interface in your own module, use the same user interface patterns as Drupal core (they have already been tested for usability and accessi‐ bility, and uniformity also means easier learning). • When adding an administrative page for your module, put it in an appropriate section of the existing administrative user interface hierarchy of Drupal core, so people can find it easily. • Test your user interfaces and theme for accessibility. • Make sure that all information you present is available in text format (not just in images or diagrams), so screen reader users can access it. Search engines also only index text information, so making your site accessible in this way will also help its visibility in search engines. • Make your theme and user interface designs adaptable to different screen sizes and magnifications. This will help with both usability on mobile devices and accessibility for people who need to magnify their screens. Further reading and reference: • World Wide Web Consortium (W3C): http://w3.org • Web Accessibility Initiative (WAI)—includes resources for accessibility testing and worldwide standards: http://www.w3.org/WAI/ • Drupal accessibility team: https://groups.drupal.org/accessibility • Drupal accessibility guidelines and resources: https://www.drupal.org/about/acces sibility • Drupal usability team: https://groups.drupal.org/usability Principle: Drupal Is Accessible and Usable | 53 • Drupal usability guidelines: https://www.drupal.org/ui-standards • Drupal mobile development guide: https://www.drupal.org/documentation/mobile Principle: Drupal Is Database-Independent Although most Drupal sites use MySQL (or a compatible clone) as the database back‐ end, Drupal version 7 (and later versions) can be used with a variety of databases. Drupal core supports MySQL, PostgreSQL, and SQLite (or some subset, depending on the Drupal version), and contributed modules add support for other databases, including at least partial support for non-SQL databases, such as MongoDB. Drupal core also provides several database-related customizations, such as the ability to prefix database table names with a string, or different strings for different tables, and to fall back to a different database if the default database is not available. In order to facilitate this portability, Drupal provides a Schema API and a Database API, which work together as a framework for defining and querying database tables. The Schema API has been fairly stable for several versions of Drupal. The Database API was completely overhauled for Drupal 7 and is based on the standard PHP PDO library; it is also sometimes referred to as DBTNG (Database, the Next Generation). Because Drupal is written in PHP, it is possible in your own programming to avoid using the Drupal Schema and Database APIs and instead use the PHP databasespecific query functions you may be familiar with to query the database directly. But this is not a good idea, because: • Using the Database API helps make your queries more secure from SQL injection bugs. • The Schema API is actually easier to use for creating and modifying tables than writing your own SQL queries, and the code is easier to read and maintain. The Database API takes an effort similar to writing your own SQL queries. • You might sometime want to switch to a different database for performance reasons. • You might sometime want to use different site set-up choices, such as setting up development and staging sites with different database options. • You might want to contribute your module to the Drupal open source project so that others can use it, and contributed code should be database-independent. • It’s a good habit to get into, in case you ever want to contribute code to the Dru‐ pal project. It should be noted that one of the best ways to make sure to stay independent of the database is to avoid direct use of the database entirely. For instance, instead of creat‐ 54 | Chapter 2: Drupal Programming Principles ing a module to store information in its own custom database table, consider whether you can use Drupal’s taxonomy, node, or entity system instead. Or perhaps you can use a contributed module, along with a custom plugin. Drupal 8 In Drupal 8, unless you are developing core systems, direct data‐ base queries are deprecated in most cases in favor of other APIs (entity system, configuration system, etc.), so if you find yourself wanting to use a database query, you should check to see if there is an API you should be using instead. For the most part, only these core API functions themselves should be making direct database queries. Further reading and reference: • The rest of this section has more details on the Database and Schema APIs. • Avoiding database programming entirely: “Mistake: Programming Too Much” on page 71. • Security concerns: “Principle: Drupal Is Secure; User Input Is Insecure” on page 61. • Drupal Schema API: https://www.drupal.org/developing/api/schema. • Drupal Database API: https://www.drupal.org/developing/api/database. • Modules providing alternative database integration: MongoDB (https:// www.drupal.org/project/mongodb), Microsoft SQL Server (https:// www.drupal.org/project/sqlsrv), Oracle (https://www.drupal.org/project/oracle). • Group for people interested in enterprise applications (including database inte‐ gration): https://groups.drupal.org/enterprise. Setting Up Database Tables: Schema API and hook_update_N() If your module needs to set up its own database tables, create them by implementing hook_schema() in your mymodule.install file. Drupal will then take care of creating the tables when your module is enabled, as well as deleting them when your module is uninstalled. For example, to define a database table called 'mymodule_foo' with fields 'bar' and 'baz': // This code works in Drupal 7 or 8. function mymodule_schema() { $schema = array(); $schema['mymodule_foo'] = array( 'description' => 'Untranslated description of this table', 'fields' => array( Principle: Drupal Is Database-Independent | 55 'bar' => array( 'description' => 'Untranslated description of this field', 'type' => 'varchar', 'length' => 50, 'default' => '', ), 'baz' => array( 'description' => 'Untranslated description of this field', 'type' => 'int', 'unsigned' => TRUE, 'default' => 0, ), ), 'primary key' => array('baz'), ); return $schema; } The Schema API is flexible enough to define all aspects of database tables, including fields of different types, indexes, and so on. See the Schema API documentation for details: https://www.drupal.org/ developing/api/schema. Once you have enabled a module and its database tables are created, you may find that you need to make a change to the database schema, such as adding a field, delet‐ ing a field, adding a new database table, and so on, to correspond to a new feature you have added to your module. The Schema API provides a standard way to do this: 1. Add a hook_update_N() implementation (also known as an update function) to your mymodule.install file, which will update the database using functions such as db_add_field(), db_create_table(), and so on. Update functions are named sequentially, and each builds upon the previous schema and updates. The com‐ ment directly before the function is shown on the Pending Updates page when you run the example.com/update.php script, so make this comment coherent and descriptive. For example, this function would change the length of the 'bar' field to 150 characters, and also add a new 'bay' field: /** * Make one field wider and add a new field in the mymodule_foo table. */ function mymodule_update_7001() { // This code works in Drupal 7 or 8. In Drupal 8, name the function // mymodule_update_8001, however! db_change_field('mymodule_foo', 'bar', 'bar', array( 'description' => 'Untranslated description of this field', 'type' => 'varchar', 'length' => 150, 56 | Chapter 2: Drupal Programming Principles 'default' => '', )); db_add_field('mymodule_foo', 'bay', array( 'description' => 'Untranslated description of this field', 'type' => 'varchar', 'length' => 50, 'default' => '', )); } 2. Edit your original hook_schema() implementation function, making correspond‐ ing changes to the schema. 3. Make changes in your module code to use the new schema. 4. If this is for a site you manage, run the update script by visiting example.com/ update.php. You will be presented with a list of pending updates, which should include the update function you just created and show the description from the function comment. Do not attempt to call Drupal Database API functions such as Dru‐ pal 7’s drupal_write_record() that rely on the schema from inside an update function, because the schema will be in an unknown state while the update function is running. Correspondingly, never reference your hook_schema() implemen‐ tation in an update function—always write out the full array values in your calls to db_change_field() and similar functions. The rea‐ son is that if you ever decide to share your module with someone else, you will not have control over when the updates are run, so you don’t know at the moment of running a particular update func‐ tion what the state of the schema in hook_schema() might be. Further reading and reference: • General information about programming with hooks: “The Basics of Module and Theme Hook Programming” on page 23. • Drupal Schema API: https://www.drupal.org/developing/api/schema. • Look up hook_schema and hook_update_N on https://api.drupal.org for full details of their return values. • The functions that update database tables, such as db_change_field(), can be found in the database.inc include file (in Drupal 7, this is in the top-level includes directory; in Drupal 8, it’s in core/includes). You can look this file up on https:// api.drupal.org to find a list of all its functions. Principle: Drupal Is Database-Independent | 57 Examples—setting up database schema: • In defining a Drupal 7 entity: “Step 2: Implement hook_schema()” on page 134 • The DBTNG example in Examples for Developers Querying the Database with the Database API If your module needs to query the Drupal database directly—whether querying its own tables or tables provided by Drupal core or another module—you will need to use the Drupal Database API to ensure that your queries are secure and portable. The Database API provides the db_query() function, which you can use to make simple queries, and a dynamic API that can be used for arbitrarily complex queries. Very simple queries For the simplest SELECT queries, you can use the Drupal db_query() function: // This exact query works in Drupal 7 only, because the table schema has // changed. $result = db_query('SELECT * FROM {users} u WHERE u.status = :status', array(':status' => $desired_status)); foreach ($result as $record) { // $record will be a PHP object with fields corresponding to the table fields. $user_name = $record->name; // ... } Notes: • The name of the database table being queried must be enclosed in {}. When Dru‐ pal runs the query, this table name will be prefixed as necessary. • Variable inputs to the query use placeholders, which start with : and should con‐ tain only letters (numbers, underscores, etc. will not work in all cases). The sec‐ ond argument to db_query() is an array giving the values of the placeholders. Never put variable inputs directly into your query strings, especially if they origi‐ nate in insecure user input. • If a placeholder is a string, do not enclose it in quotes in the query—the variable substitution will take care of adding the quotes as necessary. For instance: // Bad: "WHERE u.name = ':name'" // Good: "WHERE u.name = :name" • Only use db_query() for simple, static SELECT queries that you are certain will run on any database engine. If you have doubts about portability, or if the query 58 | Chapter 2: Drupal Programming Principles string needs to be built up dynamically using programming logic, use the dynamic query API described in the next section. • Some Drupal database tables have permission implications (for instance, the Node module has a rich permission system for restricting access to certain con‐ tent by certain users or roles). When querying such tables, do not use db_query(), because the query will need to be modified by Drupal to enforce the correct permissions. Use the dynamic query API or the entity query API instead. • Drupal also has a built-in pager system that greatly simplifies making multiplepage queries. You will need to use the dynamic query API to use this system. Dynamic queries For queries that involve paging, SQL functions such as LIKE, grouping, tables with access restrictions, or anything other than a SELECT, you will need to use Drupal’s dynamic query functions instead of the simple db_query() function. These functions allow you to build up a query in a database-independent way and then execute it to get the same type of result set returned by db_query(). For example: // Equivalent to: // SELECT title, nid, created FROM {node} n WHERE n.status = 1 // with node access enforced. // Only for Drupal 7. $result = db_select('node', 'n') ->addTag('node_access') // Enforce node access permissions. ->fields('n', array('title', 'nid', 'created')) // Fields to return. ->condition('n.status', 1) // WHERE condition. ->execute(); foreach ($result as $node) { // $node will be a PHP object with fields corresponding to the table fields. $title = $node->title; // ... } Notes: • This specific query will only work in Drupal 7, due to database table changes; however, the function and method call syntax is the same in Drupal 8. But do remember that direct database queries in Drupal 8 are usually a mistake unless you are developing Drupal core systems. • Unlike when using the simple db_query(), do not enclose table names in {}. • The addTag() method is used when you are querying a table with permissions considerations. For instance, the Node module has a complex permissions sys‐ Principle: Drupal Is Database-Independent | 59 tem, which is enforced for you in database queries if you add the 'node_access' tag to your query. Some query methods allow chaining, as illustrated in the previous example, because they alter the query in place and return the altered query object. Some do not: nota‐ bly, addField() and the join methods. If you use a nonchaining method, use syntax like this: // Equivalent to: // SELECT n.changed AS last_updated, n.title, n.nid, u.name FROM // {node} n INNER JOIN {users} u ON u.uid = n.nid WHERE // n.status = 1 // with a pager, 20 items per page, and node access enforced. // Drupal 7 only. $query = db_select('node', 'n'); $query->addField('n', 'changed', 'last_updated'); // Field with an alias. $query->innerJoin('users', 'u', 'u.uid = n.uid'); // Join. $query = $query->extend('PagerDefault'); // Paging. $result = $query ->fields('n', array('title', 'nid')) ->fields('u', array('name')) ->addTag('node_access') ->condition('n.status', 1) ->limit(20) // Number of items per page. ->execute(); Notes: • This query also only works for Drupal 7, because paging is different in Drupal 8 and database tables have changed. See the following text for the Drupal 8 version. • When using a PagerDefault query, as in the previous example, add a standard pager to your output by calling theme('pager'). This will let Drupal handle all the details of getting the right items on each page. In Drupal 8, when querying nodes and other entities, you should really use an entity query instead of the bare Database API. For purposes of illustrating the paging sys‐ tem, however, here is the Drupal 8 version of the previous query: // Equivalent to: // SELECT nd.changed AS last_updated, nd.title, nd.nid, u.name FROM // {node} n // INNER JOIN {node_field_data} nd ON n.nid = nd.nid AND n.vid = nd.vid // INNER JOIN {users_field_data} u ON u.uid = nd.uid WHERE // n.status = 1 // with a pager, 20 items per page, and node access enforced. // // DO NOT REALLY RUN THIS IN DRUPAL 8 -- use an entity query instead! // $query = db_select('node', 'n'); // Base table; two joins below. 60 | Chapter 2: Drupal Programming Principles $query->innerJoin('node_field_data', 'nd', 'n.nid = nd.nid AND n.vid = nd.vid'); $query->innerJoin('users_field_data', 'u', 'u.uid = nd.uid'); $query->addField('nd', 'changed', 'last_updated'); // Field with an alias. $query ->extend('Drupal\Core\Database\Query\PagerSelectExtender') // Add pager. ->limit(20) // 20 items per page. ->fields('nd', array('title', 'nid')) // More fields. ->fields('u', array('name')) ->addTag('node_access') // Enforce node access. ->condition('nd.status', 1); $result = $query->execute(); foreach ($result as $node) { // $node will be a PHP object with fields corresponding to the table fields. $title = $node->title; // ... } Further reading and reference: • Avoiding database programming entirely: “Mistake: Programming Too Much” on page 71. • Security concerns: “Principle: Drupal Is Secure; User Input Is Insecure” on page 61. • Drupal Database API: https://www.drupal.org/developing/api/database. • Detailed documentation for the database functions and classes can be found on https://api.drupal.org. Examples—dynamic queries: • “Querying and Loading Entities in Drupal 8” on page 160 • Paged queries and output: “Generating Paged Output” on page 107 • The DBTNG example in Examples for Developers Principle: Drupal Is Secure; User Input Is Insecure When programming for the web, you always need to think about security. One basic principle to follow is to consider all user-provided input to be insecure, whether it is provided by a trusted user such as a site administrator (who could be the target of hacking), a semitrusted user such as someone with a generic user account on your site, or an anonymous site visitor. With that in mind, whatever you do in your Drupal programming that involves user-provided input, that input will need to be cleansed or checked in some way to make it more secure before you use it to generate any out‐ put. There are also other standard web security concerns, such as preventing various types of hacking attacks, that you’ll need to keep in mind in your programming. Principle: Drupal Is Secure; User Input Is Insecure | 61 Besides these principles, which apply to all web programming, Drupal has an addi‐ tional security concern: your programming needs to respect Drupal’s permission sys‐ tem. For instance, although a module you write can include code to run arbitrary database queries, only information from the database that a particular user has per‐ mission to view should be shown to that user. And although a module you write can technically call any Drupal API function at any time, you should not call functions without checking that the user has permission to perform their actions. Both Drupal core and Drupal’s contributed modules and themes ideally follow these principles of cleansing user input, preventing hacking, and checking Drupal permis‐ sions. The Drupal project has a volunteer security team, which handles reports of security issues, and every contributor’s first module or theme is reviewed before it is allowed to be promoted to “full project” status on drupal.org. Your Drupal program‐ ming should also follow these principles, and the following sections give you an introduction to making your Drupal code more secure. You will also need to make sure your site permissions are reasonable and take other measures to set up a secure site. Further reading and reference: • Securing a Drupal site: https://www.drupal.org/security/secure-configuration. • The Drupal site building section of “Where to Find More Information” on page 199 lists additional resources. • “Mistake: Saving PHP Code in the Database” on page 81. Cleansing and Checking User-Provided Input The philosophy used in Drupal for ensuring security with user-provided input is to store whatever the user typed in the database without alteration, and then cleanse it prior to display. Both of these steps must be done carefully. In the database storage step, you need to be concerned about SQL injection attacks when queries include user-provided input: a malicious user could input specially con‐ structed text that would change your query so that it updates the database in ways you didn’t intend. If you use the Drupal Database API correctly, however, all userprovided input will either be put into the query using placeholders or as arguments to safe methods such as condition(), and the integrity of your database will be protected. In the output step, the Drupal API provides functions you can use to cleanse or escape data to make it safe for HTML output. Also, in Drupal 8, the rendering and theming process attempts to keep track of which input has been cleansed and which 62 | Chapter 2: Drupal Programming Principles hasn’t, and Twig templates will escape all HTML in output that is not marked as hav‐ ing been cleansed. Here are some examples of how you can cleanse data: • If you are outputting data that is supposed to be plain text (without HTML tags) in Drupal 7, you should pass it through the check_plain() function, which will escape the HTML tags. Drupal 8 keeps track of whether text is safe to use or not, and unless you pass it through a function such as t() that marks it as safe, the theme and render system will automatically escape text when it is printed out. If you are outputting data via JSON or another means, you will need to sanitize it. • If you are outputting data that is supposed to contain HTML tags (which should be limited for untrusted users), you should pass it through the check_markup() function, which will remove forbidden tags. • If you are outputting a user-provided URL, you should pass it through check_url(). Some Drupal API functions, such as l() or \Drupal::l() (for making links), and t() for internationalization, cleanse or escape input themselves, so read the function documentation and don’t double-escape. Examples: // Bad: $paragraph = '

' . $text . '

'; $link = '' . $text . ''; $link = l(check_plain($text), check_url($url)); // Good, Drupal 7 version: $paragraph = '

' . check_plain($text) . '

'; // The l() function sanitizes its input. $link = l($text, $url); // Good, Drupal 8 version: use Drupal; use Drupal\Core\Url; // This assumes $url is an external URL. $url_object = Url::fromUri($url); $link = Drupal::l($text, $url_object); Principle: Drupal Is Secure; User Input Is Insecure | 63 Further reading and reference: • Drupal Database API: “Querying the Database with the Database API” on page 58. • Drupal functions that cleanse data can be found on https://api.drupal.org under the “Sanitization functions” topic. • More about writing secure code: https://www.drupal.org/writing-secure-code. • “Mistake: Saving PHP Code in the Database” on page 81. Checking Drupal Permissions Drupal has a rich permission system, which your modules need to interact with prop‐ erly to ensure that users, including anonymous site visitors, are only allowed to see information and perform actions that they have permission for. There are several sys‐ tems for permission checking in Drupal core, and some contributed modules have their own permission systems. It is important to understand the permission systems of all modules that your module interacts with. Drupal core’s main permission system The main system for permission checking in Drupal core works as follows: • Modules define permissions that users can be granted, which allow them to per‐ form the module’s tasks or view the module’s information. • In Drupal 7, define permissions by implementing hook_permission() in your mymodule.module file, which goes in your main module directory: // Drupal 7: function mymodule_permission() { return array( // The array keys are the permissions' machine names. 'administer mymodule' => array( 'title' => t('Administer My Module settings'), // Human-readable name. 'description' => t('Longer description only if it is really necessary.'), ), // Define additional permissions by adding more array elements. ); } • In Drupal 8, define permissions in a mymodule.permissions.yml file, which goes in your main module directory: # Drupal 8: administer mymodule: 64 | Chapter 2: Drupal Programming Principles title: 'Administer My Module settings' description: 'Longer description only if it is really necessary.' • If you have dynamic permissions to define in Drupal 8, you can also define a permission_callbacks entry in your mymodule.permissions.yml file. The func‐ tions or methods listed return permissions in the same format. See the core/ modules/filter/filter.permissions.yml and core/modules/filter/src/FilterPermis‐ sions.php files for an example of how to do this. • In the Drupal user interface, site administrators can define roles and grant one or more permissions to each role. A user account can then be assigned to one or more roles, which grants all of the roles’ permissions to that user account. • When Drupal 7 code performs an action on behalf of a user, or displays informa‐ tion to the user, it either directly or indirectly calls user_access('permission machine name') to see whether the current user has the needed permission, and only performs the action or displays the information if that is the case. The Dru‐ pal 8 equivalent is that there is a current_user service, which will return an object implementing \Drupal\Core\Session\AccountProxyInterface; you can obtain this service in noncontainer code by calling \Drupal::currentUser(). On this object, the hasPermission() method checks whether the current user has a given permission. So, in your programming, you need to be aware of permissions that other modules define, and make sure to use the appropriate permission check when using that mod‐ ule’s functions. You also need to determine which actions your module defines that should be restricted, define appropriate permissions for them, and verify those per‐ missions appropriately. Luckily, in some cases Drupal will make the permission check for you, which makes this system pretty easy to use. For instance, when you are defining routing URLs with hook_menu() in Drupal 7, or in a routing.yml file in Drupal 8, you can define permis‐ sions for that URL, and Drupal will enforce them for you when someone goes directly to the URL, as well as when making lists of links for menus and other purposes. Dru‐ pal 8 entity definitions also include permissions for creating, viewing, and other actions, which the entity system enforces. Further reading and reference: • General information about programming with hooks: “The Basics of Module and Theme Hook Programming” on page 23 Examples—permission defining and checking: • URL registration permissions in Drupal 7: “Registering for a URL in Drupal 7” on page 89. Principle: Drupal Is Secure; User Input Is Insecure | 65 • URL registration permissions in Drupal 8: “Registering for a URL in Drupal 8” on page 92. • “Defining a Content Entity Type in Drupal 8” on page 139. • “Defining a Configuration Entity Type in Drupal 8” on page 149. • Several of the examples in Examples for Developers create or check permissions, such as the Cache example, the Tokens example, and the Menu example. Permissions and security in forms The Drupal Form API also has a permission and security system that you can take advantage of. This consists of the following components: • When you are defining a form array, each form element can be given a Boolean '#access' property—TRUE means the form element is displayed and usable, and FALSE means it is not accessible to the current user. When you are building a form, you can assign the result of an access check call to the '#access' property of an element, to programmatically show/hide form elements. • Assuming that you use the standard Drupal form functions to display and pro‐ cess the form, Drupal will protect against cross-site forgery form submissions by adding a unique token to the form and validating the token when the form is submitted. URL access permission will also be checked for the form submission URL. • You should also use confirmation forms to prevent hacking attacks that might trick an administrative user into visiting a URL that would maliciously destroy or alter data. For instance, if your module has a URL that triggers deleting a particu‐ lar database record, have that URL instead display a confirmation form, and only delete the database record if the action is confirmed. Further reading and reference: • “Using the Drupal Form API” on page 109 Examples—form security: • “Form Arrays, Form State Arrays, and Form State Objects” on page 110 • The Form example in Examples for Developers Permissions in displaying and operating on content Some Drupal core modules that manage content have complex permission systems that also need to be considered in your module programming. For instance, the core 66 | Chapter 2: Drupal Programming Principles Node module provides hooks that allow other modules to define permission systems for node content, which can be as simple as “Only allow users of role A to view node content of type B,” or as complex as allowing access to particular node content items only to certain individual users. If you are writing a module that allows users to oper‐ ate or view node content, you need to respect whatever permissions other modules may have defined. Similarly, if you are writing a module that deals with core taxon‐ omy terms, comments, or content managed by a contributed module, you need to make sure that your module is complying with the content permission scheme that applies. Luckily, modules that define complex content permission schemes have APIs that make obeying the permissions feasible, without knowing the details of the per‐ missions that are in place on a particular site. For example, if you are writing a Drupal 7 module that operates on individual node content items, you need to check access permission by calling the node_access() function, passing in the operation you are performing ('view', 'delete', etc.) and the node content item you are operating on. And if you are writing a module that queries the database to make a node content item listing, such as a block that lists content satisfying some criteria, you need to add the node_access tag to your data‐ base query. This will ensure that only items the user has permission to view are returned by the query, without having to guess at what particular permissions mod‐ ules might be in use. In Drupal 8, this is even easier: as long as you use entity queries to query entities, and the entity storage controller to load entities, view permission will be enforced. Per‐ mission for other actions can be checked using the access() method on an individ‐ ual entity object; this method is provided by the \Drupal\Core\Entity\Entity base class, so it exists on all entity classes. Further reading and reference: • “Programming with Entities and Fields” on page 129 • “Avoiding Custom Programming with Fielded Data” on page 75 Examples—content permission: • Adding tags to queries: “Dynamic queries” on page 59 • The Node Access and Entity examples in Examples for Developers Principle: Drupal Code Is Tested and Documented There was a large effort during the Drupal version 7 development cycle to adopt the SimpleTest automated web testing framework for Drupal core, and to adhere to the development principle that all major functionality should have automated tests. This Principle: Drupal Code Is Tested and Documented | 67 commitment has been continued and even strengthened in the Drupal 8 development cycle with the addition of the industry-standard PHPUnit framework for unit testing, and it is expected to continue in the future. To aid in following this principle, a team within the Drupal project maintains a group of servers to run automated tests. Before any proposed change is committed to the Drupal core source code, all of the existing automated tests must pass. Furthermore, if the change adds new functionality or fixes a bug, it must usually be accompanied by new tests to ensure that the new functionality works as expected, or that the bug is really fixed. Many contributed module projects on drupal.org have also adopted this principle, at least to some extent. A related effort in the Drupal project has been to improve the documentation of the code in Drupal core. The project has standards for in-code documentation, which include the principle that each distinct code item (function, class, file, method, etc.) should include a documentation header; these documentation headers are parsed to create the Drupal API reference site, https://api.drupal.org. Most of the Drupal 7 code is reasonably well documented, and for the Drupal 8 development cycle, a standard was adopted to say that no change should be committed without its accompanying documentation being complete, which at least made it a critical-priority bug if docu‐ mentation was omitted. Adopting the “everything should be tested” and “everything should be documented” principles in your own programming is an excellent idea. Writing formal documenta‐ tion headers before you start work on the code for a function or class is a great way to ensure that you’ve thought out what the function or class should do, and it will also help you or the next maintainer of the code to remember what the code was supposed to do. Writing README files, API documentation, and end-user documentation for your code is a great way to ensure that the user interface, API, installation procedure, and other aspects of your code design make sense (there’s nothing like trying to write coherent documentation for making you realize that something makes no sense or is incomplete). It’s also a great way to ensure that you or the next user know how to use or program with your code, and prevent people having to contact you with the same boring questions over and over…or at least if they do, you can politely tell them to read the existing documentation. In the arena of testing, writing tests for the functionality of your code does take time, and the testing framework takes some time to learn how to use. However, I have found that even basic tests usually find bugs in code that I would not otherwise be aware of, and having tests also greatly lessens the chance that feature additions or bug fixes added later will break existing functionality. Tests can also be considered to be another form of documentation, as they document the expected functionality of your code by testing that it performs the way it should. The testing frameworks in Drupal allow you to create unit tests (low-level tests of spe‐ 68 | Chapter 2: Drupal Programming Principles cific functions or classes) and functional tests (higher-level tests that can involve sim‐ ulating a browser and checking that pages and forms behave as they should); both can be appropriate for code that you write. I’ve also found tests to be very valuable when updating code from one major Drupal version to another, and when developing for a prerelease version of Drupal. For instance, before Drupal 7 was first released, I was already working on updating the contributed modules I maintain from Drupal 6 to Drupal 7. During my first pass, I had some extra work to do, because I had to port both the main module code and the tests I had written from Drupal 6 to Drupal 7. However, once I got to the stage when all of the tests passed, I was pretty confident that my module port had worked. Then, as Drupal 7 continued to evolve, I could rerun my tests and immediately know if any changes to Drupal 7 had affected my module. I also wrote tests for the code in this book. This was especially important when I started work on the Drupal 8 edition, because the Drupal API was still in quite a state of flux. So besides ensuring that the code worked correctly in the first place, having tests also meant that as Drupal 8 evolved, I could run the tests for the book against the latest Drupal 8 code, and when they failed (which happened many times), I just updated the book’s code until the tests passed again. This let me gradually evolve the book’s code as Drupal 8’s API solidified. I even wrote a test that verified that files, functions, classes, services, etc. that I mentioned in the book still existed, which was great insurance against the many reorganizations and name changes that occurred. There is really no way I could have written this edition of the book without the tests, and the code for both Drupal 7 and 8 would have had many more problems and bugs without the tests. Further reading and reference: • How to write and run automated tests: https://www.drupal.org/simpletest and https://www.drupal.org/phpunit. • Documentation standards: https://www.drupal.org/coding-standards/docs. • If you want to write tests for the Drupal project, look for issues tagged “needs tests” in a project that interests you (Drupal core or a contributed project). If you want to contribute API documentation to the Drupal project, look for issues in the “documentation” component in a project that interests you, or issues tagged “Needs documentation” or “documentation.” • The module that parses API documentation for https://api.drupal.org (you can use it to build your own API documentation site): https://www.drupal.org/project/ api. Examples—tests: Principle: Drupal Code Is Tested and Documented | 69 • If you download the code for this book from the book website (see “How to Con‐ tact Us” on page xii), it includes tests. These can be found in the tests directory under the Drupal 7 module directory, and src/Tests under the Drupal 8 module directory. • All of the examples in Examples for Developers have tests. • There are also many tests in Drupal core. In Drupal 7, test class files have exten‐ sion .test, and they are located in the core module directories. In Drupal 8, each test class is in its own .php file. SimpleTest tests are located in core/modules/ /src/Tests/ directories, and PHPUnit tests are located under core/ modules//tests/src directories. • Contributed modules with tests either follow the same conventions, or in Drupal 7, may put the test files in a tests subdirectory under the main module directory. Examples—documentation: • If you download the code for this book from the book website (see “How to Con‐ tact Us” on page xii), the downloaded code includes more documentation head‐ ers than are included in this book. • The Drupal core files for Drupal 7 and 8 are mostly pretty well documented and mostly adhere to the Drupal documentation standards. 70 | Chapter 2: Drupal Programming Principles CHAPTER 3 Common Drupal Programming Mistakes Experienced programmers have accumulated, through training or the experience of trial and error, a body of knowledge about how to approach problems and build applications. Unfortunately, some of this knowledge may lead them to make mistakes when they start working with Drupal, or to do things in less-than-optimal ways. This chapter covers several (somewhat overlapping) areas where programmers can shift their thinking or their approach in order to become more efficient at using the strengths of the Drupal platform, rather than fighting against it. Of course, following the principles in Chapter 2 will also help you shift your thinking to the Drupal way of doing things, and the suggestions and tools in Chapter 5 can help you avoid mistakes and find the mistakes that you do make. Mistake: Programming Too Much Experienced programmers who are new to Drupal often suffer from a variety of the “if all you have is a hammer, everything looks like a nail” syndrome: when faced with a challenge on a website (such as adding a feature or fixing a problem), they always try to solve it with programming. But although Drupal is built on PHP, and you can definitely do a lot of PHP programming when setting up a Drupal site, this is usually not the best approach: it results in a lot of unnecessary (and often tedious) program‐ ming. Related to this, experienced programmers coming to Drupal can be in a rush to become Drupal programmers, when really it would be better if they started out by becoming more effective Drupal users and site builders first, and resorted to program‐ ming only when necessary. While this goes against all the “solve all problems by pro‐ gramming” instincts of an experienced programmer (at least, speaking from my experience), if you learn advanced Drupal site-building techniques before you dive into Drupal programming, you will reap many benefits: 71 • You will be in a position to create very complex and interesting websites with Drupal because of your strong site-building skills. • You’ll be doing more interesting programming tasks rather than tedious ones. • You’ll save time by taking advantage of previous efforts by others. • You can use code that has been reviewed for security problems and tested on many other sites instead of custom code that is for only your site. • You can take advantage of the internationalization and translation capabilities of built-in Drupal systems. Of course, some balance is appropriate here. For instance, it may be better to write a few lines of code in a custom module rather than installing an entire contributed module with a lot of functionality that you don’t need. Each module you install has code that gets read in by the PHP interpreter on every page load, so efficiency is defi‐ nitely a consideration. That said, here are some examples of Drupal site-building knowledge you should explore in order to avoid unnecessary and tedious programming: Customizing fielded content display and editing The core Fields administrative interface provides a lot of flexibility in defining how fields are displayed and edited in content, including the order of fields, labels and label placement, data formatting, and which fields are shown. If that is not enough, the Display Suite module can be helpful, as can the Panels module. Drupal 8 provides even more flexibility for content editing forms than Drupal 7 does. Altering other user interface text Drupal provides hooks and other mechanisms that let you write a module that can alter page content and form elements. These can be quite useful, but often all you really need to do is change some text on a button or a form field label. If so, you don’t need to program—use the String Overrides module. If you need to do something to a form beyond what String Overrides covers, you can alter a form in a module. Content permissions The Drupal core Node module allows add-on modules to define very flexible content access permissions. So, if you need something beyond the default Node module permissions, your first instinct might be to write a content access mod‐ ule. However, the permissions needed for most sites are covered by the contrib‐ uted Content Access module, which allows you to define permissions on a percontent-type or per-content-item basis, which can be applied by role or by managing lists of individual users (with the addition of the Access Control Lists, or ACL, module). If you do need a permission system that is more customized 72 | Chapter 3: Common Drupal Programming Mistakes than what Content Access and other contributed content access modules provide, you will need to create a custom module. Marking and classifying content If your site needs content classification, use the core Taxonomy module. If users need to mark content (as bookmarked, spam, etc.), the contributed Flag module is the usual choice. There are also voting modules, such as Fivestar, if this is a feature you need on your site. All of these solutions are likely better choices than writing your own custom content classification or marking system, and they are well integrated with the Views and Rules modules. Placing content on pages Rather than using PHP code or embedding content directly into custom theme template files or theme functions, use the Drupal core block system to place con‐ tent into regions on pages. If your theme doesn’t have a region that would allow you to put the content where you want it, add a region to your theme. If the block system is not flexible enough, the contributed Context module provides much more flexibility in block placement, although in Drupal 8 the core block system is probably flexible enough to cover the vast majority of cases that required Context in Drupal 7. Many people prefer to use the contributed Panels module instead of Context, which lets you build pages and page types out of blocks, fields, and other pieces; try both Panels and Context and see which one you prefer. You may also find the contributed Delta module (which lets you make theme variations) and its Delta Blocks submodule useful; they integrate well with the Context module. Web forms In Drupal 7, the contributed Webform module covers many web form use cases, and you can also learn its API and do a little programming if you need a form field type that is not included (but check to see if someone has already contrib‐ uted a module that defines the form field that you need). In Drupal 8, the core Contact module allows you to use core fields to build custom web forms. Note, however, that the Drupal 8 Contact module solution is only workable for a site with a small number of forms, each having a relatively small number of fields; if you need large numbers of forms and/or large numbers of fields, the Drupal 8 port of Webform plans to support this use case. Responding to events The contributed Rules module allows you to define responses (such as sending email or redirecting to a different page) to various events (such as updating or adding content) on your website, under certain conditions (such as matching a content type or user role). If the events, conditions, or responses defined by the Rules module do not cover your needs, you can extend it, either by using a Mistake: Programming Too Much | 73 contributed module or making your own custom module. The Notifications module can also be useful—it lets users subscribe to content updates on a site. Site navigation Novice Drupal programmers sometimes want to code site navigation directly into their theme templates, because they have used this approach to ensure uni‐ form navigation on custom-built PHP/MySQL or pure HTML sites in the past. While this is certainly possible, it’s much better to use the core Menu module to manage site navigation. If you use the Menu module, you can define different menus for the main header navigation, the footer, sidebars, etc., and use CSS to style them appropriately. Drupal will automatically omit navigation items that are not accessible to whoever is viewing the site, and you will be able to manage the menus in the administrative user interface. If you code them into a theme tem‐ plate file, they will be much more difficult to manage, and permissions will not be enforced. Breadcrumbs The default Drupal breadcrumbs for content pages are often not what is desired for a site. So, it is tempting to override them by programming. While that is sometimes necessary, many use cases are covered by the contributed Custom Breadcrumbs module, which is quite easy to set up. Site configuration export In Drupal 7, the contributed Features module lets you export much of the config‐ uration of a Drupal site to PHP code. For instance, you can export your custom content types and fields, your views, and many other settings; the result is a mod‐ ule, which you can then put into revision control or share with another site. However, note that not all contributed modules support Features export, so not all of your settings can be exported. In Drupal 8, site configuration can easily be exported and imported using core modules. Further reading and reference: • “Avoiding Custom Programming with Fielded Data” on page 75 • “Defining Theme Regions for Block Placement” on page 77 • Internationalization capabilities of Drupal: “Principle: Drupal Is International” on page 48 • Configuration export: “Configuration API in Drupal 8” on page 42 74 | Chapter 3: Common Drupal Programming Mistakes Examples—code for strategies in the preceding list: • The Node Access example in Examples for Developers • “Creating Rules Module Add-Ons in Drupal 7” on page 184 • “Altering Forms” on page 121 Contributed modules mentioned in this section: • Access Control Lists: https://www.drupal.org/project/acl • Content Access: https://www.drupal.org/project/content_access • Context: https://www.drupal.org/project/context • Custom Breadcrumbs: https://www.drupal.org/project/custom_breadcrumbs • Delta: https://www.drupal.org/project/delta • Display Suite: https://www.drupal.org/project/ds • Features: https://www.drupal.org/project/features • Fivestar: https://www.drupal.org/project/fivestar • Flag: https://www.drupal.org/project/flag • Notifications: https://www.drupal.org/project/notifications • Panels: https://www.drupal.org/project/panels • Rules: https://www.drupal.org/project/rules • String Overrides: https://www.drupal.org/project/stringoverrides • Webform: https://www.drupal.org/project/webform Avoiding Custom Programming with Fielded Data While your first instinct, if you need to manage some fielded data on your website, might be to set up a custom database table to store your data, and to display your data using custom queries, you can save yourself a lot of unnecessary programming by instead using entities, fields, and the Views module. Entities were a new concept for Drupal version 7: modules define entity types to store information for a website, and the field system allows many different types of data to be attached to the base entity. In Drupal 7, core modules define four basic entity types (node content items, com‐ ments, taxonomy terms, and user accounts); Drupal 8 has many more entity types. Both Drupal core and contributed modules define fields to store various types of data (text, numbers, images, etc.). So, rather than defining a database table in a custom module to store the custom data for your website, your first step should usually be to instead set up a custom node Mistake: Programming Too Much | 75 content type and add the fields you need to it. If your data is not semantically “con‐ tent,” you might be better off setting up a taxonomy vocabulary; or if it is semantically very different from the existing entity types provided by Drupal core or contributed modules, you can create your own entity type (this will involve some programming). Once you have your core or custom entity type set up with its fields, you should not need to do any more programming to query and display the data. Instead, use the Views module, which is basically a very flexible content query and display engine (you may also find the contributed Nodequeue module useful if you are using the node content entity for your data). Examples of pages, blocks, and feeds you can build with Views include: • A photo gallery, which could include searching by keyword, topic- or user-based galleries, and a block that displays a random image added within the past 24 hours • A “recent site updates” block • A News block that detects which section of the site it is being displayed on and chooses news related to that section • A map of a company’s offices or of the site’s registered users • Blog archives with searching and filtering capability The way that Views works is that its user interface lets you set up the parameters for what is basically a formatted list of content (known as a view); these parameters can then be saved to PHP code in Drupal 7, or exported as configuration in Drupal 8. This might not sound very exciting or useful, but when you come to understand that “lists” encompasses grids, clouds, tables, feeds, and other formats; and “content” encompasses node content items, taxonomy terms, files, user account data, and other Drupal entities, you will begin to see how flexible and powerful this module is. Then when you learn how to make relationships (basically query table joins) between con‐ tent types, and how to use static, exposed, and contextual filters to define which con‐ tent items are shown, you will really become a power Views user. Finally, you can even extend Views by creating custom output formats, data types, fields, filters, and relationships, if the base module and available contributed Views add-on modules do not cover your use case. So, rather than writing modules to define database tables and using custom database queries to create content lists or displays, instead define content types (or other enti‐ ties), add fields to them, become a power Views user, and extend Views if you need to. There is help within the Views module (Drupal 7 only) that explains the basics, and countless blog articles, video tutorials, and books covering the subject. 76 | Chapter 3: Common Drupal Programming Mistakes Further reading and reference: • “Programming with Entities and Fields” on page 129 • Views module for Drupal 7: https://www.drupal.org/project/views (Views is part of Drupal 8 core) • Advanced Help module, for Views version 7 help: https://www.drupal.org/project/ advanced_help • Nodequeue module: https://www.drupal.org/project/nodequeue Examples—views programming: • “Creating Views Module Add-Ons” on page 171 Defining Theme Regions for Block Placement Blocks in Drupal are placed in regions in your theme, which might include the header, footer, left sidebar, right sidebar, and main content region. In most themes, the regions are the same on every page, although the Drupal theme system allows you to define multiple templates, each containing different regions. Drupal core’s block sys‐ tem allows you to place each block into a particular region and then define conditions for whether the block should be visible or not on particular pages or for particular users. In Drupal 8, the block placement system lets you make multiple copies of each defined block, with each one having its own visibility conditions and region place‐ ment. For some sites, this limited flexibility is not enough, so the contributed Context module or Panels module can be used to gain more flexibility in block placement. Sometimes all you need to achieve your desired site layout is a new theme region to put blocks in. For instance, maybe your site design calls for a “Call to Action” section on some pages, which would have a contrasting color scheme and appear in the upper-right corner of the main content area of the page. You can use the Drupal core block system to control what appears there on each page, if you add a new region to your theme. Here are the steps: 1. If you were using a Drupal core theme or a theme you downloaded, start by mak‐ ing a custom sub-theme. This will let you inherit most of the base theme while overriding just the parts that you want to change. Here are the mytheme.info file and mytheme.info.yml file that make a theme be a Bartik sub-theme in Drupal 7 or a Classy sub-theme in Drupal 8, respectively: ; mytheme.info for Drupal 7 name = My theme description = Bartik sub-theme core = 7.x Mistake: Programming Too Much | 77 base theme = bartik # mytheme.info.yml for Drupal 8 name: My theme type: theme description: Classy sub-theme core: 8.x base theme: classy 2. Add lines like the following ones to the info file, to tell Drupal about the new region. You will need to give the region both an internal name and a humanreadable name for the Blocks administration page: ; Regions line for Drupal 7 mytheme.info file. regions[internal_region_name] = Readable region name # Regions section of Drupal 8 mytheme.info.yml file. regions: internal_region_name: 'Readable region name' 3. If you are making a new sub-theme rather than adding to an existing custom theme, copy in the custom regions from the base theme’s info file. Only the Dru‐ pal core standard regions are inherited in a sub-theme, not the regions from the base theme. 4. Copy the page.tpl.php file (Drupal 7) or page.html.twig file (Drupal 8) from the base theme to your theme’s templates directory. Add markup that displays the region:
{# Drupal 8 page.html.twig file. #} {% if page.internal_region_name %}
{{ page.internal_region_name }}
{% endif %} 5. Clear the Drupal cache so that Drupal will recognize your new region. As a variation on this process, you could use Drupal’s template suggestions process of automatic template overrides. For instance, if your theme has a file called page-front.php (Drupal 7) or page--front.html.twig (Drupal 8), that file will be used in place of the generic page.tpl.php or page.html.twig template when the home page of your 78 | Chapter 3: Common Drupal Programming Mistakes site is being displayed. So if you only needed this new region on the home page, you could put it into the front-page template instead of the generic page template. Using a template suggestion will only control the final printing of your blocks, however, so for performance reasons, you should also make sure that your blocks are configured to show only on the appropriate pages (otherwise, they’ll be generated even though they are not printed in the end). Further reading and reference: • Creating a sub-theme for Drupal 7: http://bit.ly/drupal7_sub-themes • Creating a sub-theme for Drupal 8: http://bit.ly/drupal8_sub-themes • Template suggestions: http://bit.ly/template_suggestions • “The Drupal Cache” on page 10 Mistake: Overexecuting Code New Drupal programmers often make the mistake of writing PHP code that is exe‐ cuted more often than is necessary. Two examples of this are covered in this section. Mistake: Executing Code on Every Page Load One efficiency mistake that Drupal programmers make is to place code so that it gets executed on every page load: • In the main body of a module (outside all function definitions): All modules’ main module files are loaded on every page load, and any executable code out‐ side of function and class definitions would get run. • In Drupal 7, in an implementation of hook_boot() or hook_init(): both of these hooks run early in the page-generation process on most or all page loads. • In Drupal 8, in an event subscriber for Symfony’s main request event, which may be invoked multiple times during a typical page load. While this practice might be a good idea in a custom PHP/MySQL site or in other CMS systems, Drupal generally has a different hook or method that can be used to accomplish the same purpose with less overhead (as it will be executed only when needed) and less programming (as you are taking advantage of Drupal’s built-in logic, rather than re-creating it). For example: • Detecting an unauthorized user and redirecting to an error page: use the Drupal permissions system instead. You may also find the contributed Redirect 403 to User Login module useful. Mistake: Overexecuting Code | 79 • Detecting a particular URL and executing custom PHP code to generate output (HTML, Ajax, etc.): use the Drupal routing (URL registration) system instead. • Overriding behavior that another module has defined for a particular URL: use hook_menu_alter() in Drupal 7, and a routing system subscriber in Drupal 8. • Processing submitted form input: use the Drupal Form API instead. Examples—avoiding executing code on every page load: • “Checking Drupal Permissions” on page 64 • “Registering for URLs and Displaying Content” on page 88 • Altering routing: “Altering a URL Registration in Drupal 7” on page 91 and “Altering Routes and Providing Dynamic Routes in Drupal 8” on page 98 • “Using the Drupal Form API” on page 109 • Redirect 403 to User Login module: https://www.drupal.org/project/r4032login Mistake: Using an Overly General Hook Another efficiency mistake that many new Drupal programmers make is using an overly general hook when a more specific hook exists. This leads to your hook imple‐ mentation function being executed more often than necessary. Your hook implemen‐ tation function most likely will also be longer than necessary, because it will have more logic in it to detect the special case you are trying to handle. The most common example of this is hook_form_alter(), which you can use to make alterations in forms provided by other modules. Use hook_form_FORM_ID_alter() instead: // Bad - runs whenever any form is generated: function mymodule_form_alter($form, $form_state, $form_id) { if ($form_id == 'othermodule_form') { // Alteration code here. } } // Good - runs when this specific form is generated: function mymodule_form_othermodule_form_alter($form, $form_state) { // Alteration code here. } Examples—specific hooks instead of general ones: • “Altering Forms” on page 121 • “Repurposing an existing field widget” on page 167 80 | Chapter 3: Common Drupal Programming Mistakes You can use the generic hook_form_alter() during development as a quick way to figure out the form ID of a form you need to alter. For instance, you could put this in the function body, and it will put a message out with the ID of each form on the page: drupal_set_message($form_id); The Drupal For Firebug browser plugin is also helpful. Mistake: Saving PHP Code in the Database It is possible, by enabling certain modules and granting certain permissions, for users to include PHP code within the body of page or block content (and in other places), which is stored in the database and executed when the page or block is displayed. While this might seem like a great convenience and a time-saver, it is usually a mis‐ take to allow this because of the following factors: Security risk PHP code can add, change, or delete files and database records (any PHP code run during a page request has full rights to change the database). Thus, granting this permission opens up the possibility of malicious PHP code being added to a block or a page. Risk of bugs Even if you only grant the permission to highly trusted users, they could, through a bug in their PHP code, alter files or database records by mistake (the code would only have to run once to cause problems). Hard to debug Even if the PHP code isn’t dangerous, if it has a coding error, the page or block could sometimes produce the wrong output or cause a “White Screen of Death” (completely blank page), which is difficult to debug when it comes from PHP code stored in the database. Hard to track Allowing PHP code to be stored makes it hard to keep track of which page or block does what. Luckily, there are good alternatives for the common reasons people might want to store PHP code in the database: Control block visibility Use the Context module or the Panels module, which allow you much more flexi‐ bility on block placement. Drupal 8’s core block system is also a lot more flexible than Drupal 7’s, and you could also write a module to add new block visibility plugin. Mistake: Saving PHP Code in the Database | 81 Database queries Use the Views module. Custom page or block output Create your block or page in a module. Change how something is displayed Override a theme function or template. Calculations for a field Create your own custom field or formatter. Examples—alternatives to storing PHP in the database: • “Avoiding Custom Programming with Fielded Data” on page 75 • “Registering for URLs and Displaying Content” on page 88 • “The Basics of Module and Theme Hook Programming” on page 23 • “Defining a Field Type” on page 161 • “Programming with Field Formatters” on page 168 Contributed modules mentioned in this section: • Context: https://www.drupal.org/project/context • Panels: https://www.drupal.org/project/panels • Views for Drupal 7: https://www.drupal.org/project/views (Views is part of Drupal 8 core) Mistake: Working Alone One of the great strengths of the Drupal software is that it is produced by the Drupal open source project, which is a worldwide community of people who have chosen to come together and donate their time and money toward making Drupal better. The software has been around since 2001 or so, in which time the community of contribu‐ tors has grown from one person (the founder and continuing leader of the Drupal project, Dries Buytaert) to thousands of people. And although it does have its dys‐ functional moments, the Drupal community often works together pretty well to cre‐ ate the core Drupal software and add-ons, write the documentation, and support Drupal users. As a Drupal programmer, and especially when you’re getting started with Drupal pro‐ gramming, if you try to work alone without connecting to the Drupal community, you are making a mistake: engaging with the community will almost certainly directly help you become more effective and skillful in your Drupal endeavors. But if 82 | Chapter 3: Common Drupal Programming Mistakes you engage with the Drupal community by thinking only about what you can get from the community, you are also making a mistake. Instead, engaging in a thought‐ ful and respectful way and thinking about how you can contribute to the Drupal community and project as well as meeting your own needs will get you better results (people are more likely to help someone who has this type of attitude). Furthermore, if you are able to engage in ways that contribute to the Drupal project and commu‐ nity, you will end up with better Drupal software to use as well as improved skills. Participating in Groups and IRC One of the best ways to connect to the Drupal community is to join a local or regional Drupal user group and participate in meetings and events that members of the group organize. At these events, you may learn techniques that you can apply to your own work, or you may be inspired by seeing what others have done with Drupal; many local Drupal groups also encourage members to ask one another questions using forums or online IRC chats. If you do not live in or near a city with a local group, or if your local group does not have meetings, you could also consider organizing a meeting, which could be as sim‐ ple as choosing a coffee shop and setting a time for people to gather to talk about Drupal or show off the projects they are working on. There are also worldwide groups organized around topics and languages, which come together primarily on forums and on IRC. And if you don’t have a particular topic to gravitate toward, you can always chat with the global Drupal community on the general Drupal IRC channels. Next steps: • Find a geographical or topical group to join: https://groups.drupal.org. • Learn about IRC and local, topical, and general channels: https://www.drupal.org/ irc. Reporting Issues and Contributing Code to the Drupal Community Another key way to connect to the Drupal community is to use the Drupal issue queues (known as bug databases or ticket systems in other projects) to report software bugs and request new features. Each project (Drupal core, contributed modules, con‐ tributed themes, and contributed Drupal distributions) has its own issue queue, so the first step in reporting an issue is to narrow down exactly what project the issue pertains to. Then, visit that project’s home page on drupal.org, and search to make sure the issue has not already been reported (you will need to log in to drupal.org in order to search issues). If the issue has already been reported, you can “follow” (sub‐ scribe to) the existing issue. You might also add a comment to the issue if you are Mistake: Working Alone | 83 seeing the same problem from a slightly different cause or in a slightly different envi‐ ronment than previously reported, or if it was reported a long time ago and you’ve discovered that it is still a problem in a newer version. Finally, if what you would like to report is a new issue, click the “Create a new issue” link on the project’s issue search results page, and fill in the issue details. Subscribing to Issues The drupal.org website allows you to subscribe to individual issues or to entire projects’ issues (you’ll need to log in to do any of these actions): • To subscribe to an individual issue, click on the Follow button on the issue page. • To subscribe to an entire project’s issues, click the E-mail notifications link when viewing the issue list for the project. • You can see a paginated list of all the issues you have followed on the Your issues tab of your user profile page. • On your user Dashboard, you can add blocks that show the issues you have fol‐ lowed, or statistics and links for the issues of individual projects. • By default, you should receive email for individual issues and projects that you have followed (an email message is generated for each comment or issue update). To change your email notification settings, visit your user profile page and click on Notifications. You can also subscribe to issues for entire projects on this page. To ensure that your issue reports are well received, consider these points: Be polite and respectful Drupal is open source, community-produced software, so for the most part it is created by volunteers. Sounding angry or disgusted in your issue report is not likely to inspire the volunteer who created the software that has the bug to fix it. Be constructive If possible, suggest a solution rather than just pointing out the problem. Be clear and complete If you report a vague problem like “This doesn’t work,” without details of what you were doing, what you observed happening, and why you think what you observed was not correct, your report is likely to be ignored or marked “cannot reproduce.” You’ll also find that your issue is better received if you have selected appropriate values for status, component, and the other “issue meta-data” fields; there are links on the issue reporting page that will take you to explanations of these fields. 84 | Chapter 3: Common Drupal Programming Mistakes www.allitebooks.com Be attentive Often, the maintainers of the component or project you are reporting the issue on will have questions for you, such as whether you have certain other modules installed on your site. They will ask the questions as comment replies to your original issue; your quick response to questions will expedite action to resolve the issue. Issues are also the starting point for contributing code to the Drupal project, as most (ideally, all) code changes in Drupal core and add-on projects are coordinated on issues. To contribute suggested code changes, first file an issue report, and then create and attach a patch file to the issue. After attaching a patch, be attentive—it’s highly unlikely that your patch will be accepted without some changes, and it’s much more likely that your patch will eventually be accepted if you follow the issue and make the requested changes, rather than just dropping the patch on the issue and hoping that someone else will finalize it. Keep in mind these additional points: • It is in your interest to get the code committed (added to the project): presumably you want the new feature or want to have the bug fixed. So, do everything you can to get it committed (make changes suggested by reviewers). • Patches that don’t follow the Drupal coding standards, that have security prob‐ lems, or that don’t follow standard Drupal programming practices are unlikely to be committed. • The priorities of the project maintainers may be different from yours, but they do have final say. For instance, they may decide that they don’t want to add complex‐ ity to their module, if you propose a new feature, or they may suggest that you submit your patch (or idea) to a different project. So, be prepared to be flexible. Further reading and reference: • Novice contributor guide to patches: https://www.drupal.org/novice • Git revision control system: https://www.drupal.org/documentation/git • Drupal project coding standards: https://www.drupal.org/coding-standards Contributing to the Drupal Community in Other Ways Programmers naturally think about contributing code to the project, but there are many other ways to contribute to the project and to the community, which are all val‐ uable. Some ideas: Mistake: Working Alone | 85 • Write or edit Drupal documentation: see https://www.drupal.org/contribute/docu mentation. • Attend a DrupalCon or regional conference and make a presentation to share your Drupal knowledge: see https://groups.drupal.org/calendar. • Answer questions in the #drupal or #drupal-support IRC channels, the drupal.org Forums, or http://drupal.stackexchange.com. • If you speak a non-English language, translate Drupal software into your lan‐ guage: see https://localize.drupal.org. • Find many more ideas for volunteering your time at: https://www.drupal.org/newcontributors. • Join or donate targeted money to the Drupal Association, the nonprofit organization that keeps the drupal.org servers running, organizes DrupalCon events, and generally works for the benefit of the Drupal project: see https://association.drupal.org. 86 | Chapter 3: Common Drupal Programming Mistakes CHAPTER 4 Drupal Programming Examples Now that you have learned the basic principles of Drupal programming and how to avoid making common Drupal programming mistakes, it’s time to put your program‐ ming skills to work! This chapter covers special topics in Drupal programming that you can use to enhance websites built with Drupal; they’re all things I’ve actually needed to do either in my freelance work as a Drupal site builder or my volunteer work programming for the Drupal project (for Drupal core and contributed mod‐ ules). My advice is to skim this chapter now so you know what it covers and then read individual sections in more detail when you need them. Other places to look for programming examples: • The Drupal core code itself, which includes extensive documentation and tests • Examples for Developers, which has comprehensive coverage of the Drupal core APIs and how to use them in your own modules • Thousands of GPL-licensed contributed modules you can download from https:// www.drupal.org/project/modules and then adapt for your own work • The API reference site, https://api.drupal.org; see also “Using api.drupal.org” on page 201 87 Registering for URLs and Displaying Content “How Drupal Handles HTTP Requests” on page 7 contains overviews of how Drupal 7 and Drupal 8 handle URL requests and return content to a web browser or other requester. This section goes into more detail about how a module you write can be part of that process, by registering with Drupal to handle specific URLs and by pro‐ viding page and block content. Given that Drupal has many hooks and that it is written in PHP, there are many ways that you could consider writing code for Dru‐ pal that would return output in response to a URL request, or that would place content in a region on a page or set of pages. Most of these ways would, however, amount to subverting Drupal’s stan‐ dard processes, rather than working within the Drupal framework. Use the methods in this section of the book for best results. Assuming that you have decided you need your module to output some content, the first choice you need to make is whether to provide a router entry or a block. A router entry allows you to respond to a URL request by providing the main HTML content for that page or, in some cases, the entire output for the URL (such as XML output that is used by a Flash script on your site, an RSS feed, or an Ajax response). A block allows you to provide a chunk of output that can be placed on one or more of a site’s HTML-based pages. In either case, you will need to register with Drupal for the block or URL, and then write a function or class to generate the output; in the case of a router entry, you will also need to define permissions for accessing the URL. All of these steps are described in the following sections. Note that you should write code to provide blocks and router entries only if there is some logic or programming needed to generate the content of the block or page. If you want to display static content on a website, you can create a block or content item using Drupal’s user interface, and if you need to employ complicated logic to decide where or when to show the block, use the Context module or Panels module. Also, keep in mind that using the Views module is a better choice than making a custom module in many cases. Further reading and reference: • “Mistake: Programming Too Much” on page 71 (which also lists the modules mentioned here) • “Avoiding Custom Programming with Fielded Data” on page 75 88 | Chapter 4: Drupal Programming Examples Registering for a URL in Drupal 7 To register to provide the main page content for a URL in Drupal 7, you will need to define a router entry by implementing hook_menu() in your mymodule.module file, which is covered in this section, and then you will need to write a function to gener‐ ate your page content, which is covered in “Creating Render Arrays for Page and Block Output” on page 102 (the functions to generate the actual page and block out‐ put are quite similar, so they are covered together). To define your router entry, first you will need to choose a URL. All URLs are relative to your base site URL, and in Drupal terms, the suffix of the URL, after your base site URL and possibly a language code, is known as a path. Here are some considerations when choosing a URL path for your page: • If you are providing an administrative page, the path should be chosen to place the page in an appropriate, existing section of the Drupal core administration screens. For instance, if it’s “structural,” it should start with admin/structure/, and if it’s for use by developers, it should start with admin/config/development/. You can see a complete list of the sections in the system_menu() function in the mod‐ ules/system/system.module file that comes with Drupal (paths that start with admin). • Make sure your path does not conflict with a path that another module might provide. Normally, prefixing with or including your module’s machine name is a good idea (mymodule in this example). • Make your path like others in Drupal. For instance, if you are defining an autocomplete responder for a form, choose the mymodule/autocomplete path, similar to the user/autocomplete path defined by the core User module. • The path can contain placeholders. For example, the core Node module defines a path node/ followed by the node content item’s ID number. After choosing your path, implement hook_menu() to tell Drupal about it: function mymodule_menu() { $items = array(); // Put the chosen path here. Do not start it with /. $items['mymodule/mypath'] = array( // Do not translate this! 'title' => 'My page title', // Function that will generate the content. 'page callback' => 'mymodule_page_generate', // Function used to check permissions. This defaults to user_access(), // which is provided here as an illustration -- you can omit this line // if you want to use the user_access() function. Put the name of your // custom access check function here if you have one. 'access callback' => 'user_access', Registering for URLs and Displaying Content | 89 // Arguments needed for your access callback function. If using the // default user_access() function, the argument is the name of the // permission a user must have to access the page. 'access arguments' => array('administer mymodule'), ); return $items; } Notes: • hook_menu() is invoked rarely, and its output is cached. • Strings such as the page title in hook_menu() implementations are not translated using t(). The menu and routing systems translate the strings when necessary on output. • The hook_menu() implementation references a page-generating function (mymodule_page_generate() in this example). Because block-generating func‐ tions are very similar to page-generating functions, the details of what this func‐ tion should return are covered in a separate section, “Creating Render Arrays for Page and Block Output” on page 102. • There is no need to explicitly check for access permissions in your pagegenerating function or elsewhere, assuming that you set up an access callback in your hook_menu() implementation. Drupal will verify and run this access check for you automatically and return a 403 access denied response for unau‐ thorized users. • If you use the default user_access() callback, the permission name needs to be registered in a hook_permission() implementation. Autoloading, Arguments, and Placeholders in hook_menu() The routing system in Drupal 7 is fairly powerful, and one of the powerful features that new Drupal programmers often don’t know about is the ability to autoload objects. Using this feature takes several steps: 1. Use a placeholder in the path you are registering for and give it a name. Place‐ holders in hook_menu() paths start with %, so for instance you could register for a path like mymodule/%mymodule_object. 2. Define a function of the same name as your placeholder with a load suffix: mymodule_object_load() in this example. It should load your object and return it, or return FALSE if it cannot be loaded, which will trigger a 404 Not Found response. 90 | Chapter 4: Drupal Programming Examples 3. When someone goes to a specific URL matching your pattern, such as exam‐ ple.com/mymodule/123, your load function will be called with the corresponding piece of the URL as its first argument ('123' in this example). You can tell Dru‐ pal to pass additional arguments to this function by adding a 'load arguments' element to your hook_menu() implementation. 4. In 'page arguments', 'access arguments', and related elements of your hook_menu() implementation, you can pass the loaded object by using the numeric index of your placeholder in the path. In the mymodule/%mymod‐ ule_object path, for example, the 0 placeholder would have value 'mymodule' and the 1 placeholder would have your loaded object. So if your page-generating function has two arguments, the object and a view mode, you might put: 'page arguments' => array(1, 'full'), into your hook_menu() implementation to indicate this. There is an example that uses an autoloading placeholder in Step 4 of “Defining an Entity Type in Drupal 7” on page 132. Further reading and reference: • “The Basics of Module and Theme Hook Programming” on page 23 • “Drupal core’s main permission system” on page 64 Examples—hook_menu() implementations: • “Basic Form Generation and Processing in Drupal 7” on page 112. • “Adding Autocomplete to Forms” on page 119. • “Defining an Entity Type in Drupal 7” on page 132. • The Page example in Examples for Developers. • Look up hook_menu() on https://api.drupal.org for complete documentation of all its return value options and to find many examples of Drupal core implementations. Altering a URL Registration in Drupal 7 A related task that you may need to do in a Drupal 7 module is to alter how another module has registered for a URL. One common reason would be that you want to use a different access permission system for the URL. To do this, implement hook_menu_alter() in your mymodule.module file. For example: Registering for URLs and Displaying Content | 91 function mymodule_menu_alter(&$items) { // $items contains all items from hook_menu() implementations. $items['other/module/path']['access callback'] = 'mymodule_check_access'; } function mymodule_check_access() { // The user who is trying to access the page. global $user; // Calculate whether this user should get access or not, // and return TRUE or FALSE. } Further reading and reference: • Look up hook_menu() on https://api.drupal.org for complete documentation of all its return value options. The combined return values from all modules’ hook_menu() implementations are passed into hook_menu_alter() implementations. • “The Basics of Module and Theme Hook Programming” on page 23. • “Drupal core’s main permission system” on page 64. Registering for a URL in Drupal 8 To register to provide the main page content for a URL in Drupal 8, you will need to register for the URL in a routing.yml file and define a class method to provide the actual page output. These steps are covered in this section. To register the URL, create a file called mymodule.routing.yml in your main module directory. As in Drupal 7, first you will need to choose a URL. All URLs are relative to your base site URL, and in Drupal terms, the suffix of the URL, after your base site URL and possibly a language code, is known as a path. Here are some considerations when choosing a URL path for your page: • If you are providing an administrative page, the path should be chosen to place the page in an appropriate, existing section of the Drupal core administration screens. For instance, if it’s “structural,” it should start with admin/structure/, and if it’s for use by developers, it should start with admin/config/development/. The Drupal core hierarchy can be found in the core/modules/system/system.rout‐ ing.yml file, in the paths that start with admin. • Make sure your path does not conflict with a path that another module might provide. Normally, prefixing with or including your module’s machine name is a good idea (mymodule in this example). 92 | Chapter 4: Drupal Programming Examples • Make your path like others in Drupal. For instance, if you are defining an auto‐ complete responder for a form, make the path mymodule/autocomplete, similar to the block-category/autocomplete path defined by the core Block module. • The path can contain placeholders. For example, the core Node module defines a path of node/ followed by the node content item’s ID number. After choosing your path, register it as a route with Drupal’s routing framework by creating a file called mymodule.routing.yml in your main module directory, which contains: mymodule.mydescription: path: '/mymodule/mypath' defaults: _controller: '\Drupal\mymodule\Controller\MyUrlController::generateMyPage' _title: 'My page title' requirements: _permission: 'administer mymodule' Notes: • The first line is a route identifier, which must be unique. By convention, it should start with your module’s machine name, followed by a period. • The path line gives the URL path that you have chosen, starting with /. It can also contain placeholders. For instance, this entry would register for a set of URL paths containing an ID: path: '/mymodule/mypath/{id}' • The defaults section tells what method on what class should be used to generate the content for the path and provides other defaults. You have a few choices for specifying the content generator, which is known as the route controller. Specify‐ ing it with _controller, as in this example, means that it is a generic page. Another possibility is _form, if your page contains a form. You can also provide default argument values for your page-generating method, by adding lines to this section. For instance, if your method had an argument $foo, you could define its default value using: foo: 'bar' • The _title line within defaults gives the page title. • The requirements section defines the permission needed to access the page. It must be registered in your mymodule.permissions.yml file. After registering the route in the routing file, you need to define a page-controller class to provide the actual page output. This class should usually extend the Registering for URLs and Displaying Content | 93 \Drupal\Core\Controller\ControllerBase class; by convention, it should be part of the \Drupal\mymodule\Controller namespace and thus must be located in the src/Controller subdirectory under your module directory. In our example, the class is called MyUrlController, so the file must be called MyUrlController.php and it con‐ tains: namespace Drupal\mymodule\Controller; use use use use Drupal\Core\Controller\ControllerBase; Drupal\Core\Database\Connection; Drupal\Core\DependencyInjection\ContainerInjectionInterface; Symfony\Component\DependencyInjection\ContainerInterface; class MyUrlController extends ControllerBase { // Database service, obtained through dependency injection. // This is needed for the pager sample output in a later section. protected $database; public static function create(ContainerInterface $container) { return new static($container->get('database')); } public function __construct(Connection $database) { $this->database = $database; } // Page-generating method. public function generateMyPage() { // Function body will be defined later. } } The page-generating method on your controller either needs to return a Symfony response object of class \Symfony\Component\HttpFoundation\Response, or a render array; a render array is the usual choice for generic pages. Because block-generating functions also return render arrays, they are covered in a separate section: “Creating Render Arrays for Page and Block Output” on page 102. Further reading and reference: • “The Basics of Module and Theme Hook Programming” on page 23 • “Automatic Class Loading in Drupal” on page 12 • “Drupal core’s main permission system” on page 64 Examples—routing: • “Basic Form Generation and Processing in Drupal 8” on page 114 94 | Chapter 4: Drupal Programming Examples • “Creating Confirmation Forms” on page 116 • “Adding Autocomplete to Forms” on page 119 • “Defining a Content Entity Type in Drupal 8” on page 139 • “Defining a Configuration Entity Type in Drupal 8” on page 149 Providing Administrative Links In Drupal 7, when you use hook_menu() to register for a path under admin, you will also automatically be creating an administrative menu link. For instance, if you regis‐ ter a path admin/structure/mymodule and navigate to the Structure administrative page at admin/structure, your page will be listed; you’ll also be able to see it in the Management menu hierarchy. In Drupal 8, menu-entry creation has been decoupled from route registration, so using a *.routing.yml file to register a route does not automatically register an admin‐ istrative menu link. In order to register an administrative menu link for a route you’ve already defined, you’ll need to create a mymodule.links.menu.yml file in your main module directory. In this file, you’ll tell Drupal where to place the menu entry (by giving the machine name of the parent link—not the route name of the parent link, although they are often the same). You’ll also need to provide your route machine name (which will tell Drupal the URL path of your link), the link title, and the description. For instance, you could make the route defined in the previous section appear in the Structure menu section with this entry in mymodule.links.menu.yml: mymodule.mydescription: title: 'Configure My Module' description: 'Longer description goes here' parent: system.admin_structure route_name: mymodule.mydescription In some cases, you’ll want to provide a local task (tab) for an existing administrative page, rather than adding your own administrative menu entry. In Drupal 7, this is also taken care of in hook_menu(), by making an entry that is of type MENU_LOCAL_TASK instead of the default MENU_NORMAL_ITEM. Here’s an example from the core User module, which makes the Permissions page be a local task for the Peo‐ ple list page: $items['admin/people/permissions'] = array( 'title' => 'Permissions', 'description' => 'Determine access to features.', 'page callback' => 'drupal_get_form', 'page arguments' => array('user_admin_permissions'), 'access arguments' => array('administer permissions'), 'file' => 'user.admin.inc', Registering for URLs and Displaying Content | 95 'type' => MENU_LOCAL_TASK, ); In Drupal 8, the creation of a local task to be displayed on a given page is accom‐ plished by creating a mymodule.links.task.yml file in your main module directory, giv‐ ing the route name for the task page, the tab title, and the base route name (the route name of the page that should display the task). For this same User module Permis‐ sions page, the core/modules/user/user.links.task.yml file contains: user.admin_permissions: title: Permissions route_name: user.admin_permissions base_route: entity.user.collection Besides local tasks, there are also local actions. Local tasks are usually rendered as tabs at the tops of pages, while local actions are usually rendered as buttons. A typical use for a local action is an “add” button; for example, the Drupal core User module has an “Add user” button on the People administration page. This is accomplished in Drupal 7 with the following code in its hook_menu() implementation: $items['admin/people/create'] = array( 'title' => 'Add user', 'page arguments' => array('create'), 'access arguments' => array('administer users'), 'type' => MENU_LOCAL_ACTION, ); In Drupal 8, local actions are defined in the mymodule.links.action.yml file in the main module directory. For this same Add user button, the core/modules/user/ user.links.action.yml file contains: user_admin_create: route_name: user.admin_create title: 'Add user' appears_on: - entity.user.collection Note that the appears_on entry is the name of the route for the page the local action should appear on. The final type of administration link that you can define is a contextual link, which gives users context-based operations. They are a little bit more complex to implement than local tasks and actions, because they operate on some specific piece of data and have to be collected and added to administrative pages that display that type of data. In Drupal 7, again contextual links are part of the hook_menu() system, specified by giving a context element to a local task entry. Here’s the section of block_menu() from the core Block module that sets up the Configure link for a block: $items['admin/structure/block/manage/%/%/configure'] = array( 'title' => 'Configure block', 96 | Chapter 4: Drupal Programming Examples 'type' => MENU_DEFAULT_LOCAL_TASK, 'context' => MENU_CONTEXT_INLINE, ); In Drupal 8, the entity system sets up contextual links automatically for content enti‐ ties; if you want to have contextual links for non-entity data, here are the steps that Drupal core uses to provide this same contextual link. First, choose a group name for your contextual links, which needs to be unique and should describe what type of data the links operate on. In our Block module example, there is a group called block for contextual links that operate on blocks. Second, in your mymodule.links.contextual.yml file, define the operations on either a group that your module has defined or one from another module. In the Block exam‐ ple, the Configure link is defined in its block.links.contextual.yml file: block_configure: title: 'Configure block' route_name: 'entity.block.edit_form' group: 'block' Here, the first line defines the machine name of the contextual link, and the other lines give the group machine name, the link title, and the route name for making the link. Third, on the administrative page that displays your data, add a #contextual_links element to a render array, to render the links. In the Block example, \Drupal\block \BlockViewBuilder::viewMultiple() has these lines: $build[$entity_id]['#contextual_links'] => array( 'block' => array( 'route_parameters' => array('block' => $entity->id()), ), ); This tells the Contextual Links module (if installed) to collect all the contextual links in the block group and render them, passing in the route parameter from the current block to construct each link. If the Contextual Links module is not installed, this part of the render array will be ignored. If you want to alter the menu links, local tasks, contextual links, or local actions defined by another module, you can do that with an alter hook. In Drupal 7, all of these are defined in hook_menu(), so use hook_menu_alter() to alter them. In Drupal 8, use hook_menu_links_discovered_alter() to alter the administrative menu links, hook_menu_local_tasks_alter() to alter the local tasks, hook_menu_local_actions_alter() to alter the local actions, and hook_contextual_ links_alter() to alter contextual links. For local tasks, you can also implement hook_menu_local_tasks() to provide additional, dynamic local tasks; this hook runs before the alter hook. Registering for URLs and Displaying Content | 97 Further reading and reference: • “The Basics of Module and Theme Hook Programming” on page 23 • hook_menu_alter() in Drupal 7: “Altering a URL Registration in Drupal 7” on page 91 Examples—administrative links: • “Defining a Content Entity Type in Drupal 8” on page 139 • “Defining a Configuration Entity Type in Drupal 8” on page 149 Altering Routes and Providing Dynamic Routes in Drupal 8 Drupal 8’s event system is used for dynamic routes and route altering: • Dynamic routes are for URL paths that cannot be set up as static routes in a rout‐ ing.yml file. For example, the core Field UI module provides field management pages (Manage Fields, Manage Display, and Manage Form Display) for each entity type and subtype that supports fields and administrative management. Because the Field UI module does not know what entity types will be available on a given Drupal site, and because each entity type has full flexibility and control over its administrative URL path, there is no way for the Field UI module to set these up as static routes in a YAML file. Instead, at runtime, it needs to set up the needed routes for defined entity types. • Route altering is when a module changes some aspect of a route that another module has set up. As an example, if the core Editor module is enabled, it changes the names and descriptions of the Filters administrative page, because editors are managed on the same page. To provide dynamic routes or alter other modules’ routes: 1. Create an event subscriber class. This class must implement \Symfony\Component \EventDispatcher\EventSubscriberInterface; the easiest way to do that is to extend \Drupal\Core\Routing\RouteSubscriberBase, as it sets up all of the needed framework for you. Put it into the \Drupal\mymodule\Routing namespace, so if the class name is MyModuleRouting, it must go into the src/Routing/MyModuleRouting.php file under your main module directory. Be sure to include use statements for the \Symfony\Component\Routing\Route, \Symfony\Component\Routing\RouteCollection, and \Drupal\Core\Routing \RouteSubscriberBase classes. 98 | Chapter 4: Drupal Programming Examples 2. In your class, override the alterRoutes() method, which operates on the route collection (the list of all of the routes that are set up by all modules). Here are some examples: protected function alterRoutes(RouteCollection $collection) { // Alter the title of the People administration page (admin/people). $route = $collection->get('entity.user.collection'); $route->setDefault('_title', 'User accounts'); // Make sure that the title text is translatable. $foo = t('User accounts'); // Add a dynamic route at admin/people/mymodule, which could have been // a static route in this case. $path = $route->getPath(); // Constructor parameters: path, defaults, requirements, as you would have // in a routing.yml file. $newroute = new Route($path . '/mymodule', array( '_controller' => '\Drupal\mymodule\Controller\MyUrlController::generateMyPage', '_title' => 'New page title', ), array( '_permission' => 'administer mymodule', )); // Make sure that the title text is translatable. $foo = t('New page title'); $collection->add('mymodule.newroutename', $newroute); } 3. Because this example is altering routes from the User module, declare a depend‐ ency on this module in the mymodule.info.yml file: dependencies: - user 4. Put these lines into the mymodule.services.yml file in the main module directory (omit the first line if you already have services in the file): services: mymodule.subscriber: class: Drupal\mymodule\Routing\MyModuleRouting tags: - { name: event_subscriber } 5. After adding a new route subscriber, or after updating what it does, you will need to clear the Drupal cache so that Drupal will rebuild its cached routing information. Registering for URLs and Displaying Content | 99 Further reading and reference: • “The Drupal Cache” on page 10 • “Drupal 8 Services and Dependency Injection” on page 35 • “Interacting with the Drupal 8 Event System” on page 38 Registering a Block in Drupal 7 If you want to provide content that can be displayed on multiple pages, you should register for a block rather than for a path in your module. To register for a block in Drupal 7, you need to implement hook_block_info() in your mymodule.module file to tell Drupal about the existence of your block, and then implement hook_block_view() to generate the block content. For example: // Tell Drupal about your block. function mymodule_block_info() { $blocks = array(); // The array key is known as the block "delta" (a unique identifier // within your module), and is used in other block hooks. Choose // something descriptive. $blocks['first_block'] = array( // The name shown on the Blocks administration page. // Be descriptive and unique across all blocks. 'info' => t('First block from My Module'), ); return $blocks; } // Generate the block content. Note that the $delta value passed in // is the same as the array key you returned in your hook_block_info() // implementation. function mymodule_block_view($delta = '') { if ($delta == 'first_block') { return array( // The block's default title. 'subject' => t('First block'), // The block's content. 'content' => mymodule_block_generate(), ); } } 100 | Chapter 4: Drupal Programming Examples Notes: • Implementations of hook_block_info() can be more complex than this: they can specify cache parameters (block output is cached by default for efficiency) and default placement. • Blocks can also have configuration settings. These are provided by implementing hook_block_configure() and hook_block_save(). • The hook_block_view() implementation here calls a function (in this example, mymodule_block_generate()) to provide the actual block content. Because block-generating functions are very similar to page-generating functions, the details of what this function should return are covered in a separate section: “Creating Render Arrays for Page and Block Output” on page 102. • After adding a new block to a hook_block_info() implementation, you will need to clear the Drupal cache to make it visible on the Blocks page. Further reading and reference: • “The Basics of Module and Theme Hook Programming” on page 23 • “The Drupal Cache” on page 10 Examples—blocks: • Look up hook_block_info() on https://api.drupal.org to find all the options and links to the Drupal core functions that implement it. • The Block example in Examples for Developers and many Drupal core blocks include configuration options, cache settings, and other options. Registering a Block in Drupal 8 In Drupal 8 (as in Drupal 7), if you want to provide content that can be displayed on multiple pages, you should register for a block rather than for a route in your module. Drupal 8 uses a plugin system for blocks, so all you have to do to register a block in Drupal 8 is properly define a Block plugin class. Block plugin classes should either extend the \Drupal\Core\Block\BlockBase class or implement the \Drupal\Core \Block\BlockPluginInterface interface, and they need to have Block plugin anno‐ tation from the \Drupal\Core\Block\Annotation\Block annotation class. They must be in the \Drupal\mymodule\Plugin\Block namespace and thus must be loca‐ ted in the src/Plugin/Block subdirectory under your module directory. Registering for URLs and Displaying Content | 101 To define the same block as in “Registering a Block in Drupal 7” on page 100, a good name for the class would be MyModuleFirstBlock, making the file src/Plugin/Block/ MyModuleFirstBlock.php: namespace Drupal\mymodule\Plugin\Block; use Drupal\Core\Block\BlockBase; /** * Provides a sample block. * * @Block( * id = "mymodule_first_block", * admin_label = @Translation("First block from My Module") * ) */ class MyModuleFirstBlock extends BlockBase { public function build() { // Function body will be defined later. } } Notes: • The @Block annotation provides a unique identifier for the block, as well as the name for the block that will appear on the Blocks administration page. See “The Basics of Drupal 8 Plugin Programming” on page 28 for more details about plu‐ gins and annotation. • The build() method does the work of providing output for the block. Because block-generating functions are very similar to page-generating functions, the details of what this function should return are covered in “Creating Render Arrays for Page and Block Output” on page 102. • You can also implement or override additional methods to provide access con‐ trol, caching control, and configuration settings. • After defining a new block plugin class, you will need to clear the Drupal cache to make it visible on the Blocks page. Creating Render Arrays for Page and Block Output Once your module has registered for a URL or block (see previous sections), you need to write a function or a method that returns the page or block output. In many content management systems, and in Drupal 6 and prior versions, output is gener‐ ated directly and returned as a string of HTML markup and text. The problem with this philosophy is that when a module (rather than the theme) is making HTML markup decisions, it is difficult for the theme to have full control over how data is 102 | Chapter 4: Drupal Programming Examples presented. Accordingly, Drupal 7 and 8 use a different philosophy, where modules that provide output for site visitors should always return the output data and metadata properties, rather than rendered markup. This data can then be altered by other modules and finally used by the theme system to render the data into HTML. The structure used to return the output data and properties is known as a render array. Here is the general structure of a render array that you could return from a page- or block-generating function or method: $output = array( 'sensible_identifier_1' => array( '#type' => 'element_identifier', // Other properties and data here. ), 'sensible_identifier_2' => array( '#theme' => 'theme_hook', // Other properties and data here. ), // Other pieces of output here. ); Notes: • The outermost array keys are arbitrary: choose sensible identifiers that will remind you of what each piece of your block or page is. • At the next level of arrays, keys starting with '#' are property keys that are recog‐ nized by the Render API. You can also provide properties on the outermost array. • A render array should contain one or more of the following: — A '#type' property, whose value is the machine name of a render element; see “What Is a Render Element?” on page 104. — A '#theme' property, whose value is the name of a theme hook. If used with the '#type' property, it would override the default theme hook for the render element. — Keys not starting with '#', whose array values are themselves render arrays. If the parent array does not have a render element type or theme hook specified, each child array will be independently rendered and the output concatenated. • Theme hooks are defined by modules by implementing hook_theme(), and each theme hook also requires one or more properties to be provided. • Be sure that all your text is internationalized. Registering for URLs and Displaying Content | 103 • If there is a '#markup' property and '#type' is not set, a render element type of markup will be assumed. What Is a Render Element? A render element represents the attributes and data to be output in one or more HTML elements, such as tables, forms, and form input elements; in principle, render elements can also represent non-HTML output. In practical terms, each render ele‐ ment defined in Drupal has a machine name and an array of default values for its properties; the property names start with '#'. Properties of render elements include '#theme' (for the theme hook that will be used to create the HTML output), other attributes, and the data to be output. When you use a render element in a render array, by giving its machine name in the '#type' property of the array, you will pro‐ vide property values to override the defaults. In Drupal 7, render elements are defined in implementations of hook_ element_info(), and the common ones are defined by the System module in system_element_info(). You can discover available element types by looking at the hook implementations; the machine names for elements are the array keys in the return values, and the default properties for each element are the array values. In Drupal 8, render elements are plugins annotated with \Drupal\Core\Render \Annotation\RenderElement or \Drupal\Core\Render\Annotation\FormElement annotation, whose classes implement \Drupal\Core\Render\Element\Element Interface or \Drupal\Core\Render\Element\FormElementInterface. You can look for classes that implement either of these interfaces to discover element types; the machine names for the render elements are given in the plugin annotation, and the default properties are in the getInfo() class methods. Here’s an example of a render array that has an informational paragraph, followed by a list of items, followed by a table (the paragraph uses a 'markup' render element; the list and table use the 'item_list' and 'table' theme hooks): $output = array( 'introduction' => array( '#type' => 'markup', '#markup' => '

' . t('General information goes here.') . '

', ), 'colors' => array( '#theme' => 'item_list', '#items' => array(t('Red'), t('Blue'), t('Green')), '#title' => t('Colors'), ), 'materials' => array( '#theme' => 'table', 104 | Chapter 4: Drupal Programming Examples '#caption' => t('Materials'), '#header' => array(t('Material'), t('Characteristic')), '#rows' => array( array(t('Steel'), t('Strong')), array(t('Aluminum'), t('Light')), ), ), ); Generic JavaScript code and files can be added to a render array by using the '#attached' property. In Drupal 7, the jQuery library will be attached by default on every page, so you can make use of that when writing your JavaScript. Here are some Drupal 7 examples: // Attach a JavaScript file. $output['#attached']['js'][] = drupal_get_path('module', 'mymodule') . '/mymodule.js'; // Attach some in-line JavaScript code. $output['#attached']['js'][] = array( 'type' => 'inline', 'data' => $my_code, ); CSS attachment works the same way, with the 'css' array element. You can also define a library that contains one or more JavaScript and CSS files, and it can depend on other libraries. Look up hook_library_info() on https://api.drupal.org for more information on that. Further reading and reference: • The general structure and many of the details of render arrays are the same between Drupal 7 and Drupal 8. Drupal 8 specifics are covered in “Render Arrays in Drupal 8” on page 106. • More about theme hooks: “Making Your Module Output Themeable” on page 25. • Internationalizing text: “Principle: Drupal Is International” on page 48. • Find Drupal core theme hooks on the “Default theme implementations” (Drupal 7) or “Theme system overview” (Drupal 8) topic on https://api.drupal.org. • CSS and JavaScript libraries in Drupal 7: http://bit.ly/js_in_drupal7. • CSS and JavaScript libraries in Drupal 8: http://bit.ly/css_js_in_drupal8. Examples—render arrays: • “Generating Paged Output” on page 107 • “Form Arrays, Form State Arrays, and Form State Objects” on page 110 • “Wrapper-Based Ajax Callback Functions” on page 127 Registering for URLs and Displaying Content | 105 • “Defining a field formatter in Drupal 7” on page 168 • “Defining a field formatter in Drupal 8” on page 169 It is still possible in Drupal 7 to return strings from your page and block content functions instead of render arrays. Using render arrays is preferred, however, because: • They are self-documenting. • They allow modules to use hook_page_alter() to alter the page before it is rendered. • They leave final rendering until late in the page-generation process, so unnecessary rendering can be avoided if a particu‐ lar section of the page is not actually displayed. Render Arrays in Drupal 8 As noted in the previous section, both the general concept and nearly all of the details of render arrays are the same in Drupal 7 and 8. Here are some differences: • If your render array is being generated in a class that has a t() method, you should call this method in place of the global t() function, to internationalize user-interface text. • In Drupal 8, attaching CSS and JavaScript files has to be done via a library. You’ll need to put all of your JavaScript and CSS into files, and then define a library in a mymodule.libraries.yml file in your main module directory that looks like this: myjslib: version: 1.x js: mymodule.js : {} • Once you have a library defined, you can attach it to a render array like this: $form['#attached']['library'][] = 'mymodule/myjslib'; • Data caching in Drupal 8 is more sophisticated and granular than in Drupal 7, and page caching is done on a more granular level. To enable this, render array elements need to include information about what cache tags and cache contexts they depend on, so that the Drupal rendering system can decide whether to ren‐ der them during a page load or use a cached version. 106 | Chapter 4: Drupal Programming Examples Further reading and reference: • “Drupal 8 Cache API” on page 11 Generating Paged Output If a page or block you are generating output for is listing data, you need to think about what should happen if the list of data gets long; usually you would want the output to be separated into pages. If you are using a database query to generate the list, Drupal’s Database API and theme system make separating the output into pages very easy. Here are the steps: 1. Use a dynamic query with db_select(), rather than a static query with db_query(). In Drupal 8, the select() method on a database service object is preferable to using db_select(), if you have dependency injection available. 2. In Drupal 7, add the PagerDefault extender to your database query. In Drupal 8, it is the \Drupal\Core\Database\Query\PagerSelectExtender extender. 3. Add an entry with '#theme' set to pager to your output render array in Drupal 7, or '#type' set to pager in Drupal 8. This will add a section to your page con‐ taining links to the pages of output. Each link will take you to your base page URL, with the URL query parameter ?page=N appended (N starts at 0 for the first page, 1 for the second, etc.). The pager query extender will automatically read this URL query parameter to figure out what page of output the user is on and return the appropriate rows in the database query. As an example, assume you want to show the titles of the most recently created node content items, and you want to show 10 items per page. You should really use the Views module to do this, but for purposes of illustration, here is the code you would need to put into your output-generating function for the block or page in Drupal 7: // Find the most recently created nodes. $query = db_select('node', 'n') ->fields('n', array('title')) ->orderBy('n.created', 'DESC') // Be sure to check permissions, and only show published items. ->addTag('node_access') ->condition('n.status', 1) // Put this last, because the return value is a new object. ->extend('PagerDefault'); // This only applies with the PagerDefault extender. $query->limit(10); $result = $query->execute(); // Extract the information from the query result. $titles = array(); Registering for URLs and Displaying Content | 107 foreach ($result as $row) { $titles[] = check_plain($row->title); } // Make the render array for a paged list of titles. $build = array(); // The list of titles. $build['items'] = array( '#theme' => 'item_list', '#items' => $titles, ); // The pager. $build['item_pager'] = array('#theme' => 'pager'); return $build; In Drupal 8, you should also use the Views module to accomplish this, or an entity query. But for illustration purposes, here is the equivalent code in Drupal 8: // If you have a database connection object $database: $query = $database->select('node', 'n'); // Otherwise: $query = db_select('node', 'n'); // Continuing on... $query->innerJoin('node_field_data', 'nd', 'n.nid = nd.nid AND n.vid = nd.vid'); $query = $query ->extend('Drupal\Core\Database\Query\PagerSelectExtender') // Add pager. ->addMetaData('base_table', 'node') // Needed for join queries. ->limit(10) // 10 items per page. ->fields('nd', array('title', 'nid')) // Get the title field. ->orderBy('nd.created', 'DESC') // Sort by last updated. ->addTag('node_access') // Enforce node access. ->condition('nd.status', 1); $result = $query->execute(); // Extract and sanitize the information from the query result. $titles = array(); foreach ($result as $row) { $titles[] = $row->title; } // Make the render array for a paged list of titles. $build = array(); $build['items'] = array( '#theme' => 'item_list', '#items' => $titles, ); // Add the pager. $build['item_pager'] = array('#type' => 'pager'); return $build; 108 | Chapter 4: Drupal Programming Examples Further reading and reference: • “Dynamic queries” on page 59. • “Cleansing and Checking User-Provided Input” on page 62. • It is usually better to use the Views module rather than doing your own page queries: “Avoiding Custom Programming with Fielded Data” on page 75. • “Querying and Loading Entities in Drupal 8” on page 160. Using the Drupal Form API One of the real strengths of Drupal for programmers is the Form API, which is a vast improvement over the process you would see in a standard reference on PHP pro‐ gramming for output and processing of web forms. The first step in using the Drupal Form API is to create a form-generating function (Drupal 7) or a form class with a form-generating method (Drupal 8); this function or method generates a structured form array containing information about the form elements and other markup to be displayed. Then, you will write form validation and submission functions or meth‐ ods, which are called automatically by Drupal when your form is submitted, allowing you to securely process the submitted data. The advantages of using the Form API over doing all of this in raw HTML and PHP are: • You can write a lot less code, as you’re letting Drupal handle all of the standard parts of form creation and submission. For instance, you do not have to write code to read the $_GET or $_POST variable, and you do not have to create a dedi‐ cated PHP file with root-level code for the form’s action attribute. • Your code will be easier to read and maintain: Drupal’s form arrays are much eas‐ ier to deal with than raw HTML forms. • As with other parts of Drupal, your form will be alterable by other modules, and the exact rendering is controlled by the theme system. • When the form is rendered, Drupal adds a unique token to protect against crosssite scripting, and this is validated during form submission. • Other security checks are also performed on form submission, such as matching up submitted values to allowed fields, omitting submissions to disabled form ele‐ ments, and so on. • The HTML output of the Drupal Form API is set up to be accessible, with no extra effort required on your part. Using the Drupal Form API | 109 Form Arrays, Form State Arrays, and Form State Objects Form arrays have the same general structure as the render arrays discussed in “Creat‐ ing Render Arrays for Page and Block Output” on page 102—they are a specialized type of render array. (Actually, form arrays predate render arrays in Drupal history, so you could also say that render arrays are a generalization of form arrays.) There are special render element types that should only be used in form arrays, representing HTML form input elements or groups of input elements. Here is an example of a form array: $form = array(); // Plain text input element for first name. $form['first_name'] = array( '#type' => 'textfield', '#title' => t('First name'), ); // Plain text element for company name, only visible to some // users. $form['company'] = array( '#type' => 'textfield', '#title' => t('Company'), // This assumes permission 'use company field' has been defined. '#access' => user_access('use company field'), ); $my_information = 'stuff'; // Some hidden information to be used later. $form['information'] = array( '#type' => 'value', '#value' => $my_information, ); // Submit button. $form['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), ); Notes: • The preceding code is for Drupal 7. In Drupal 8, only one line changes: user_access() is replaced by \Drupal::currentUser()->hasPermission(), or the equivalent call on a dependency-injected current_user service. • The type of element is specified in the '#type' property. The types supported by Drupal are listed in the Drupal Form API Reference, which also tells which prop‐ 110 | Chapter 4: Drupal Programming Examples erties each element uses. You can find the Drupal-version-specific Form API Ref‐ erence on https://api.drupal.org. • The value form element type can be used to pass information to form validation and submission functions. This information is not rendered at all into the form’s HTML, and no input is accepted from these elements (in contrast to form elements of type hidden, which render as HTML input elements with type="hidden"), so they are more secure and can contain any PHP data structure. • Most elements have '#title' properties, which are displayed as form element labels. • All form elements have an '#access' property. If it evaluates to TRUE or is omit‐ ted, the element is shown; if it evaluates to FALSE, the element is not displayed. In Drupal 8, you can alternatively provide an '#access_callback' property, which is a PHP callable (or function name), which is called to determine access (the ele‐ ment being access checked is passed in as the sole argument). • You can include markup and other render elements in your form arrays. The form generation, validation, and submission functions or methods receive infor‐ mation about the form submission in a form state variable, $form_state, which is retained through the whole process. In Drupal 7, $form_state is an array; in Drupal 8, it is an object that implements \Drupal\Core\Form\FormStateInterface. You can add custom information to the form state during any step for later use. In Drupal 7, add information directly as array elements in $form_state, and retrieve it later from the array. In Drupal 8, use the set() method to add information and get() to retrieve it later. The main piece of information you will use from $form_state is the values entered in the form elements by the user, which are in $form_state['values'] in Drupal 7, and retrieved by a call to the getValues() method in Drupal 8. Either way, the submitted values are an array, keyed by the same keys used in the form array for the form input elements. One difficulty in the values array is that if the form has a nested structure, the values can be either nested or flat, depending on the '#tree' property of the form as a whole, or any parent subarray. Because this would make extracting form values com‐ plicated, after the initial processing step Drupal’s Form API provides a '#parents' property, on each element or subelement in a form array, which is an array giving the nested parents needed to find the submitted values in the values array. Assuming that $parents holds this property for the element you’re trying to get the value of, you can extract the value by calling: Using the Drupal Form API | 111 // Drupal 7 $value = drupal_array_get_nested_value($form_state['values'], $parents); // Drupal 8 $values = $form_state->getValues(); $value = \Drupal\Component\Utility\NestedArray::getValue($values, $parents); Further reading and reference: • “Drupal 8 Services and Dependency Injection” on page 35 • “Creating Render Arrays for Page and Block Output” on page 102 Examples—form arrays (besides the rest of the form section): • “Programming with Ajax in Drupal” on page 123 • “Defining an Entity Type in Drupal 7” on page 132 • “Defining a Content Entity Type in Drupal 8” on page 139 • “Defining a Configuration Entity Type in Drupal 8” on page 149 • “Defining a field widget in Drupal 7” on page 164 • “Defining a field widget in Drupal 8” on page 165 Basic Form Generation and Processing in Drupal 7 Here are the steps needed to generate and process a form in Drupal 7: 1. Choose an ID for your form, which is a string that should typically start with your module name. For example, you might choose mymodule_personal_data_form. 2. In your mymodule.module file, define a form-generating function (also known as a form constructor) with the same name as the form ID, which returns the form array (see “Form Arrays, Form State Arrays, and Form State Objects” on page 110): function mymodule_personal_data_form($form, &$form_state) { // Generate your form array here. return $form; } 3. If you need to perform any data validation steps on form submissions, create a form validation function called mymodule_personal_data_form_validate(). This function should call form_set_error() if the submission is invalid, and it should do nothing if all is well. 112 | Chapter 4: Drupal Programming Examples 4. Create a form-submission function called mymodule_personal_data_form_ submit() to process the form submissions (for instance, to save information to the database). Here is an example of a form submission function: function mymodule_personal_data_form_submit(&$form, &$form_state) { // The values submitted by the user are in $form_state['values']. $name = $form_state['values']['first_name']; // Values you stored in the form array are also available. $info = $form_state['values']['information']; // Your processing code goes here, such as saving this to the database. // Sanitize values before display, but not before storing to the database! } 5. Call drupal_get_form('mymodule_personal_data_form') to build the form— do not call your form-generating function directly. Your validation and submis‐ sion functions will be called automatically when a user submits the form. If your form is the sole content of a page whose path you are registering for in a hook_menu() implementation, you can use drupal_get_form() as the pagegenerating function: // Inside your hook_menu() implementation: $items['mymodule/my_form_page'] = array( 'page callback' => 'drupal_get_form', 'page arguments' => array('mymodule_personal_data_form'), // Don't forget the access information, title, etc.! ); Further reading and reference: • hook_menu(): “Registering for a URL in Drupal 7” on page 89 Examples—form processing (besides the rest of the form section): • “Defining a field widget in Drupal 7” on page 164. • “Defining an Entity Type in Drupal 7” on page 132. • Form-generating functions in Drupal core are listed in the “Form builder func‐ tions” topic on https://api.drupal.org. • The Form example in Examples for Developers. Be careful about caching form output, because drupal_get_form() adds verification information to the form output, and this informa‐ tion is invalid after some time has passed. If your form is displayed in a block, be sure that the block is not cached; this is not a prob‐ lem if the form is part of the main page content. Using the Drupal Form API | 113 Basic Form Generation and Processing in Drupal 8 In Drupal 8, to generate and process a form, create a form class. By convention, form classes usually go into the \Drupal\mymodule\Form namespace, and of course you should pick a class name that describes the form. You’ll also need to choose a unique ID for your form, which should generally start with your module’s machine name. Here is a simple example of a form class for a personal data form; this needs to go into the src/Form/PersonalDataForm.php file under your module directory: namespace Drupal\mymodule\Form; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; class PersonalDataForm extends FormBase { // getFormId() returns the form ID you chose, which must be unique. public function getFormId() { return 'mymodule_personal_data_form'; } // buildForm() generates the form array. public function buildForm(array $form, FormStateInterface $form_state) { // Generate your form array here. return $form; } // submitForm() processes the form submission. public function submitForm(array &$form, FormStateInterface $form_state) { // Extract the values submitted by the user. $values = $form_state->getValues(); $name = $values['first_name']; // Values you stored in the form array are also available. $info = $values['information']; // Your processing code goes here, such as saving this to the database. // Sanitize values if you are displaying them, but do not sanitize before // saving to the database! } } 114 | Chapter 4: Drupal Programming Examples Notes: • The three methods defined here are essential. getFormID() returns the unique ID you chose for your form. buildForm() returns the form array; see “Form Arrays, Form State Arrays, and Form State Objects” on page 110 for details. submit Form() processes the form submission. • You can also include a validateForm() method, if your form needs validation. If you find a validation error, call the setErrorByName() method on $form_state; if there are no validation errors, just return. • In a form class, in place of calling the t() function to translate user interface text, use the t() method from the FormBase class. • If you are creating a form for simple (non-entity) configuration, you should extend \Drupal\Core\Form\ConfigFormBase instead of the generic FormBase class. Once you have a form class defined, you need to do one of the following to display your form: • If your form is the sole content of a page, you can put the class name into your mymodule.routing.yml file. For example, to make the personal data form appear on path mymodule/my_form_page, visible to anyone, you would use the following entry: mymodule.personal_data_form: path: '/mymodule/my_form_page' defaults: _form: '\Drupal\mymodule\Form\PersonalDataForm' _title: 'Personal data form' requirements: _access: 'TRUE' • If your form is to appear in a block or inside some other content you are generat‐ ing, you can add it to a render array as follows: // Code without dependency injection or $container variable: $my_render_array['personal_data_form'] = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\PersonalDataForm'); // With $container variable (dependency injection): $builder = $container->get('form_builder'); $my_render_array['personal_data_form'] = $builder->getForm('Drupal\mymodule\Form\PersonalDataForm'); Further reading and reference: • “Registering for a URL in Drupal 8” on page 92 Using the Drupal Form API | 115 • “Creating Render Arrays for Page and Block Output” on page 102 • “Drupal 8 Services and Dependency Injection” on page 35 Examples—form processing (besides the rest of the form section): • “Defining a field widget in Drupal 8” on page 165 • “Defining a Content Entity Type in Drupal 8” on page 139 • “Defining a Configuration Entity Type in Drupal 8” on page 149 Creating Confirmation Forms For security reasons, it is important to verify destructive actions connected with a URL. For instance, if your module has a URL that allows an administrative user to delete some data or a file, you should confirm this intention before deleting the data. One reason is that the user could have been tricked into visiting that URL by a crosssite scripting attack; also, sometimes people click links by mistake, and confirming before destroying all their data is the polite thing to do. Drupal makes this type of confirmation easy. Here are the steps for Drupal 7: 1. Instead of registering your path with a function that performs the deletion directly, use drupal_get_form() as the page callback, passing in the name of a form-generating function. 2. Have your form-generating function call confirm_form() to generate a confir‐ mation form. 3. Perform the data deletion in the form-submission function, which will only be called if the action is confirmed (which also means that the unique form token will be validated). Here’s an example of the code: // The menu router registration. function mymodule_menu() { // ... // Assume there is a content ID number. $items['admin/content/mycontent/delete/%'] = array( 'title' => 'Delete content item?', 'page callback' => 'drupal_get_form', // Pass the content ID number to the form-generating function. // It is position 4 in the path (starting from 0). 'page arguments' => array('mymodule_confirm_delete', 4), // Permission needs to be defined by the module. 'access arguments' => array('delete mycontent items'), ); 116 | Chapter 4: Drupal Programming Examples // ... } // Form-generating function. function mymodule_confirm_delete($form, $form_state, $id) { // Save the ID for the submission function. $form['mycontent_id'] = array( '#type' => 'value', '#value' => $id, ); return confirm_form($form, // You could load the item and display the title here. t('Are you sure you want to delete content item %id?', array('%id' => $id)), // The URL path to return to if the user cancels. 'mymodule/mypath'); } // Form-submission function. function mymodule_confirm_delete_submit($form, $form_state) { // Read the ID saved in the form. $id = $form_state['values']['mycontent_id']; // Sanitize. $id = (int) $id; // Perform the data deletion. // ... // Redirect somewhere. drupal_goto('mymodule/my_form_page'); } In Drupal 8, this same form can be generated by the following class, which needs to go into the src/Form/ConfirmDeleteForm.php file under the main module directory: namespace Drupal\mymodule\Form; use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; class ConfirmDeleteForm extends ConfirmFormBase { protected $to_delete_id; public function getFormId() { return 'mymodule_confirm_delete'; } // Note that when adding arguments to buildForm(), you need to give Using the Drupal Form API | 117 // them default values, to avoid PHP errors. public function buildForm(array $form, FormStateInterface $form_state, $id = '') { // Sanitize and save the ID. $id = (int) $id; $this->to_delete_id = $id; $form = parent::buildForm($form, $form_state); return $form; } public function getQuestion() { return t('Are you sure you want to delete content item %id?', array('%id' => $this->to_delete_id)); } public function getCancelUrl() { return new Url('mymodule.mydescription'); } public function submitForm(array &$form, FormStateInterface $form_state) { $id = $this->to_delete_id; // Perform the data deletion. // ... // Redirect somewhere. $form_state->setRedirect('mymodule.personal_data_form'); } } You’ll also need to display this form on a page. Here’s a mymodule.routing.yml entry, with a placeholder corresponding to the form builder input variable name $id: mymodule.delete_confirm: path: '/admin/content/mycontent/delete/{id}' defaults: _form: '\Drupal\mymodule\Form\ConfirmDeleteForm' _title: 'Delete content item?' requirements: _permission: 'delete mycontent items' Further reading and reference: • “Form Arrays, Form State Arrays, and Form State Objects” on page 110 • “Basic Form Generation and Processing in Drupal 7” on page 112 • “Basic Form Generation and Processing in Drupal 8” on page 114 • “Principle: Drupal Is Secure; User Input Is Insecure” on page 61 • “Registering for a URL in Drupal 7” on page 89 118 | Chapter 4: Drupal Programming Examples • “Registering for a URL in Drupal 8” on page 92 Examples—confirmation forms: • “Defining a Content Entity Type in Drupal 8” on page 139 • “Defining a Configuration Entity Type in Drupal 8” on page 149 Adding Autocomplete to Forms If you have a form where a user needs to select a value from several choices, there are several ways to do it: • If there are fewer than about seven choices, you should use checkboxes if the user can select multiple items, and radio buttons if the user can only select one item. • If there are more than seven choices, you should use a select list. • However, if you have a very large number of choices, select lists do not perform well in browsers; in this case, you should use an autocomplete text field: choices pop up as the user begins to enter text. • You may also want to use an autocomplete text field if the user is free to make an entry that was not one of the suggested choices. This will let users see existing choices, prompting them to select one if it exists, while still letting them create a new entry. To make a text input field have autocomplete behavior in Drupal, here are the steps: 1. In Drupal 7, add an '#autocomplete_path' property to your 'textfield' form element array, with a URL path in it. In Drupal 8, the property is '#autocomplete_route_name', and the value is a route machine name. Or to autocomplete on an entity in Drupal 8, add an 'entity_autocomplete' render element to your array. This looks like: // In a form-generating function: $form['my_autocomplete_field'] = array( '#type' => 'textfield', '#title' => t('Autocomplete field'), // Drupal 7: '#autocomplete_path' => 'mymodule/autocomplete', // Drupal 8: '#autocomplete_route_name' => 'mymodule.autocomplete', ); // For an entity autocomplete in Drupal 8 for Nodes: $form['my_node_element'] = array( '#type' => 'entity_autocomplete', Using the Drupal Form API | 119 '#target_type' => 'node', // Limit this to just Articles. '#selection_settings' => array( 'target_bundles' => array('article'), ), ); 2. Register for this URL path in your hook_menu() implementation (Drupal 7) or for the route in your mymodule.routing.yml file (Drupal 8). This looks like: // Inside Drupal 7 hook_menu() implementation: $items['mymodule/autocomplete'] = array( 'page callback' => 'mymodule_autocomplete', 'access arguments' => array('use company field'), 'type' => MENU_CALLBACK, ); # Inside Drupal 8 mymodule.routing.yml file: mymodule.autocomplete: path: '/mymodule/autocomplete' defaults: _controller: '\Drupal\mymodule\Controller\MyUrlController::autocomplete' requirements: _permission: 'use company field' 3. Define the page-callback function or page-generation method. In Drupal 7, it will take one argument (the string the user has typed); in Drupal 8, the argument will be the Symfony request. In either case, it should return an array of responses in JSON format, as in these examples: // Drupal 7: function mymodule_autocomplete($string = '') { $matches = array(); if ($string) { // Sanitize $string and find appropriate matches -- about 10 or fewer. // Put them into $matches as $key => $visible text. // ... } drupal_json_output($matches); } // Drupal 8, top of MyUrlController class file: use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; // Drupal 8, new method in MyUrlController class: public function autocomplete(Request $request) { $string = $request->query->get('q'); 120 | Chapter 4: Drupal Programming Examples $matches = array(); if ($string) { // Sanitize $string and find appropriate matches -- about 10 or fewer. // Put them into $matches as items, each an array with // 'value' and 'label' elements. // ... } return new JsonResponse($matches); } Further reading and reference: • “Registering for a URL in Drupal 7” on page 89. • “Registering for a URL in Drupal 8” on page 92. • Autocomplete is a special case of Ajax (see “Programming with Ajax in Drupal” on page 123). Autocomplete is covered here because in Drupal it is handled in a completely different manner from other Ajax responses. Examples—autocomplete: • There are several examples of autocompletes in Drupal core version 7, such as the author name field in node_form(), which auto-completes on user names at path user/autocomplete. This path is registered in user_menu(), and its pagecallback function is user_autocomplete(). • In Drupal 8, most ad-hoc autocomplete functionality has been removed in favor of the generic entity autocomplete described in the preceding text. But the Block module still has a special autocomplete controller for block categories. The routing is in core/modules/block/block.routing.yml (route name block.category_autocomplete) with the \Drupal\block\Controller \CategoryAutocompleteController::autocomplete() page-controller method. • There is a complete standalone autocomplete example in the Ajax example in Examples for Developers. Altering Forms One of the more common reasons that someone building a Drupal site would decide to create a custom module is to alter a form that is displayed by Drupal core or another module. Typically, the reason is that the site owner or site designer decides that some of the text on the form is confusing, wants some part of the form hidden, wants to change the order of fields on a form, or wants some additional validation to be done on form submissions. All of these alterations can be done by implementing a form alter hook. Using the Drupal Form API | 121 Before deciding you need a custom form-altering module, however, you should check to see if you can alter the form in a different way. Some core and contributed mod‐ ules, for example, have configuration options that will let you alter labels on forms, and you can also use the String Overrides contributed module to make global text changes (such as changing all Submit buttons to say Send). If you want to add text at the top of a form, you might be able to use a block. Also, content-editing forms are configurable in the administrative user interface: you can add help text to fields, change field labels, change the order of fields, add and remove fields from content types, and change the displayed name of the content type, among other settings. In Drupal 8, you can also define form modes, which allow you to make different content editing forms for different situations, in the user interface, with different subsets of fields displayed, different labels, and different ordering. Each content type also has several settings for comments that affect the comment form, and there are many other examples of configuration options—so be sure to investigate before you start programming. If you do need to alter a form via an alter hook in your custom module, here are the steps (in Drupal 7 or 8): 1. Figure out the form ID of the form you are altering. The easiest way to do this is to look at the HTML source of the page with the form—the ID will be the id attribute of the HTML form tag. For this example, let’s assume the ID is the_form_id. 2. Implement hook_form_FORM_ID_alter() by declaring a function called mymodule_form_the_form_id_alter() in your mymodule.module file. Some forms, like field widget forms, use a different alter hook, such as hook_field_widget_form_alter(); these hooks work the same way as hook_form_FORM_ID_alter(). 3. Alter the form array in this function. Implementing hook_form_FORM_ID_alter() can sometimes lead to some crazy-looking function names. For instance, in both Drupal 7 and 8, there is a module used for testing called form_test.module. This module defines a form whose ID is form_test_alter_form, and then it implements hook_form_FORM_ID_alter() to test form alteration hook functionality. The name of the implementing function is therefore composed of the module’s machine name, then form, then the form ID, then alter, resulting in form_test_form_form_test_alter_form_alter()—kind of a mouthful! 122 | Chapter 4: Drupal Programming Examples As an example, assume that you want to change the user-registration form on a site so that it only allows people to register using email addresses within your company’s domain. The form ID in this case is user_register_form, and here is the alter func‐ tion you would need to define: // Form alteration in Drupal 7. Drupal 8 is the same except the // function signature, see below. function mymodule_form_user_register_form_alter(&$form, &$form_state, $form_id) { // Change the label on the email address field. $form['account']['mail']['#title'] = t('Company e-mail address'); // Add a validation function. $form['#validate'][] = 'mymodule_validate_register_email'; } // Drupal 8 use statement needed: use Drupal\Core\Form\FormStateInterface; // Drupal 8 function signature: function mymodule_form_user_register_form_alter(&$form, FormStateInterface $form_state, $form_id) // Validation function in Drupal 7. function mymodule_validate_register_email($form, $form_state) { $email = $form_state['values']['mail']; // Check that the email is within the company domain. // If not, call form_set_error('mail', t('message goes here')); } // Validation function in Drupal 8. function mymodule_validate_register_email($form, FormStateInterface $form_state) { $values = $form_state->getValues(); $email = $values['mail']; // Check that the email is within the company domain. // If not, call $form_state->setErrorByName('mail', t('message goes here')); } Further reading and reference: • “Repurposing an existing field widget” on page 167 Programming with Ajax in Drupal Ajax is a technique whereby a web page can make an asynchronous (background) request to a URL without a full page load, in response to a JavaScript event (mouse click, typing, etc.) on an HTML element. When the request finishes, JavaScript Programming with Ajax in Drupal | 123 commands are run, typically updating part of the page. Autocomplete text fields in forms, covered in “Adding Autocomplete to Forms” on page 119, are one example of Ajax handling in Drupal; generic Ajax responses in forms work differently and are covered in this section. Ajax was formerly known as AJAX, or Asynchronous JavaScript and XML. However, these days few people actually work with XML when doing their asynchronous requests, so what was once the acronym AJAX has become Ajax, and it is used to mean any kind of asynchronous JavaScript request (whether or not XML is involved). Like many aspects of Drupal, because Drupal is written in PHP and browsers support JavaScript, you can technically use any Ajax programming techniques you know to accomplish your Ajax requirements. However, you should use the Drupal Ajax frame‐ work instead, which provides several benefits: • Defining the Ajax responses for HTML elements is done in the form array, as part of the Drupal Form API, which is much easier than doing it from scratch. • Drupal performs its standard security checks for forms and HTTP requests. • In place of completely handling the server end (the URL request), you can let Drupal use its standard Ajax URL and just provide the PHP function to be called to handle the processing. You can also use your own URL if you wish; in that case, you would use the standard Drupal routing system to set up your Ajax URL. • In place of writing all the JavaScript commands for the browser when the response is returned, the Drupal Ajax framework provides an easy and flexible way to define the browser actions. The remainder of this section describes how to use the Drupal Ajax framework. Setting Up a Form for Ajax Drupal Ajax responses are specified in forms. To set up an Ajax response in a form, the main thing you need to do is add the #ajax property (an array) to the form ele‐ ment that should trigger the response. In the #ajax array, provide values for one or more of the following keys: • callback: The name of the PHP function or method to call when the event occurs. • In place of callback, if you want to use your own URL, in Drupal 7 you can instead define a value for the path key (a URL path string), and register for the URL in the standard Drupal way. In Drupal 8, you’d use the url key and its value would be a \Drupal\Core\Url object. 124 | Chapter 4: Drupal Programming Examples • wrapper: If you want the browser response to replace part of the page markup on return, set up a
with an id attribute, and provide this ID. By default, the entire
will be completely replaced with returned content (including the
tags), but you can also supply a value for method, equal to 'append', 'prepend', 'before', 'after', or the name of another JQuery manipulation function to change this. Omit wrapper if your response is more complex than just replacing markup. • event: Form elements have default events to respond to, but you can override the default by specifying the name of a JavaScript event. • prevent: Optionally, specify an event to prevent from being triggered (e.g., if you respond to 'mousedown' events, you might want to prevent 'click' events from being triggered when the mouse comes back up). • There are additional Ajax elements governing effect speeds, progress indicators, and other factors of the Ajax response. If you are using your own URL for Ajax, via the path (Drupal 7) or url (Drupal 8) element in your #ajax property instead of callback, you’ll need to construct your URL so that all the infor‐ mation you need is part of the HTTP request (generally, you’ll put the information into URL query parameters); it is usually simpler to use callback. As the start of an example (continued in the rest of this section), here’s a form array with two Ajax-triggering elements, and two HTML
elements to put responses in: $form['ajax_output_1'] = array( '#type' => 'markup', '#markup' => '
', ); $form['text_trigger'] = array( '#type' => 'textfield', '#title' => t('Type here to trigger Ajax'), '#ajax' => array( 'event' => 'keyup', 'wrapper' => 'ajax-output-spot', 'callback' => 'mymodule_ajax_text_callback', ), ); $form['ajax_output_2'] = array( '#type' => 'markup', '#markup' => '
', Programming with Ajax in Drupal | 125 ); $form['button_trigger'] = array( '#type' => 'button', '#value' => t('Click here to trigger Ajax'), '#ajax' => array( 'callback' => 'mymodule_ajax_button_callback', ), ); If you are doing this in Drupal 8, the callbacks could also be public static class meth‐ ods on the form class, such as: 'callback' => 'Drupal\mymodule\Form\PersonalDataForm::ajaxTextCallback', 'callback' => 'Drupal\mymodule\Form\PersonalDataForm::ajaxButtonCallback', The Ajax callback specified in the callback element of the #ajax property on a form element is a PHP function that receives $form and $form_state as arguments. $form_state['triggering_element'] (Drupal 7) or $form_state>get('triggering_element') (Drupal 8) will tell you which form element triggered the Ajax response, so you can use the same callback to handle Ajax responses to sev‐ eral different form elements, if you wish. You can also retrieve all of the currently entered form values in $form_state as you would in any other form processing. The following sections give a few examples of these callbacks. In most cases, the other thing you need to do for Ajax to work properly is to make sure that the form is rebuilt properly during Ajax handling. To do that, you’ll need to set $form_state['rebuild'] to TRUE in your form submit handler function (Drupal 7), or call the setRebuild() method on the form state object in Drupal 8. Further reading and reference: • “Form Arrays, Form State Arrays, and Form State Objects” on page 110 • “Basic Form Generation and Processing in Drupal 8” on page 114 • “Registering for a URL in Drupal 7” on page 89 • “Registering for a URL in Drupal 8” on page 92 • JQuery manipulation functions: http://bit.ly/jquery_manipulation • Online documentation for Ajax in Drupal 7 (also mostly applies to Drupal 8): http://bit.ly/js_api_in_drupal7 Examples—Ajax (besides the rest of this section): • The Ajax example in Examples for Developers 126 | Chapter 4: Drupal Programming Examples Wrapper-Based Ajax Callback Functions If you use the wrapper element of the #ajax property, your callback returns the HTML markup to replace the wrapper
, or (preferably) a render array that gen‐ erates the desired HTML markup. In addition, any calls to drupal_set_message() during processing will result in messages being prepended to the returned markup. Here’s an example callback function for Drupal 7: function mymodule_ajax_text_callback($form, &$form_state) { // Read the text from the text field. $text = $form_state['values']['text_trigger']; if (!$text) { $text = t('nothing'); } // Set a message. drupal_set_message(t('You have triggered Ajax')); // Return a render array for markup to replace the wrapper
contents. return array( '#type' => 'markup', // Text was not sanitized, so use @variable in t() to sanitize. // Be sure to include the wrapper div! '#markup' => '
' . t('You typed @text', array('@text' => $text)) . '
', ); } Only the first few lines are different in Drupal 8, to make it a static method on the PersonalDataForm class defined in “Basic Form Generation and Processing in Drupal 8” on page 114 and use the Drupal 8 $form_state interface: public static function ajaxTextCallback(array $form, FormStateInterface $form_state) { // Read the text from the text field. $text = $form_state->getValues()['text_trigger']; Further reading and reference: • “Form Arrays, Form State Arrays, and Form State Objects” on page 110 • “Creating Render Arrays for Page and Block Output” on page 102 Command-Based Ajax Callback Functions in Drupal 7 If you are not using a wrapper element in your #ajax property, your Drupal 7 Ajax callback function should return a set of Ajax commands from the Drupal Ajax frame‐ work. Note that unlike when using wrapper, if you are using a command-based Programming with Ajax in Drupal | 127 callback, drupal_set_message() does not automatically trigger messages to be dis‐ played. Here’s an example of a callback using commands for Drupal 7: function mymodule_ajax_button_callback($form, &$form_state) { $commands = array(); // Replace HTML markup inside the div via a selector. $text = t('The button has been clicked'); $commands[] = ajax_command_html('div#other-ajax-spot', $text); // Add some CSS to the div. $css = array('background-color' => '#ddffdd', 'color' => '#000000'); $commands[] = ajax_command_css('div#other-ajax-spot', $css); return array('#type' => 'ajax', '#commands' => $commands); } Each command in the returned array is the return value of one of the Drupal Ajax command functions. You can find all of these functions listed in the “Ajax framework commands” topic on https://api.drupal.org. Command-Based Ajax Callback Functions in Drupal 8 If you are not using a wrapper element in your #ajax property, your Drupal 8 Ajax callback function should return a set of Ajax commands from the Drupal Ajax frame‐ work, in the form of an object of class \Drupal\Core\Ajax\AjaxResponse. Note that unlike when using wrapper, if you are using a command-based callback, drupal_set_message() does not automatically trigger messages to be displayed. Here is an example of a callback using commands, as a method on the PersonalDataForm class defined in “Basic Form Generation and Processing in Drupal 8” on page 114: use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\HtmlCommand; use Drupal\Core\Ajax\CssCommand; public static function ajaxButtonCallback(array $form, FormStateInterface $form_state) { $response = new AjaxResponse(); // Replace HTML markup inside the div via a selector. $text = t('The button has been clicked'); $response->addCommand( new HtmlCommand('div#other-ajax-spot', $text)); // Add some CSS to the div. $css = array('background-color' => '#ddffdd', 'color' => '#000000'); $response->addCommand( new CssCommand('div#other-ajax-spot', $css)); 128 | Chapter 4: Drupal Programming Examples return $response; } Each command you add to the AjaxResponse object via the AjaxResponse :: add Command() method is an object that implements \Drupal\Core\Ajax\CommandInter face. The available commands can be found in the core/lib/Drupal/Core/Ajax direc‐ tory in the Drupal source code (find classes there whose names end in Command). Programming with Entities and Fields This section covers programming with Drupal entities and fields. The sections on defining entity types, field types, widgets, and formatters are independent of one another, so skim the terminology section first, and then you can skip to the section you need. The code samples in this section complement, but do not duplicate, the well-documented Entity and Field examples in the Examples for Developers project. Further reading and reference: • “Avoiding Custom Programming with Fielded Data” on page 75 Examples—entity and field programming (besides the rest of this section): • The Drupal core entities and fields are all good to look at; Drupal 8 has even more Drupal core examples than Drupal 7. • Entity example in Examples for Developers. Note that the Drupal 7 example is a bit different from the example in this section, because it does not make use of the contributed Entity API module. • Field example in Examples for Developers. • The Node example in Examples for Developers shows how to create a content type for the core Node entity in a module. There is sometimes confusion between entity fields and database table fields. Within this section, the term field will always mean an entity field as defined in this section, and any references to database table fields will be clearly noted as such. Terminology of Entities and Fields The Drupal entity and field systems introduced quite a bit of terminology that you’ll need to be familiar with if you’re planning on doing any entity or field programming. Here’s a list of terms and other background information: Programming with Entities and Fields | 129 Entity As of Drupal version 7, Drupal core defines the concept of an entity, which stores data (such as content or settings) for a Drupal website. Entity type Each entity type represents a particular kind of data, and comes with a small number of properties, such as an ID, a universal identifier (UUID), and a label or title. Content and configuration entities Drupal 8 formally divides entities into content entities (for content that should be displayed to site visitors) and configuration entities (for site configuration). This distinction is not explicit in Drupal 7, and entities in Drupal 7 are really meant only for content. Drupal core entity types Drupal core version 7 defines five main user-visible entity types: node (for basic content), taxonomy_term and taxonomy_vocabulary (for classification of con‐ tent), comment (for comments attached to nodes), and user (for user account information). Drupal 7 core also defines the file entity type, which is used inter‐ nally to manage uploaded files. Drupal 8 core defines many additional entity types, most of which are configuration entities. Also, in Drupal 8, the comment entity type is generalized: comments can be attached to any entity type, not just nodes. Module-defined entity types The Drupal API allows modules to define additional entity types. The API is quite different for Drupal 7 and Drupal 8 and is described in the following sections. Bundle Each content entity type can have one or more bundles, which are groupings of the entity objects within a given entity type. For instance, the bundles of the node entity type are content types, which an administrator can define within the Dru‐ pal user interface (modules can define them too); examples of content types are basic pages, news items, blog posts, and forum posts. The bundles of the taxon‐ omy entity type are vocabularies, and the objects are the individual taxonomy terms. The user entity type doesn’t use bundles, and its objects are user accounts. Each entity object belongs to exactly one bundle (assuming that you count enti‐ ties that don’t use bundles as having all objects belonging to the same, default bundle). Fields, base fields, and properties Most content entity types are fieldable, meaning that fields can be added to each bundle of the entity type (the fields can be different for each bundle within an 130 | Chapter 4: Drupal Programming Examples entity type). In Drupal 7, fields supplement the intrinsic properties of the entity type; in Drupal 8, the entity properties are actually fields themselves (they’re known as base fields in Drupal 8). Fields and intrinsic properties both store infor‐ mation, which could be text, numbers, attached files, images, media URLs, or other data, and fields can be single- or multiple-valued. Some entity types are not fieldable or do not allow administrators to change their fields; for example, con‐ figuration entity types are normally not fieldable. Field type Each field has a field type, which defines what type of data the field stores; Drupal core defines several field types—including one-line text, formatted long text, and images—and modules can define additional field types. A field type can be used to create one or more individual fields, which have machine names and other set‐ tings pertaining to data storage (such as how many values they can store); the set‐ tings cannot be changed after the field is created. Field instance Once a field is created, it can be attached to one or more bundles; each field/ bundle combination is known as a field instance. In Drupal 7, fields can be shared across entity types; in Drupal 8, fields are specific to each entity type, so for instance, if you needed a first-name field for both Nodes and Comments, you’d have to create it twice. Field instances have settings such as whether the field is required and a label; these settings can be different for each bundle and can be changed later. Widgets and form modes When a user is creating or editing an entity object, a field widget is used to edit the field data on the entity editing form. For instance, a field storing text can use a normal HTML text field as its widget, or if its values are restricted to a small set, it could use an HTML select, radio buttons, or checkboxes. Drupal core defines the common widgets needed to edit its fields in standard ways, and mod‐ ules can define widgets for their fields or other modules’ fields. In Drupal 7, widgets are assigned to each field instance when the field is attached to the bun‐ dle. In Drupal 8, each bundle can have one or more form modes, which allow entity objects and their fields to be edited differently under different circumstan‐ ces. In each form mode, some fields can be hidden, display order can be chosen, and a widget can be chosen for each visible field. Formatters and view modes When an entity object is being displayed, field formatters are used to display the field data. For instance, a long text field could be formatted as plain text (with all HTML tags stripped out), passed through a text filter, or truncated to a particular length. Modules can define field formatters for their own or other modules’ field types. Each bundle can have one or more view modes (such as full page and teaser Programming with Entities and Fields | 131 for the node entity type); fields can be hidden or shown, ordered, and assigned different formatters in each view mode. View modes allow entity objects and their fields to be displayed differently under different circumstances; this is mostly useful for fieldable content entity types. Translations and revisions The data in entity objects and their fields can be edited and translated, and many entity types keep track of revisions, making it possible to revert entity and field data to a prior version. If your data storage needs are similar to an existing entity type, it is a good idea to use it instead of defining your own entity type. This will be a lot less work, because existing entity types include admin‐ istrative screens and other functionality, and it will also allow you to use the many add-on modules that work with existing entity types. Defining your own entity type is a good idea for these circumstances: • In Drupal 7, to store groups of settings for a module, to allow them to be translated with the Entity Translation module. • In Drupal 8, to store configuration that has multiple copies, as described in “Defining a Configuration Entity Type in Drupal 8” on page 149. • In either Drupal 7 or 8, to define storage for a set of content for a site that needs a completely different permissions system and display mechanism from the Drupal core node entity type (and from other existing entity types). Define your own entity when the additional programming that would be needed to coerce an existing entity type into doing what you want would be more work than the programming needed to define a separate entity type. Defining an Entity Type in Drupal 7 This section shows how to define a new entity type in Drupal 7, which could be used to store a special type of content, or for module settings. You might want to download the Entity example from Examples for Developers and follow along there, or perhaps look at the code for one of the Drupal core entities. Step 1: Implement hook_entity_info() The first step in defining a new entity type is to implement hook_entity_info() in your module. In Drupal 7, it is advisable to make use of the contributed Entity API 132 | Chapter 4: Drupal Programming Examples module, as it takes care of many standard operations for you; you may also want to make use of the Entity Construction Kit module. To use the Entity API module, you’ll need your module to have a dependency in its mymodule.info file: dependencies[] = entity With that taken care of, to define an entity type whose machine name is myentity, declare the following function in your mymodule.module file: // Simple internal-use entity. function mymodule_entity_info() { $return = array(); $return['myentity'] = array( // Define basic information. 'label' => t('Settings for My Module'), 'plural label' => t('Settings for My Module'), 'fieldable' => TRUE, // Provide information about the database table. 'base table' => 'mymodule_myentity', 'entity keys' => array( 'id' => 'myentity_id', 'label' => 'title', ), // Use classes from the Entity API module. 'entity class' => 'Entity', 'controller class' => 'EntityAPIController', // Have Entity API set up an administrative UI. 'admin ui' => array( 'path' => 'admin/myentity', ), 'module' => 'mymodule', 'access callback' => 'mymodule_myentity_access', // For content-type entities only, define the callback that // returns the URL for the entity. 'uri callback' => 'mymodule_myentity_uri', ); return $return; } // For content-type entities, return the URI for an entity. function mymodule_myentity_uri($entity) { return array( 'path' => 'myentity/' . $entity->myentity_id, ); } Programming with Entities and Fields | 133 Note about the URI callback: if your entity is for settings, you probably just need a way to edit the settings, rather than a dedicated page to display them. So, you proba‐ bly do not need a URL for each entity (akin to node/1 for a node entity). In this case, you can leave out the URI callback and the hook_menu() entry defined in a later step. Further reading and reference: • “The Basics of Module and Theme Hook Programming” on page 23 • Entity API module: https://www.drupal.org/project/entity • Entity Construction Kit module: https://www.drupal.org/project/eck Step 2: Implement hook_schema() The next step, for both settings and content entity types, is to implement hook_schema() in your mymodule.install file, to set up the database table for storing your entity information. The table name and some of the database field names need to match what you put into your hook_entity_info() implementation, and you’ll also want a database field for language (assuming that you want your entity objects to be translatable), and possibly additional database fields to keep track of when entity objects are created and last updated. Here’s the schema for the settings entity type example: function mymodule_schema() { $schema = array(); $schema['mymodule_myentity'] = array( 'description' => 'Storage for myentity entity: settings for mymodule', 'fields' => array( 'myentity_id' => array( 'description' => 'Primary key: settings ID.', 'type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE, ), 'title' => array( 'description' => 'Label assigned to this set of settings', 'type' => 'varchar', 'length' => 200, 'default' => '', ), 'language' => array( 'description' => 'Language of this set of settings', 'type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => '', ), // Consider adding additional fields for time created, time updated. 134 | Chapter 4: Drupal Programming Examples ), 'primary key' => array('myentity_id'), 'indexes' => array( 'language' => array('language'), // Add indexes for created/updated here too. ), ); return $schema; } Further reading and reference: • “Setting Up Database Tables: Schema API and hook_update_N()” on page 55 Step 3: Add predefined fields in hook_install() If you are defining an entity type to use for settings, the next step is to attach fields to your entity bundle to store the settings you need. For a content-type entity, you may want to just let administrators add the fields in the administrative user interface (the Entity API module provides the URLs and screens), in which case you can skip this step. To add fields programmatically, implement hook_install() in your mymod‐ ule.install file, using Drupal core Field API functions: function mymodule_install() { // Create a plain text field for a setting. $field = field_create_field(array( 'field_name' => 'myentity_setting_1', 'type' => 'text', 'entity_types' => array('myentity'), 'locked' => TRUE, 'translatable' => TRUE, )); // Attach the field to the entity bundle. $instance = field_create_instance(array( 'field_name' => 'myentity_setting_1', 'entity_type' => 'myentity', 'bundle' => 'myentity', 'label' => t('Setting 1'), 'description' => t('Help for this setting'), 'required' => TRUE, 'widget' => array( 'type' => 'text_textfield', ), 'display' => array( 'default' => array( 'label' => 'above', 'type' => 'text_default', ), Programming with Entities and Fields | 135 ), )); // Repeat these two function calls for each additional field. } Further reading and reference: • “The Basics of Module and Theme Hook Programming” on page 23 Step 4: Set up display The next step is to set up your entity type so that its objects can be displayed, which is only necessary for a content-type entity. Given the mymodule_myentity_uri() URL callback function that was declared in “Step 1: Implement hook_entity_info()” on page 132, you need to register for the URL it returns and tell Drupal to use the Entity API module’s entity_view() function to display the entity: function mymodule_menu() { $items = array(); // Register for the URL that mymodule_myentity_uri() returns. // The placeholder %entity_object in the URL is handled by the Entity // API function entity_object_load(). $items['myentity/%entity_object'] = array( // entity_object_load() needs to know what the entity type is. 'load arguments' => array('myentity'), // Use a callback for the page title, not a static title. 'title callback' => 'mymodule_myentity_page_title', 'title arguments' => array(1), // Callback to display the entity. 'page callback' => 'entity_ui_entity_page_view', 'page arguments' => array(1), // Access callback. 'access callback' => 'mymodule_myentity_access', 'access arguments' => array('view', array(1)), ); return $items; } // Title callback function registered above. function mymodule_myentity_page_title($entity) { return $entity->title; } Further reading and reference: 136 | Chapter 4: Drupal Programming Examples • “Registering for a URL in Drupal 7” on page 89 • “Autoloading, Arguments, and Placeholders in hook_menu()” on page 90 • “Checking Drupal Permissions” on page 64 Step 5: Set up editing and management Both settings and content entity types need management pages and forms for creating and editing entity objects. The Entity API module sets these up for you using the information that you provided in your hook_entity_info() implementation (in “Step 1: Implement hook_entity_info()” on page 132). There are several functions that you do need to define, however: • An access callback (which defines access permissions for your entity type). The function name is provided in your hook_entity_info() and hook_menu() imple‐ mentations. You’ll also need to implement hook_permission() to define permissions. • A function to generate the entity object editing form, which must be called myentity_form(). A corresponding form-submission handler is also needed. Your form needs to handle editing the title and the language, and then it needs to call field_attach_form() to let the Field module add the other fields to the form. Here is the code for these functions: // Define the permissions. function mymodule_permission() { return array( 'view myentity' => array( 'title' => t('View my entity content'), ), 'administer myentity' => array( 'title' => t('Administer my entities'), ), ); } // Access callback for Entity API. function mymodule_myentity_access($op, $entity, $account = NULL) { // $op is 'view', 'update', 'create', etc. // $entity could be NULL (to check access for all entity objects) // or it could be a single entity object. // $account is either NULL or a user object. // In this simple example, just check permissions for // viewing or administering the entity type generically. if ($op == 'view') { Programming with Entities and Fields | 137 return user_access('view myentity', $account); } return user_access('administer myentity', $account); } // Form-generating function for the editing form. function myentity_form($form, $form_state, $entity) { $form['title'] = array( '#title' => t('Title'), '#type' => 'textfield', '#default_value' => isset($entity->title) ? $entity->title : '', ); // Build language options list. $default = language_default(); $options = array($default->language => $default->name); if (module_exists('locale')) { $options = array(LANGUAGE_NONE => t('All languages')) + locale_language_list('name'); } // Add language selector or value to the form. $langcode = isset($entity->language) ? $entity->language : ''; if (count($options) > 1) { $form['language'] = array( '#type' => 'select', '#title' => t('Language'), '#options' => $options, '#default_value' => $langcode, ); } else { $form['language'] = array( '#type' => 'value', '#value' => $langcode, ); } $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array( '#type' => 'submit', '#value' => t('Save'), '#weight' => 999, ); field_attach_form('myentity', $entity, $form, $form_state, $langcode); return $form; } // Form submission handler for editing form. function myentity_form_submit($form, &$form_state) { 138 | Chapter 4: Drupal Programming Examples // Make use of Entity API class. $entity = entity_ui_form_submit_build_entity($form, $form_state); $entity->save(); // Redirect to the management page. $form_state['redirect'] = 'admin/myentity'; } Further reading and reference: • “Checking Drupal Permissions” on page 64 • “Using the Drupal Form API” on page 109 Step 6: Enable your module If you have followed all of these steps, you should be able to enable your module and see your entity type’s administration pages at example.com/admin/myentity (as given in the hook_entity_info() implementation). If you had previously installed your module, you’ll probably need to uninstall (losing all your data) and then reenable. You may be able to just disable and enable, if you also define an update hook that adds the necessary database tables. Defining a Content Entity Type in Drupal 8 Entity types in Drupal 8 are a type of plugin, so they use the plugin system described in “Implementing a plugin in a module” on page 33. This section describes how to define a content entity type, and the following section (“Defining a Configuration Entity Type in Drupal 8” on page 149) describes how to define a configuration entity type. Before you start, you will need to choose a machine name for your entity type, which should be short but unique; in this example, myentity is the machine name. The maximum length for an entity type machine name is 32 characters. Step 1: Define the entity interface and class The first steps in defining an entity in Drupal 8 are to define an entity interface and entity class. The interface should define the get* and set* methods for the base properties of your entity (such as getTitle() and setTitle() for the title property), and should extend \Drupal\Core\Entity\ContentEntityInterface. The class should implement your interface and normally extends \Drupal\Core\Entity \ContentEntityBase; it will need to implement any methods you put on the class, plus ContentEntityInterface::baseFieldDefinitions(). The interface should either go in the top-level namespace of your module, or in the Entity namespace underneath; the entity class needs to be in the Entity namespace and needs to have Programming with Entities and Fields | 139 \Drupal\Core\Entity\Annotation\ContentEntityType annotation in its documen‐ tation header to be recognized as an entity type definition plugin. The interface can usually be pretty simple, because normally there are only a few base properties (most of the data goes in fields). Here is an example (minus documenta‐ tion headers), which goes in src/Entity/MyEntityInterface.php under the module directory: namespace Drupal\mymodule\Entity; use Drupal\Core\Entity\ContentEntityInterface; interface MyEntityInterface extends ContentEntityInterface { public function getTitle(); public function setTitle($title); } The class is a bit more complicated, because of the necessary annotation in the docu‐ mentation header. Here is a fairly minimal example, which goes in src/Entity/MyEn‐ tity.php under the module directory: namespace Drupal\mymodule\Entity; use Drupal\mymodule\Entity\MyEntityInterface; use Drupal\Core\Entity\ContentEntityBase; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; /** * Represents a MyEntity entity object. * * @ContentEntityType( * id = "myentity", * label = @Translation("My entity"), * bundle_label = @Translation("My entity subtype"), * fieldable = TRUE, * handlers = { * "view_builder" = "Drupal\Core\Entity\EntityViewBuilder", * "list_builder" = "Drupal\Core\Entity\EntityListBuilder", * "views_data" = "Drupal\mymodule\Entity\MyEntityViewsData", * "form" = { * "default" = "Drupal\mymodule\Entity\MyEntityForm", * "edit" = "Drupal\mymodule\Entity\MyEntityForm", * "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm", * } * }, * admin_permission = "administer my entities", * base_table = "myentity", * data_table = "myentity_field_data", * translatable = TRUE, * entity_keys = { * "id" = "eid", * "bundle" = "subtype", * "label" = "title", 140 | Chapter 4: Drupal Programming Examples * "langcode" = "langcode", * "uuid" = "uuid", * }, * links = { * "canonical" = "/myentity/{myentity}", * "delete-form" = "/myentity/{myentity}/delete", * "edit-form" = "/myentity/{myentity}/edit", * }, * field_ui_base_route = "entity.myentity_type.edit_form", * bundle_entity_type = "myentity_type", * ) */ class MyEntity extends ContentEntityBase implements MyEntityInterface { public function getTitle() { return $this->get('title')->value; } public function setTitle($title) { $this->set('title', $title); return $this; } public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { // Define base fields for the items in the entity_keys // annotation. Note that as this is a static method, you cannot use // $this->t() here; use t() for translation instead. // Also note that the reason for the redundant descriptions is that // Views displays errors if they are missing. $fields['eid'] = BaseFieldDefinition::create('integer') ->setLabel(t('My entity ID')) ->setDescription(t('My entity ID')) ->setReadOnly(TRUE) ->setSetting('unsigned', TRUE); $fields['subtype'] = BaseFieldDefinition::create('entity_reference') ->setLabel(t('Subtype')) ->setDescription(t('Subtype')) ->setSetting('target_type', 'myentity_type'); // Add a language code field so the entity can be translated. $fields['langcode'] = BaseFieldDefinition::create('language') ->setLabel(t('Language')) ->setDescription(t('Language code')) ->setTranslatable(TRUE) ->setDisplayOptions('view', array( 'type' => 'hidden', )) ->setDisplayOptions('form', array( 'type' => 'language_select', 'weight' => 2, )); Programming with Entities and Fields | 141 // The title field is the only editable field in the base // data. Set it up to be configurable in Manage Form Display // and Manage Display. $fields['title'] = BaseFieldDefinition::create('string') ->setLabel(t('Title')) ->setDescription(t('Title')) ->setTranslatable(TRUE) ->setRequired(TRUE) ->setDisplayOptions('view', array( 'label' => 'hidden', 'type' => 'string', 'weight' => 5, )) ->setDisplayConfigurable('view', TRUE) ->setDisplayOptions('form', array( 'type' => 'string_textfield', 'weight' => 5, )) ->setDisplayConfigurable('form', TRUE); $fields['uuid'] = BaseFieldDefinition::create('uuid') ->setLabel(t('UUID')) ->setDescription(t('Universally Unique ID')) ->setReadOnly(TRUE); return $fields; } } A few notes on the annotation lines: • The first few lines of the annotation give the ID you chose, whether the entity is fieldable or not, and a human-readable label for the entity and its bundles. If your entity does not use bundles, leave out everything in this example that refers to them. • The permission defined in the admin_permission annotation must be defined in your mymodule.permissions.yml file. See “Drupal core’s main permission system” on page 64. • The base_table and entity_keys annotation sections define the database table and database table fields for the basic entity data. data_table is used for transla‐ tions. You can also indicate revision_table if your entity type should store revi‐ sions, in which case you should also add a revision key to the entity_keys section. Assuming your entity uses the default entity storage controller as your entity storage mechanism, these tables will be automatically set up for you. • If your entity supports bundles, each bundle definition is a configuration entity. So, you’ll need to define a configuration entity type for your bundles and put its 142 | Chapter 4: Drupal Programming Examples machine name (ID) in the bundle_entity_type annotation. To find out how to do this, see “Defining a Configuration Entity Type in Drupal 8” on page 149, which uses this exact example. • “Implementing a plugin in a module” on page 33 has more information about annotations in general. The rest of the steps in the entity definition process create the classes that are referred to in the annotation header of your entity class, and set up other necessary data, func‐ tions, and so on. Step 2: Define handlers The handlers section of the annotation lists the classes that govern storage, access, and other operations on your entity objects. Drupal provides default handlers for most of the operations; you can override the defaults by adding entries to this part of the annotation. You’ll need to define the classes for each specific handler you desig‐ nate; most entities, like this example, will at least need custom edit and delete confirm forms. The edit form should extend \Drupal\Core\Entity\ContentEntityForm. In this example, the annotation gives the class as \Drupal\mymodule\Entity\MyEntityForm, so it needs to go into the src/Entity/MyEntityForm.php file under the main module directory. Typically, entity forms will need to override the form(), save(), and possi‐ bly validateForm() methods; you may also need to override other methods for spe‐ cial cases, such as buildEntity() if taking form values and making an entity is special. Here’s a basic example (look for Drupal core classes that extend Content EntityForm for others); in this case, the base method for form() is sufficient, as it will take care of the title and all of the added fields: namespace Drupal\mymodule\Entity; use Drupal\Core\Entity\ContentEntityForm; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; class MyEntityForm extends ContentEntityForm { public function save(array $form, FormStateInterface $form_state) { $entity = $this->entity; $entity->save(); // You could do some logging here, set a message, and so on. // Redirect to the entity display page. $form_state->setRedirect('entity.myentity.canonical', array('myentity' => $entity->id( ))); } } Programming with Entities and Fields | 143 The delete form should confirm that the user wants to delete the entity object. The default class of \Drupal\Core\Entity\ContentEntityDeleteForm is sufficient for this entity; if not, you could extend this class (for instance, to override the confirma‐ tion message). This entity type is set up to use the default \Drupal\Core\Entity\EntityView Builder to build render arrays for viewing entities with view modes. This relies on the existence of a theme hook named 'myentity', which should render the base properties of the entity. So, you’ll need to add this to the hook_theme() implementa‐ tion, the mymodule_theme() function in mymodule.module: function mymodule_theme($existing, $type, $theme, $path) { return array( 'myentity' => array( 'render element' => 'elements', 'template' => 'myentity', ), ); } Then you’ll also need to create the templates/myentity.html.twig file to do the rendering: {% if not page %} {{ title }} {% endif %}
{{ content }}
And you’ll need a preprocess function to set up the template variables, which goes into the mymodule.module file: use Drupal\Core\Render\Element; use Drupal\Core\Url; function template_preprocess_myentity(&$variables) { $variables['view_mode'] = $variables['elements']['#view_mode']; $entity = $variables['elements']['#myentity']; $variables['entity'] = $entity; $variables['title'] = $variables['entity']->getTitle(); // See if the entity is being viewed on its own page. $route_match = \Drupal::routeMatch(); $page = FALSE; if ($variables['view_mode'] == 'full' && 144 | Chapter 4: Drupal Programming Examples $route_match->getRouteName() == 'entity.myentity.canonical') { $page_entity = $route_match->getParameter('myentity'); if ($page_entity && $page_entity->id() == $entity->id()) { $page = TRUE; } } $variables['page'] = $page; // Set up content variable for templates. $variables += array('content' => array()); foreach (Element::children($variables['elements']) as $key) { $variables['content'][$key] = $variables['elements'][$key]; } // Set up attributes. $variables['attributes']['class'][] = 'myentity'; } The list_builder and views_data handlers are for Views integration, which is cov‐ ered in “Step 4: Add Views integration” on page 147. Further reading and reference: • “Basic Form Generation and Processing in Drupal 8” on page 114 • “Creating Confirmation Forms” on page 116 • “Making Your Module Output Themeable” on page 25 Step 3: Set up routing and links The links annotation section lists the URL paths for the basic operations; the corre‐ sponding routes need to have names like entity.myentity.canonical and need to be defined in your mymodule.routing.yml file. If your entity can be displayed on its own page, you’ll need to define the canonical link to be the viewing page; otherwise, your canonical link should probably be the edit page. Most content entities also need delete-form and edit-form links. If your entity type is fieldable, then you also need to define the field_ui_base_route annotation property (outside of the links sec‐ tion), because this route is used by the Field UI module to set up the management pages for fields, forms, and display modes. For nonbundle entities, it should be a page for the settings for the entity type; for entities with bundles, it should be the page where you edit the bundle. This example uses bundles, so the route it uses is defined later, as part of defining the bundle configuration entity type. Programming with Entities and Fields | 145 Given the annotation in this entity example, the mymodule.routing.yml file needs to contain: entity.myentity.canonical: path: '/myentity/{myentity}' defaults: _entity_view: 'myentity.full' requirements: _entity_access: 'myentity.view' entity.myentity.delete_form: path: '/myentity/{myentity}/delete' defaults: _entity_form: 'myentity.delete' _title: 'Delete' requirements: _entity_access: 'myentity.delete' entity.myentity.edit_form: path: '/myentity/{myentity}/edit' defaults: _entity_form: 'myentity.edit' _title: 'Edit my entity' requirements: _entity_access: 'myentity.edit' Instead of defining routes as shown here in your mymodule.rout‐ ing.yml file, you can use an entity route provider class, which cov‐ ers the canonical, edit, and delete routes. The API for this is still in flux as of September 2015, so it is not described here. Check the “Entity API” topic on https://api.drupal.org to learn more. The classes for the entity forms have already been defined, and this example uses the default entity viewer for the entity page, with a placeholder that loads the entity. You could also override the default class to define your own view handler. For most entities, if you are on the entity viewing page, you would want to see a tab or link to edit the entity. This can be accomplished via a local task set, which goes into the mymodule.links.task.yml file: entity.myentity.canonical: title: 'View' route_name: entity.myentity.canonical base_route: entity.myentity.canonical entity.myentity.edit_form: title: 'Edit' route_name: entity.myentity.edit_form base_route: entity.myentity.canonical 146 | Chapter 4: Drupal Programming Examples You’ll also need to think about the process for adding a new entity of this type. For instance, to add a node, you need to choose which type (bundle) of node you’re adding, and then visit example.com/node/add/thetypename. Taxonomy terms work in a similar way, with an add page that depends on the vocabulary (bundle). But you don’t add a comment by bundle—comments are added to a specific entity object. For this example, we’ll make it so that you add a new entity object from the entity subtype (bundle) management page, so that will be explained in conjunction with defining the bundle configuration entity type, in “Defining a Configuration Entity Type in Drupal 8” on page 149. Further reading and reference: • “Registering for a URL in Drupal 8” on page 92 • Parameter upcasting in routes: http://bit.ly/parameter_upcasting • “Providing Administrative Links” on page 95 Step 4: Add Views integration Your content entity type will probably also need to have an administrative page, which would presumably list existing entities with edit and delete links, and allow you to add new entities. The best way to create an administrative page is by providing a default view. In order to do that, you’ll need to provide Views integration for your entity, as well as a list builder. Both the Views integration and the list builder are specified in the handlers section of your entity annotation, as views_data and list_builder. The entity here uses the default entity list builder class but has its own views data class \Drupal\mymodule \Entity\MyEntityViewsData. This class provides the same kind of output as hook_views_data() (see “Providing a New Views Data Source” on page 175), but most of it is provided in an automated way by the base class. In this case, the class goes in src/Entity/MyEntityViewsData.php under the module directory: namespace Drupal\mymodule\Entity; use Drupal\views\EntityViewsData; class MyEntityViewsData extends EntityViewsData { public function getViewsData() { // Start with the Views information provided by the base class. $data = parent::getViewsData(); // Override a few things... // Define a wizard. $data['myentity_field_data']['table']['wizard_id'] = 'myentity'; // You could also override labels or put in a custom field Programming with Entities and Fields | 147 // or filter handler. return $data; } } This class, in turn, refers to a wizard plugin, which is a plugin class that enables a user to go to the “Add new view” page at admin/structure/views/add and choose your entity from the list to create a new view. The preceding code tells Views that the plu‐ gin ID for this wizard is myentity. For Views to find the plugin, it needs to be in the src/Plugins/views/wizard directory (and corresponding namespace). Let’s call the class MyEntityViewsWizard and just accept the default behavior of a Views wizard: namespace Drupal\mymodule\Plugin\views\wizard; use Drupal\Core\Form\FormStateInterface; use Drupal\views\Plugin\views\wizard\WizardPluginBase; /** * Provides a views wizard for My Entity entities. * * @ViewsWizard( * id = "myentity", * base_table = "myentity_field_data", * title = @Translation("My Entity") * ) */ class MyEntityViewsWizard extends WizardPluginBase { } This is sufficient to provide Views integration for the entity. So, to provide an admin‐ istrative interface for managing the My Entity content, you’d also need to: • Define a view using the Views user interface. It should include exposed filters; sorting (usually via table headers); and links to add, edit, and delete your entities. • Export the view as a YAML configuration file. Edit the file manually and remove the UUID line, because the export will contain a UUID, but supplied configura‐ tion shouldn’t. Place the configuration file in your module’s config/install or con‐ fig/optional directory. See “Configuration file format and schema in Drupal 8” on page 42. Note that if the only available management page for your entity uses Views, you might want to make the Views module a dependency of your module, because if Views is not enabled, users will not be able to manage their entities. 148 | Chapter 4: Drupal Programming Examples Step 5: Enable your module If you are using bundles, your content entity type will not work until you’ve also defined the configuration entity type for the bundle (see “Defining a Configuration Entity Type in Drupal 8” on page 149). If you are not using bundles, enable your module and the entity type should be defined. If your module is already enabled, you should be able to get by with a con‐ tainer rebuild (see “Rebuilding the container” on page 38), but you may need to unin‐ stall your module and reinstall it. You should probably test your entity type module in a test installation of Drupal until you’ve verified that it’s working, though, or at least make frequent database backups, because you may get Drupal into an unrecoverable state during the debugging phase. Defining a Configuration Entity Type in Drupal 8 Defining a configuration entity type is somewhat similar to defining a content entity type (described in the previous section, “Defining a Content Entity Type in Drupal 8” on page 139), but the process has several differences. The basic steps are listed in this section; as an example, we’ll define the content entity bundle configuration entity needed for the content entity type defined “Defining a Content Entity Type in Drupal 8” on page 139. If you need to define a configuration entity for a generic configuration purpose rather than as a bundle for a content entity, a good example to look at is the entity for date format configuration in the Drupal core DateTime library. Accordingly, this section also points out which classes and files are used to define this entity, so that you can follow along with that example as well. Before you start, you will need to choose a machine name for your configuration entity, and a configuration prefix for configuration data storage. The configuration prefix defaults to $module_name.$machine_name, or core.$machine_name for entities defined in Drupal core outside of modules, but you can shorten the suffix (after the module name) if you want, by adding config_prefix to the annotation to your con‐ figuration entity class header. For our bundle example, the machine name myentity_type was specified in the content entity type annotation, and we’ll leave the configuration prefix as mymodule.myentity_type. For the date format entity, the machine name is date_format and the prefix is core.date_format. The maximum length for an entity type machine name or configuration prefix is 32 characters. Programming with Entities and Fields | 149 Further reading and reference: • http://bit.ly/config_entitites has some more documentation about configuration entities. Step 1: Define the configuration schema The first step in defining a configuration entity type is to define a configuration schema for mymodule.myentity_type.* in the config/schema/mymodule.schema.yml file that defines the fields for your configuration data. For the date format example, see the core.date_format section of core/config/schema/core.data_types.schema.yml; for our bundle example: mymodule.myentity_type.*: type: config_entity label: 'My entity subtype' mapping: id: type: string label: 'Machine-readable name' label: type: label label: 'Name' description: type: text label: 'Description' settings: label: 'Settings' type: mymodule.settings.myentity mymodule.settings.myentity: type: mapping label: 'My entity subtype settings' mapping: default_status: type: boolean label: 'Published by default' Note that the 'Published by default' setting is just for illustration; the content entity does not actually have a 'published' property. Further reading and reference: • “Configuration file format and schema in Drupal 8” on page 42 150 | Chapter 4: Drupal Programming Examples Step 2: Define the entity interface and class The next step is to define an entity interface and class for your configuration entity. The interface should extend \Drupal\Core\Config\Entity\ConfigEntityInterface and may define a few additional methods that your configuration entities will need. In the date format example, this interface is \Drupal\Core\Datetime\DateFormat Interface; in our bundle example, it’s \Drupal\mymodule\Entity\MyEntityType Interface, which goes in the src/Entity/MyEntityTypeInterface.php file under the module directory: namespace Drupal\mymodule\Entity; use Drupal\Core\Config\Entity\ConfigEntityInterface; interface MyEntityTypeInterface extends ConfigEntityInterface { public function getDescription(); } Note that even if you do not need to define additional methods, it is still a good idea to define an interface, so that you can declare objects to be of that type (leading to better self-documenting code). The entity class should extend \Drupal\Core\Config\Entity\ConfigEntityBase and implement your entity interface. For a configuration entity being used as an entity bundle, there is another base class to use: Drupal\Core\Config\Entity\Con figEntityBundleBase, which contains some additional helper code. Whichever base class is used, your class also needs to be annotated with \Drupal\Core\Entity\Anno tation\ConfigEntityType annotation in its documentation header. In the date for‐ mat example, this class is \Drupal\Core\Datetime\Entity\DateFormat; in our bundle example, the class is \Drupal\mymodule\Entity\MyEntityType, which goes in the src/Entity/MyEntityType.php file under the module directory: namespace Drupal\mymodule\Entity; use use use use Drupal\Core\Config\ConfigException; Drupal\Core\Config\Entity\ConfigEntityBundleBase; Drupal\Core\Entity\EntityStorageInterface; Drupal\mymodule\Entity\MyEntityTypeInterface; /** * Defines the My Entity bundle configuration entity. * * @ConfigEntityType( * id = "myentity_type", * label = @Translation("My entity subtype"), * handlers = { * "form" = { * "add" = "Drupal\mymodule\Entity\MyEntityTypeForm", * "edit" = "Drupal\mymodule\Entity\MyEntityTypeForm", * "delete" = "Drupal\mymodule\Entity\MyEntityTypeDeleteForm", Programming with Entities and Fields | 151 * }, * "list_builder" = "Drupal\mymodule\Entity\MyEntityTypeListBuilder", * }, * admin_permission = "administer my entities", * bundle_of = "myentity", * entity_keys = { * "id" = "id", * "label" = "label", * }, * links = { * "add-form" = "/admin/structure/myentity_type/add", * "edit-form" = "/admin/structure/myentity_type/manage/{myentity_type}", * "delete-form" = "/admin/structure/myentity_type/delete/{myentity_type}", * } * ) */ class MyEntityType extends ConfigEntityBundleBase implements MyEntityTypeInterface { // Machine name or ID of the entity bundle. public $id; // Human-readable name of the entity bundle. public $label; // Description of the entity bundle. public $description; // Settings for the entity bundle. public $settings = array(); public function getDescription() { return $this->description; } public function preSave(EntityStorageInterface $storage) { parent::preSave($storage); if (!$this->isNew() && ($this->getOriginalId() != $this->id())) { throw new ConfigException('Cannot change machine name'); } } } A few notes on the annotation lines: • The first few lines of the annotation give the ID you chose and a human-readable label for the entity. • For some configuration entities, you will need to define permissions for adminis‐ tering your entity in your mymodule.permissions.yml file. This permission is ref‐ erenced in the annotation on your entity class; you can also use an existing 152 | Chapter 4: Drupal Programming Examples permission from another module (if so, make sure that this module is listed as a dependency of your module). • The bundle_of annotation gives the machine name of the entity that this entity is a bundle type for. Omit for generic configuration entities. • The entity_keys annotation section lists the mapping between the entity ID and label to the configuration schema fields that you’ve defined. • The rest of the annotation is discussed in the following steps. • “Implementing a plugin in a module” on page 33 has more information about annotations. Step 3: Define handlers The handlers section of the annotation lists classes that handle storage, access, and other operations on your entity objects. Drupal provides default handler classes for most of the operations; you can override the defaults by adding entries to this list. For configuration entities, you will need to define the form for adding and editing, the confirmation form for deleting, and the list builder, which is used to build an admin‐ istrative screen to manage your configuration items. The editing form should extend \Drupal\Core\Entity\EntityForm; usually, the only methods you need to override are form(), save(), and possibly validateForm(). In the date format example, this class is \Drupal\system\Form\DateFormatEditForm; in our bundle example, the class is given in the annotation as \Drupal\mymodule \Entity\MyEntityTypeForm, so it needs to go in file src/Entity/MyEntityType‐ Form.php under the main module directory: namespace Drupal\mymodule\Entity; use Drupal\Core\Entity\EntityForm; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; class MyEntityTypeForm extends EntityForm { public function form(array $form, FormStateInterface $form_state) { $form = parent::form($form, $form_state); $form['id'] = array( '#title' => $this->t('Machine-readable name'), '#type' => 'textfield', '#required' => TRUE, ); // If we are editing an existing entity, show the current ID and // do not allow it to be changed. if ($this->entity->id()) { $form['id']['#default_value'] = $this->entity->id(); Programming with Entities and Fields | 153 $form['id']['#disabled'] = TRUE; } $form['label'] = array( '#title' => $this->t('Label'), '#type' => 'textfield', '#default_value' => $this->entity->label, ); $form['description'] = array( '#title' => $this->t('Description'), '#type' => 'textfield', '#default_value' => $this->entity->description, ); $form['settings'] = array( '#type' => 'details', '#title' => t('Settings'), '#open' => TRUE, ); $settings = $this->entity->settings; $form['settings']['default_status'] = array( '#title' => t('Published by default'), '#type' => 'checkbox', ); if (isset($settings['default_status']) && $settings['default_status']) { $form['settings']['default_status']['#default_value'] = TRUE; } return $form; } public function validateForm(array &$form, FormStateInterface $form_state) { parent::validateForm($form, $form_state); $values = $form_state->getValues(); // Require non-empty ID. $id = trim($values['id']); if (empty($id)) { $form_state->setErrorByName('id', $this->t('Subtype names must not be empty')); } } public function save(array $form, FormStateInterface $form_state) { $type = $this->entity; $type->save(); // You could do some logging here, set a message, and so on. // Redirect to admin page. $form_state->setRedirect(new Url('mymodule.my_entity_type_list')); 154 | Chapter 4: Drupal Programming Examples } } The delete form should confirm that the user wants to delete the configuration item and should extend \Drupal\Core\Entity\EntityConfirmFormBase. For this class, you will need to specify (in methods) the text for the question to ask the user, what to do if delete is confirmed, and the URL (route) to go to if the action is canceled. In the date format example, this class is \Drupal\system\Form\DateFormatDeleteForm. Our example is a bit more complicated: you should not delete an entity bundle if there would be any corresponding entity objects left over. Some entity types (such as node—see \Drupal\node\Form\NodeTypeDeleteConfirm) handle this by disallowing bundle deletion if entities exist of that type, and others delete the corresponding enti‐ ties (of course, warning the user). For this example, in order to illustrate how to do it, we’ll take the latter tactic and delete the entities. The delete form class is given in the annotation as \Drupal\mymodule\Entity\MyEntityTypeDeleteForm, and it needs to go in the src/Entity/MyEntityTypeDeleteForm.php file under the main module direc‐ tory: namespace Drupal\mymodule\Entity; use use use use use use Drupal\Core\Entity\EntityConfirmFormBase; Drupal\Core\Entity\EntityManagerInterface; Drupal\Core\Entity\Query\QueryFactory; Drupal\Core\Form\FormStateInterface; Drupal\Core\Url; Symfony\Component\DependencyInjection\ContainerInterface; class MyEntityTypeDeleteForm extends EntityConfirmFormBase { protected $manager; protected $queryFactory; public function __construct(QueryFactory $query_factory, EntityManagerInterface $manager) { $this->queryFactory = $query_factory; $this->manager = $manager; } public static function create(ContainerInterface $container) { return new static( $container->get('entity.query'), $container->get('entity.manager') ); } public function getQuestion() { return $this->t('Are you sure you want to delete %label?', array('%label' => $this->entity->label())); } Programming with Entities and Fields | 155 public function getDescription() { return $this->t('All entities of this type will also be deleted!'); } public function getCancelUrl() { return new Url('mymodule.myentity_type.list'); } public function submitForm(array &$form, FormStateInterface $form_state) { // Find all the entities of this type, using an entity query. $query = $this->queryFactory->get('myentity'); $query->condition('subtype', $this->entity->id()); $ids = $query->execute(); // Delete the found entities, using the storage handler. // You may actually need to use a batch here, if there could be // many entities. $storage = $this->manager->getStorage('myentity'); $entities = $storage->loadMultiple($ids); $storage->delete($entities); // Delete the bundle entity itself. $this->entity->delete(); $form_state->setRedirectUrl($this->getCancelUrl()); } } The list builder class makes an administration overview page for managing your con‐ figuration data; it should usually extend \Drupal\Core\Config\Entity\ConfigEnti tyListBuilder. In the date format example, this class is \Drupal\system \DateFormatListBuilder; in our bundle example, the annotation gives the class name as \Drupal\mymodule\Entity\MyEntityTypeListBuilder, so it needs to go in the src/Entity/MyEntityTypeListBuilder.php file under the main module directory: namespace Drupal\mymodule\Entity; use Drupal\Core\Config\Entity\ConfigEntityListBuilder; use Drupal\Core\Entity\EntityInterface; use Drupal\Component\Utility\Xss; class MyEntityTypeListBuilder extends ConfigEntityListBuilder { public function buildHeader() { $header['label'] = $this->t('Label'); $header['description'] = array( 'data' => $this->t('Description'), ); return $header + parent::buildHeader(); 156 | Chapter 4: Drupal Programming Examples } public function buildRow(EntityInterface $entity) { $row['label'] = array( 'data' => $this->getLabel($entity), ); $row['description'] = Xss::filterAdmin($entity->description); return $row + parent::buildRow($entity); } } Further reading and reference: • “Basic Form Generation and Processing in Drupal 8” on page 114 • “Creating Confirmation Forms” on page 116 • “Querying and Loading Entities in Drupal 8” on page 160 Step 4: Define routing and route controllers Finally, in your module’s routing file, you will need to define administrative routes for managing your configuration entity: for the paths in the links annotation and vari‐ ous classes. You’ll also probably want to add an administrative menu entry for your overview page, so users will be able to find it. In the date format example, the admin‐ istrative routes are defined in core/modules/system/system.routing.yml, the menu entry is in core/modules/system/system.links.menu.yml, and there are local actions and tasks defined in core/modules/system/system.links.action.yml and core/modules/system/ system.links.task.yml, respectively. For the bundle example, the following routes go into the mymodule.routing.yml file for the administrative actions on the entity subtypes: mymodule.myentity_type.list: path: '/admin/structure/myentity_type' defaults: _entity_list: 'myentity_type' _title: 'My entity subtypes' requirements: _permission: 'administer my entities' entity.myentity_type.add_form: path: '/admin/structure/myentity_type/add' defaults: _entity_form: 'myentity_type.add' _title: 'Add my entity subtype' requirements: _entity_create_access: 'myentity_type' Programming with Entities and Fields | 157 entity.myentity_type.edit_form: path: '/admin/structure/myentity_type/manage/{myentity_type}' defaults: _entity_form: 'myentity_type.edit' _title: 'Edit my entity subtype' requirements: _entity_access: 'myentity_type.edit' entity.myentity_type.delete_form: path: '/admin/structure/myentity_type/delete/{myentity_type}' defaults: _entity_form: 'myentity_type.delete' _title: 'Delete my entity subtype' requirements: _entity_access: 'myentity_type.delete' Instead of defining routes as shown here in your mymodule.rout‐ ing.yml file, you can use an entity route provider class, which cov‐ ers the canonical, edit, and delete routes. The API for this is still in flux as of September 2015, so it is not described here. Check the “Entity API” topic on https://api.drupal.org to learn more. Also, this menu link entry goes into mymodule.links.menu.yml, to make the adminis‐ tration page visible in the Structure administrative section: mymodule.myentity_type.list: title: My entity subtypes description: Manage my entity subtypes and their fields, display, etc. route_name: mymodule.myentity_type.list parent: system.admin_structure In order to make an Add link visible on the list page, the following goes into the mymodule.links.action.yml file: entity.myentity_type.add_form: route_name: entity.myentity_type.add_form title: 'Add my entity subtype' appears_on: - mymodule.myentity_type.list The Field UI module makes a set of local tasks for managing fields, view modes, and form modes on fieldable entities. In order to make the entity subtype editing form part of this set of tasks, the following goes into the mymodule.links.task.yml file: entity.myentity_type.edit_form: title: 'Edit' route_name: entity.myentity_type.edit_form base_route: entity.myentity_type.edit_form And finally, during definition of the content entity, we decided that we’d handle adding new content entity objects from the entity subtype bundle management page. 158 | Chapter 4: Drupal Programming Examples So what we want to happen is that once you have an entity subtype defined, one of the actions available to you (besides Edit, Delete, Manage fields, etc.) would be to add a new entity object. To accomplish this, we need this route in the mymodule.routing.yml file: mymodule.myentity.add: path: '/myentity/add/{myentity_type}' defaults: _controller: '\Drupal\mymodule\Controller\MyUrlController::addEntityPage' _title: 'Add new my entity' requirements: _entity_create_access: 'myentity' We also need to define the controller method to put up the page, which goes into src/ Controller/MyUrlController.php: namespace Drupal\mymodule\Controller; use Drupal\mymodule\Entity\MyEntityTypeInterface; class MyUrlController extends ControllerBase { public function addEntityPage(MyEntityTypeInterface $type) { // Create a stub entity of this type. $entity = $this->entityManager() ->getStorage('myentity') ->create(array('subtype' => $type->id())); // You might want to set other values on the stub entity. // Return the entity editing form for the stub entity. return $this->entityFormBuilder()->getForm($entity); } } To make this appear in the operations list for the entity subtype, we need to override the getDefaultOperations() method in the MyEntityListBuilder list builder class defined earlier and add a new use statement at the top: // At the top. use Drupal\Core\Url; // New method. public function getDefaultOperations(EntityInterface $entity) { $operations = parent::getDefaultOperations($entity); // Add an operation for adding a new entity object of this type. $url = new Url('mymodule.myentity.add', array('myentity_type' => $entity->id())); $operations['add_new'] = array( 'title' => $this->t('Add new My Entity'), 'weight' => 11, 'url' => $url, Programming with Entities and Fields | 159 ); return $operations; } Further reading and reference: • “Registering for a URL in Drupal 8” on page 92 • “Providing Administrative Links” on page 95 Step 5: Enable your module Once you have all of these files created and code written, enable your module and your entity type should be defined. If your module is already enabled, you may be able to get by with a container rebuild (see “Rebuilding the container” on page 38), but you may need to uninstall your module and reinstall it. You should probably test your entity type module in a test installation of Drupal until you’ve verified that it’s working, though, or at least make frequent database backups, because you may get Drupal into an unrecoverable state during the debugging phase. Querying and Loading Entities in Drupal 8 Although you can technically use the basic Database API described in “Querying the Database with the Database API” on page 58 to query entities—or, worse yet, you could use the base PHP functions for MySQL queries—in Drupal 8 this is strongly discouraged. The reason is that content entity storage is a service in Drupal 8, to allow sites (in principle, anyway) to use alternative storage mechanisms, such as MongoDB or other non-SQL methods, to store entities. So although the default con‐ tent entity storage is in the main Drupal database, it is a good idea to use entity quer‐ ies to query entities, rather than the basic Database API. Entity queries are objects that implement \Drupal\Core\Entity\Query\QueryInter face (for regular queries), or \Drupal\Core\Entity\Query\QueryAggregateInter face (for aggregate queries). You can retrieve the appropriate query object from the entity.query service, as follows: // Code without dependency injection or $container variable: $query = \Drupal::entityQuery('myentity'); $query = \Drupal::entityQueryAggregate('myentity'); // Using dependency injection or a $container variable in a class: $query_service = $container->get('entity.query'); $query = $query_service->get('myentity'); $query = $query_service->getAggregate('myentity'); 160 | Chapter 4: Drupal Programming Examples Once you have a query object, you can use the condition() method to add field or base data conditions, and then execute the query. For example, to find all the entities of a given bundle: // Generic entities with 'bundle' property: $query->condition('bundle', 'mybundle'); // Node entities: $query->condition('type', 'mytype'); $ids = $query->execute(); The result of a query will be a list of the matching entity IDs. To load them, you should use the entity storage manager, which is an object that implements \Drupal \Core\Entity\EntityStorageInterface, which you can retrieve from the entity.manager service: // Code without dependency injection or $container variable: $storage = \Drupal::entityManager()->getStorage('myentity'); // Dependency injection or $container variable: $storage = $container->get('entity.manager')->getStorage('myentity'); // Load entities: $entities = $storage->loadMultiple($ids); The result will be an array of loaded entity objects, keyed by the entity IDs. Further reading and reference: • “Drupal 8 Services and Dependency Injection” on page 35 Defining a Field Type If you need to attach data to nodes or other entity types, you need to find a field type that stores this type of data. Between Drupal core and contributed modules, there are field types available for most of the common use cases for fielded content (plain text, numbers, formatted text, dates, images, media attachments, etc.), so if you are build‐ ing a website, and you need to store a particular type of data that is not covered by the fields in Drupal core, start by searching contributed modules for a field type that will suit your needs. Keep in mind that the field type only defines the stored data, while the formatter defines the display of the data and the widget defines the method for data input. So instead of defining a field, you may only need a custom widget or formatter for your use case. Here are several examples: • You need to store plain text data, based on clicking in a region on an image or using a Flash-based custom input method. For this use case, use a core Text field for storage, and create a custom widget for data input. Programming with Entities and Fields | 161 • You need to select one of several predefined choices on input, and display a pre‐ defined icon or canned text on output based on that choice. For this use case, use a core Number field for storage, and a core Select widget for input (with text labels; you could also use a core Text field for storage). Create a custom formatter for display. • You are creating a website that displays company profiles, using a Company node content type. For each company content item, you need to attach several office locations. For this use case, use the contributed Geofield, Location, Address Field, or another geographical information field module rather than defining your own custom field (try module category “Location” to find more). • For this same Company content type, you need several related fields to be grou‐ ped together on input and display; for instance, you might want to group the company size, annual revenue, and other similar fields together under Statistics. For this use case, use the Field Group contributed module to group the fields rather than creating a custom field type module. • For this same Company content type, you need to keep track of staff people, where each staff person has a profile with several fields. For this use case, create a separate Staff node content type, and use the contributed Entity Reference or Relation module to relate staff people to companies or companies to staff people; the Entity Reference field is included in Drupal core version 8. Or, use the Field Collection contributed module to create a staff field collection that is attached to the Company content type. • You have a field collection use case similar to the Staff of Company example, but you feel that it is general enough that many other websites would want to use this same field collection. In this case, it may make sense to create a custom field module and contribute it to drupal.org so that others can use it. Alternatively, you could use a the Field Collection contributed module, and export your collection configuration using the Features module (Drupal 7) or Drupal core configuration export (Drupal 8). See “Finding Drupal add-ons” on page 4 for hints on locating contributed modules. Note that some of the modules mentioned here may not yet be available for Drupal 8. The remainder of this section describes how to define a new field type, if you’ve deci‐ ded that this is what you need. Widgets and formatters are covered in “Programming with Field Widgets” on page 164 and “Programming with Field Formatters” on page 168, respectively. Defining a field type in Drupal 7 Assuming that you have decided you need a custom field module, here is an overview of how to define a field type in Drupal 7: 162 | Chapter 4: Drupal Programming Examples 1. Implement hook_field_info() in your mymodule.module file to provide basic information about your field type (such as the label used to select it when attach‐ ing a field to an entity bundle in the administrative user interface). 2. Implement hook_field_schema() in your mymodule.install file to provide infor‐ mation about the data stored in your field. This defines database fields in a way similar to hook_schema(), but it is not exactly the same. 3. Set up a widget for editing the field, and a formatter for displaying it (see the fol‐ lowing sections). There are many fields defined by Drupal core and contributed modules, so rather than providing another programming example here, I’ll just suggest that you use one of the following as a starting point: • A Drupal core field module (Image, File, Text, List, Number, or Taxonomy). • The documentation for the two field hooks. These are part of Drupal core, in the modules/field/field.api.php file (or look them up on https://api.drupal.org). • Date, Link, or another contributed field module (search modules for category “Fields”). • The Field example in Examples for Developers, which has some extra documen‐ tation explaining what is going on. Further reading and reference: • “The Basics of Module and Theme Hook Programming” on page 23 • “Finding Drupal add-ons” on page 4 Defining a field type in Drupal 8 In Drupal 8, field types are plugins. So, to define one in a module, you need to: • Define a class in the Plugin\Field\FieldType namespace under your module’s namespace, therefore located in the src/Plugin/Field/FieldType directory. The class needs to implement \Drupal\Core\Field\FieldItemInterface, and typi‐ cally extends \Drupal\Core\Field\FieldItemBase. You’ll usually just need to define methods propertyDefinitions() and schema(). • Annotate it with \Drupal\Core\Field\Annotation\FieldType annotation. See “The Basics of Drupal 8 Plugin Programming” on page 28 for a more detailed overview of the plugin system. Programming with Entities and Fields | 163 There are many good examples in Drupal core of field types, and they’re fairly simple to do, so I have not provided another one here. You can find them listed on the Field‐ Type annotation class page on https://api.drupal.org. Programming with Field Widgets There are several reasons that you may need to do some programming with field widgets: • If you have defined your own custom field type, you will need to define a widget for entering data for that field or repurpose an existing widget for use on your field. • You may need to define a custom input method for an existing field type. • You may be want to repurpose an existing widget for use on a different field type. This section covers both how to define a new widget and how to repurpose an exist‐ ing widget. Defining a field widget in Drupal 7 To define a field widget in Drupal 7, you need to implement two hooks in your mymodule.module file: hook_field_widget_info() and hook_field_widget_form(); the latter uses the Form API. If you’re defining a field widget for a custom field type that you’ve defined, I suggest going back to the field type module you used as a start‐ ing point and using that module’s widget as a starting point for your widget. If you’re defining a new widget for an existing field, the following example may be helpful. Assume that you want to define a widget for the core Text field that provides a custom method for input of plain text data, which could use Flash, JavaScript, or an image map to let users click on a region on an image or map, and store their choice as a predefined text string in the field. As a proxy for the custom input method, this example just uses an HTML select element; if you really just need an HTML select for your site, you could use a Drupal core “List (text)” field and choose the “Select list” widget. Here are the two hook implementations: // Provide information about the widget. function mymodule_field_widget_info() { return array( // Machine name of the widget. 'mymodule_mywidget' => array( // Label for the administrative UI. 'label' => t('Custom text input'), // Field types it supports. 'field types' => array('text'), ), 164 | Chapter 4: Drupal Programming Examples // Define additional widgets here, if desired. ); } // Set up an editing form. // Return a Form API form array. function mymodule_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { // Verify the widget type. Only needed if you define more than one widget. if ($instance['widget']['type'] == 'mymodule_mywidget') { // Find the current text field value. $value = isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL; // Set up the editing form element. Substitute your custom // code here, instead of using an HTML select. $element['value'] = array( '#type' => 'select', '#options' => array('x_stored' => t('x label'), 'y_stored' => t('y label')), '#default_value' => $value, ); } return $element; } Further reading and reference: • “The Basics of Module and Theme Hook Programming” on page 23 • “Using the Drupal Form API” on page 109 Defining a field widget in Drupal 8 In Drupal 8, field widgets are plugins. So, to define one in a module, you need to: • Define a class in the Plugin\Field\FieldWidget namespace under your mod‐ ule’s namespace, therefore located in the src/Plugin/Field/FieldWidget directory. The class needs to implement \Drupal\Core\Field\WidgetInterface, and it typically extends \Drupal\Core\Field\WidgetBase or a more specific base class that extends it. You’ll need to define the formElement() method (which gives the widget editing form), and you may need to override additional default methods on the base class. • Annotate it with \Drupal\Core\Field\Annotation\FieldWidget annotation. If you’re defining a field widget for a custom field type that you’ve defined, I suggest going back to the field type module you used as a starting point and using that mod‐ ule’s widget as a starting point for your widget. Programming with Entities and Fields | 165 If you’re defining a new widget for an existing field, the following example may be helpful. Assume that you want to define a widget for the core plain Text field that provides a custom method for input of plain text data, which could use Flash, Java‐ Script, or an image map to let the user click on a region on an image or map, and store their choice as a predefined text string in the field. As a proxy for the custom input method, this example just uses an HTML select element; if you really just need an HTML select for your site, you could use a Drupal core “List (text)” field and choose the “Select list” widget. Here is the widget class, which needs to go into the src/Plugin/Field/FieldWidget/ MyCustomText.php file under the main module directory: namespace Drupal\mymodule\Plugin\Field\FieldWidget; use Drupal\Core\Field\WidgetBase; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Form\FormStateInterface; /** * Custom widget for choosing an option from a map. * * @FieldWidget( * id = "mymodule_mywidget", * label = @Translation("Custom text input"), * field_types = { * "string" * } * ) */ class MyCustomText extends WidgetBase { public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { $value = isset($items[$delta]->value) ? $items[$delta]->value : NULL; // Set up the editing form element. Substitute your custom // code here, instead of using an HTML select. $element['value'] = $element + array( '#type' => 'select', '#options' => array( 'x_stored' => $this->t('x label'), 'y_stored' => $this->t('y label'), ), '#default_value' => $value, ); return $element; } } 166 | Chapter 4: Drupal Programming Examples Further reading and reference: • “The Basics of Drupal 8 Plugin Programming” on page 28 Examples—field widgets: • There are many good examples of field widgets in Drupal core. You can find them listed on https://api.drupal.org on the page for the FieldWidget annotation class. Repurposing an existing field widget Because the module that defines the widget tells Drupal what field types it supports in its hook_field_widget_info() implementation (Drupal 7) or plugin annotation (Drupal 8), if you want to repurpose an existing widget to apply to a different field type, you need to implement hook_field_widget_info_alter() in your mymod‐ ule.module file. This hook allows you to alter the information collected from all other modules’ hooks or plugins. For example: function mymodule_field_widget_info_alter(&$info) { // Add another field type to a widget. $info['widget_machine_name']['field types'][] = 'another_field_type'; } This example works in both Drupal 7 and Drupal 8. The only difference is that the widget machine name comes from hook_field_widget_info() (return value array key) in Drupal 7, and the plugin annotation (id annotation key) in Drupal 8. You may also need to alter the widget form so that the widget will work correctly with the new field type. There are two “form alter” hooks that you can use for this: hook_field_widget_form_alter(), which gets called for all widget forms, and the more specific hook_field_widget_WIDGET_TYPE_form_alter(), which gets called only for the widget you are interested in (and is therefore preferable). These hooks are present in both Drupal 7 and 8. Further reading and reference: • “The Basics of Module and Theme Hook Programming” on page 23. • “Using the Drupal Form API” on page 109. • “Altering Forms” on page 121. • https://api.drupal.org is the best place to look up details of any of the hooks men‐ tioned here. Programming with Entities and Fields | 167 Programming with Field Formatters There are two reasons you might need to do some programming with field format‐ ters: • If you have defined your own custom field type, you will need to define a format‐ ter that displays the data for that field, or repurpose an existing field formatter. • You may need to define a custom formatting method for an existing field type. If you need to repurpose an existing field formatter for a different field type, use hook_field_formatter_info_alter(), which works the same as hook_field_widget_info_alter() described in the preceding section. The following sections detail how to define new field formatters in Drupal 7 and 8. Defining a field formatter in Drupal 7 To define a field formatter in Drupal 7, you need to implement two hooks in your mymodule.module file: hook_field_formatter_info() and hook_field_format ter_view(). If you’re defining a field formatter for a custom field type that you’ve defined, I suggest going back to the field type module you used as a starting point and using that module’s formatter as a starting point for your formatter. If you’re defining a new formatter for an existing field, the following example may be helpful. Assume that you have set up a Text field with several preselected values, and on output you want to display an icon or some predefined text that corresponds to the preselected value. Here are the hook implementations for this formatter example: // Provide information about the formatter. function mymodule_field_formatter_info() { return array( // Machine name of the formatter. 'mymodule_myformatter' => array( // Label for the administrative UI. 'label' => t('Custom text output'), // Field types it supports. 'field types' => array('text'), ), // Define additional formatters here. ); } // Define how the field information is displayed. // Return a render array. function mymodule_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { $output = array(); 168 | Chapter 4: Drupal Programming Examples // Verify the formatter type. if ($display['type'] == 'mymodule_myformatter') { // Handle multi-valued fields. foreach ($items as $delta => $item) { // See which option was selected. switch ($item['value']) { case 'x_stored': // Output the corresponding text or icon. $output[$delta] = array('#markup' => '

' . t('Predefined output text x') . '

'); break; case 'y_stored': // Output the corresponding text or icon. $output[$delta] = array('#markup' => '

' . t('Predefined output text y') . '

'); break; // Handle other options here. } } } return $output; } Further reading and reference: • Render arrays: “Creating Render Arrays for Page and Block Output” on page 102 Examples—field formatters: • There are many Drupal core examples of field formatters. You can find the core implementations of hook_field_formatter_info() on the hook page on https:// api.drupal.org. • A contributed module that I wrote, Simple Google Maps is another example to look at. It’s a formatter for a plain text field, which assumes the text is an address and formats it as an embedded Google map. • The Field example in Examples for Developers is also good. Defining a field formatter in Drupal 8 In Drupal 8, field formatters are plugins. So, to define one in a module, you need to: • Define a class in the Plugin\Field\FieldFormatter namespace under your module’s namespace, therefore located in the src/Plugin/Field/FieldFormatter directory. The class needs to implement \Drupal\Core\Field\FormatterInter Programming with Entities and Fields | 169 face, and it typically extends \Drupal\Core\Field\FormatterBase. You’ll need to define method viewElements() (which builds a render array for the output), and you may need to override additional default methods on the base class. • Annotate it with \Drupal\Core\Field\Annotation\FieldFormatter annotation. If you’re defining a field formatter for a custom field type that you’ve defined, I sug‐ gest going back to the field type module you used as a starting point and using that module’s formatter as a starting point for your formatter. If you’re defining a new formatter for an existing field, the following example may be helpful. Assume that you have set up a plain Text field with several preselected values, and on output you want to display an icon or some predefined text that corresponds to the preselected value. Here is the plugin class (which goes in file src/Plugin/Field/FieldFormatter/MyCustom‐ Text.php under the main module directory): namespace Drupal\mymodule\Plugin\Field\FieldFormatter; use Drupal\Core\Field\FormatterBase; use Drupal\Core\Field\FieldItemListInterface; /** * Custom text field formatter. * * @FieldFormatter( * id = "mymodule_myformatter", * label = @Translation("Custom text output"), * field_types = { * "string", * } * ) */ class MyCustomText extends FormatterBase { public function viewElements(FieldItemListInterface $items) { $output = array(); foreach ($items as $delta => $item) { // See which option was selected. switch ($item->value) { case 'x_stored': // Output the corresponding text or icon. $output[$delta] = array('#markup' => '

' . t('Predefined output text x') . '

'); break; case 'y_stored': // Output the corresponding text or icon. 170 | Chapter 4: Drupal Programming Examples $output[$delta] = array('#markup' => '

' . t('Predefined output text y') . '

'); break; // Handle other options here. } } return $output; } } Further reading and reference: • “The Basics of Drupal 8 Plugin Programming” on page 28 Examples—field formatters: • There are many good examples of field formatters in Drupal core. You can find them listed on https://api.drupal.org on the page for the FieldFormatter annota‐ tion class. Creating Views Module Add-Ons The Views module (a contributed module in Drupal 7 and part of Drupal core in Drupal 8) is, at its heart, a query engine for Drupal that can be used to make format‐ ted lists of pretty much any type of data. The base Views module and other contrib‐ uted Views add-on modules provide the ability to query Node module content items, comments, taxonomy terms, users, and other data; to filter and sort the data in vari‐ ous ways; to relate one type of data to another; and to display the data using a list, grid, table, map, and other formats. In addition, custom entities and fields that you have defined are well supported by Views, and Views uses the Field system’s format‐ ters to display field data. But even with all of that ability, you may sometime have needs not covered by Views and existing add-on modules. This section provides an overview of how to create your own Views module add-ons for the following purposes: • Querying additional types of data • Relating new data to existing data types • Formatting the output in additional ways • Providing default views that site builders can use directly or adapt to their needs Aside from the background information in “Views Programming Terminology and Output Construction” on page 172, which you should read or at least skim, each topic Creating Views Module Add-Ons | 171 in this section is independent. However, if you are programming in Drupal 7, you’ll need to follow the steps in “Setting Up Your Module for Views in Drupal 7” on page 174 in order to accomplish all other Views tasks. Also note that some of the topics in this section assume knowledge of advanced usage of the Views user interface, such as relationships and contextual filters. Further reading and reference: • Views module for Drupal 7: https://www.drupal.org/project/views. • “Avoiding Custom Programming with Fielded Data” on page 75. • “Programming with Entities and Fields” on page 129. • If you use the Views Bulk Operations module and you need a custom bulk opera‐ tion: they are actually Rules components. You can create these in the Rules user interface; see also “Creating Rules Module Add-Ons in Drupal 7” on page 184. • For Views programming not covered in this book, there is documentation in the views.api.php file in the main Views module download for Drupal 7, as well as in the Advanced Help provided by the module. For Drupal 8, the API is docu‐ mented in a series of topics that you can find on https://api.drupal.org—start with the “Views overview” topic and go from there. Views Programming Terminology and Output Construction There are several pieces of background knowledge you’ll need before you start pro‐ gramming for the Views module. First, some terminology. Views makes use of a number of PHP classes, which in the Drupal 7 version are separated into several groups: • Classes known as handlers take care of field display, sorting, filtering, contextual filtering, and relationships. • Classes known as plugins take care of the overall display of the view, access restrictions, paging, and default values for contextual filters. • Other classes that are neither plugins nor handlers take care of assembling and performing the database query and other Views functionality. The distinction among handlers, plugins, and other classes is somewhat arbitrary, but because they’re declared and defined differently, it’s important to know about if you are programming for Drupal 7. In Drupal 8, the distinction among these types of classes doesn’t hold—nearly all of them are plugins in Drupal 8, using the standard Drupal 8 plugin API; however, you’ll still see some of these plugins referred to as han‐ dlers in Drupal 8 documentation. 172 | Chapter 4: Drupal Programming Examples Some Views code and documentation use the term argument for what is called a contextual filter in the Drupal 7 and 8 user interface (in Drupal 6 and prior versions of Views, it was also called an argu‐ ment in the user interface). The terms are basically interchangeable in Views. In order to program effectively with Views, you also need to understand how Views uses handlers and plugins to construct its output. Here is a conceptual overview (the actual order of Views performing these steps may be a bit different): 1. Views takes all of the field, relationship, filter, contextual filter, and sort defini‐ tions in the view and creates and executes a database query. This process involves a number of handlers, such as relationship handlers, field handlers, filter handlers, and sort handlers. Fields defined using the Drupal Field API are not added directly to the query unless they are used in relationships, sorts, or filters; they are instead loaded during the display process. 2. If the view uses fields in its display, each field is run through its field handler’s display routines to render the fields. 3. Each row in the database query result is run through a row plugin (known as row style plugin in Drupal 7), if one is in use. Row plugins format the rows, as a com‐ bination of rendered fields and/or raw query output. 4. The formatted rows and/or fields are handed off to the style plugin, which com‐ bines the rows and fields into a larger output. The base Views module includes style plugins for HTML tables, HTML unordered lists, and so on, and each style plugin is compatible with a certain subset of row plugins (for instance, an HTML list can use either a field row plugin or a row plugin that displays the entire entity, whereas an HTML table does not use a row plugin). 5. The formatted output is handed off to the overall display plugin; examples of dis‐ play plugins are the standard Views Page, Block, and Feed displays. Note that Views caches information about what hooks, handlers, and plugins exist (and their properties), so whenever you add or modify the properties of a Views hook implementation, handler class, or plugin class, you will most likely need to clear the Views cache. You can do this on the Views advanced settings page, where you can also disable Views caching while you’re developing. The Views cache is also cleared when you clear the main Drupal cache. Creating Views Module Add-Ons | 173 Further reading and reference: • “The Basics of Drupal 8 Plugin Programming” on page 28 • “The Drupal Cache” on page 10 Setting Up Your Module for Views in Drupal 7 In Drupal 7, several steps are necessary to make sure that Views will recognize your module as a valid provider of default views, handlers, and/or plugins; none of these steps is needed for Drupal 8 Views programming. The first step for Drupal 7 is to implement the Views hook_views_api() hook in your mymodule.module file. To do that, you’ll need to choose a location for some additional files; typically, you make a subdirectory called views in your module directory to hold all of the Views files, and if you are doing a lot of output formatting, optionally another subdirectory for the theme template files. Alternatively, you can just put all the Views files in your main module directory. The hook_views_api() implementation tells Views this informa‐ tion. For example: function mymodule_views_api() { return array( // Which version of the Views API you are using. For Views 7.x-3.x, use 3. 'api' => 3, // Where your additional Views directory is, if you have one. 'path' => drupal_get_path('module', 'mymodule') . '/views', // Where Views-related theme templates are located. 'template path' => drupal_get_path('module', 'mymodule') . '/views/templates', ); } Any files that contain Views classes (see the following sections) will also need to be added to your mymodule.info file, so that they are recognized by the Drupal 7 class loader: files[] = views/name_of_my_include_file.inc In addition, if Views integration is fundamental to the functioning of your module, you can make Views a module requirement by adding the following line to your mymodule.info file: dependencies[] = views Further reading and reference: • “The Basics of Module and Theme Hook Programming” on page 23 174 | Chapter 4: Drupal Programming Examples Providing a New Views Data Source A common need in a custom module is to integrate it with Views—that is, to make the data managed by the module available to Views. If you are storing data in existing entities or using standard Drupal fields to store the data, your data will already be integrated with Views. But if you are defining your own entity type in Drupal 7, or for some reason not using entities, you will need to provide Views integration yourself, by defining a new Views data source (also known as a base table). Once you’ve defined the data source, you can select it when setting up a new view: instead of selecting a Node-module Content view (the default), you can select your data source instead, or if appropriate, you can create a view using a different data type, and use a relationship to join it with your data type. Adding data sources is described in this section; the next section describes how to add fields and relationships to existing data sources. Drupal 8 You can provide Views integration for non-entity data in Drupal 8 by using hook_views_data(), which is very similar to the Drupal 7 hook described here. This is not common, as most data in Drupal 8 should be stored in entities. Views integration for content entities in Drupal 8 is described in “Defining a Content Entity Type in Drupal 8” on page 139. To define a Views data source in Drupal 7, assuming you have already followed the steps in “Setting Up Your Module for Views in Drupal 7” on page 174, start by creat‐ ing a file called mymodule.views.inc, which must be located in the Views directory specified in your hook_views_api() implementation. In Drupal 8, the mymod‐ ule.views.inc file is located in the top-level module directory. In this file, implement hook_views_data(). The return value of this hook, in both versions of Drupal, is an associative array of arrays, where the outermost array key is the database table name, and the array value gives information about that database table, the way it relates to other data tables known to Views, and the database table fields that can be used for filtering, sorting, and field display. Here is an example, showing a subset of the return value of this hook in Drupal 7 for the User module (function user_views_data(), located in the modules/user.views.inc file under the main Views directory). The code has been slightly modified for clarity, and comments have been added (including notes about how you would do something similar in Drupal 8, which is mostly the same except where noted): // The main data table is 'users'. $data['users'] = array(); // The 'table' section gives information about the table as a whole. $data['users']['table'] = array(); Creating Views Module Add-Ons | 175 // Grouping name for fields in this table in the Views UI. $data['users']['table']['group'] = t('User'); // Define this as a base table, meaning it can be used as the starting // point for a view. $data['users']['table']['base'] = array( // Primary key field. 'field' => 'uid', 'title' => t('User'), 'help' => t('Users who have created accounts on your site.'), 'access query tag' => 'user_access', ); // Tell Views this is an entity. $data['users']['table']['entity type'] = 'user'; // Define individual database table fields for use as Views fields, // filters, sorts, and arguments (also known as contextual filters). // The 'uid' database field. $data['users']['uid'] = array( // Overall title and help, for all uses, except where overridden. 'title' => t('Uid'), 'help' => t('The user ID'), // Expose it as a views Field. 'field' => array( // Name of the field handler class to use, for Drupal 7. 'handler' => 'views_handler_field_user', // In Drupal 8, replace this with the ID of the field plugin: // 'id' => 'field', // Override a setting on the field handler. 'click sortable' => TRUE, ), // Expose it as a views Contextual Filter (argument). 'argument' => array( // Name of argument handler class to use, for Drupal 7. 'handler' => 'views_handler_argument_user_uid', // In Drupal 8, replace this with the ID of the argument plugin: // 'id' => 'user_uid', // Override a setting. 'name field' => 'name', ), // Expose it as a views Filter. 'filter' => array( // Call the filter 'Name' instead of 'Uid' in the UI. 'title' => t('Name'), // Name of filter handler class to use, for Drupal 7. 'handler' => 'views_handler_filter_user_name', 176 | Chapter 4: Drupal Programming Examples // In Drupal 8, replace this with the ID of the filter plugin: // 'id' => 'user_name', ), // Expose it as a views Sort. 'sort' => array( // Name of sort handler class to use, for Drupal 7. 'handler' => 'views_handler_sort', // In Drupal 8, replace this with the ID of the sort plugin: // 'id' => 'standard', ), // Define a relationship (join) that can be added to a view of Users, // where this field can join to another base Views data table. Only // one join per base table field can be defined; to define more, you // will need to use dummy field entries in the base table. 'relationship' => array( // Call the relationship 'Content authored' instead of 'Uid'. 'title' => t('Content authored'), // Also override the 'uid' default help. 'help' => t('Relate content to the user who created it.'), // Name of relationship handler class to use, for Drupal 7. 'handler' => 'views_handler_relationship', // In Drupal 8, replace this with the ID of the relationship plugin: // 'id' => 'standard', // Name of Views base table to join to. 'base' => 'node', // Database table field name in the joined table. 'base field' => 'uid', // Database table field name in this table. 'field' => 'uid', // When you add a relationship in the UI, you assign it a label, which // is used elsewhere in the UI. This provides the default value. 'label' => t('nodes'), ), ); Further reading and reference: • “Programming with Entities and Fields” on page 129 • “Setting Up Database Tables: Schema API and hook_update_N()” on page 55 Adding Handlers to Views A hook_views_data() implementation in Drupal 7 refers to the names of handler classes in various spots (fields, filters, sorts, etc.). In Drupal 8, your hook_views_data() or entity views data class instead refers to the IDs of the handler classes. In either case, you have the choice to use handlers provided by the Views module, or a class you create. Creating Views Module Add-Ons | 177 To create your own handler class in Drupal 7, here are the steps: 1. Usually, create a handlers subdirectory inside the Views directory specified in your hook_views_api() implementation. 2. In that subdirectory, create an include file named for your handler class, such as mymodule_views_handler_field_myfield.inc if your class is called mymodule_views_handler_field_myfield. Note that in contrast with the usual Drupal coding standards, for historical reasons Views-related classes are gener‐ ally defined using all-lowercase names with underscores, rather than CamelCase names. 3. In that file, extend an existing Views handler class of the same type (field, filter, etc.), and override the appropriate methods to define the actions of your class. You can find existing Views handlers in the handlers subdirectory of the Views download. For instance, if you are making a field handler, you’ll need to extend the views_handler_field class and override the render() method; if your field handler has display options, you’ll also need to override the option_defini tion() and options_form() methods. 4. Add the handler file to your mymodule.info file, so that the class will be automati‐ cally loaded by the Drupal class-loading system: files[] = views/handlers/mymodule_views_handler_field_myfield.inc In Drupal 8, handlers use the Plugin API; see “The Basics of Drupal 8 Plugin Pro‐ gramming” on page 28 for details on how to define them. The information you’ll need: Namespace The different types of handlers each have their own namespace. For instance, field handlers go in the Plugin\views\field namespace, and contextual filter (argument, in code) plugins go in Plugin\views\argument, under your main module namespace; therefore, in the src/Plugin/views/field and src/Plugin/views/ argument subdirectories under your main module directory. Annotation Each of these types of handlers has an annotation class, such as \Drupal\views \Annotation\ViewsField and \Drupal\views\Annotation\ViewsArgument. Base classes Each type of handler has a base class, such as \Drupal\views\Plugin\views \field\FieldPluginBase, or you might want to extend one of the existing han‐ dler plugins in the appropriate core/modules/views/src/Plugin/views/ subdirectory. 178 | Chapter 4: Drupal Programming Examples Further reading and reference: • “Providing a New Views Data Source” on page 175 or in Drupal 8 for entities, “Defining a Content Entity Type in Drupal 8” on page 139 • “Automatic Class Loading in Drupal” on page 12 • “The Basics of Drupal 8 Plugin Programming” on page 28 Examples—handlers: • In Drupal 7, the Views module’s handlers directory contains general-purpose handlers. Use these as starting points when defining your own handlers. The Drupal 7 API module also has some good examples of handler classes. • In Drupal 8, the Views module’s handlers are in subdirectories of core/modules/ views/src/Plugin/views in Drupal core. Some entity modules also have their own plugins in their src/Plugin/views directories. Adding Fields and Relationships to an Existing Views Data Source In addition to providing completely new Views data sources, as described in “Provid‐ ing a New Views Data Source” on page 175, some custom modules may need to pro‐ vide additional fields or relationships to existing Views data sources. To do this, implement hook_views_data_alter() in the mymodule.views.inc file that you set up in the previous section; this hook takes as input, by reference, the array of all of the hook_views_data() information from all implementing modules and allows you to alter it. Note that in Drupal 8 you would use hook_views_data_alter() to alter entity views data as well as non-entity views data. This example from the Drupal 7 API module illustrates the two most common things you can do with this hook: • Adding a relationship from an existing table to your table: in this example, the reason is that the API module allows users to comment on API documentation pages, so if someone were creating a view whose base data source is comments, they might want to add a relationship to the API documentation page that is being commented upon. Relationships are defined on the base table side, so this relationship needs to be added to the comment table’s Views data. • Adding an automatic join to your table (automatic joins provide additional data‐ base fields to a data source without having to add a relationship to the view): again, this example is comment-related: the node_comment_statistics table is normally automatically joined to the node base table, so that the number-ofcomments field is available on node content items. Automatic joins are defined on the table that is automatically joined to a Views base table, so this join needs Creating Views Module Add-Ons | 179 to be added to the node_comment_statistics table Views data, to automatically join it when the api_documentation table is in a view. Here is the code for Drupal 7 to make these two modifications, with notes added where Drupal 8 would be different: function api_views_data_alter(&$data) { // Add a relationship to the Comment table. The array key must be // unique within the comment table -- do not overwrite any existing // comment fields. $data['comment']['did'] = array( 'title' => t('Documentation ID'), 'help' => t('The ID of the documentation object the comment is a reply to.'), 'relationship' => array( // Table to join to. 'base' => 'api_documentation', // Field in that table to join with. 'base field' => 'did', // Field in the comment table to join with. 'field' => 'nid', // Name of relationship handler class to use for Drupal 7. 'handler' => 'views_handler_relationship', // For Drupal 8, this would be the ID of the relationship plugin, like: // 'id' => 'standard', // When you add a relationship in the UI, you assign it a label, which // is used elsewhere in the UI. This provides the default value. 'label' => t('API documentation object'), 'title' => t('API documentation object'), 'help' => t('The ID of the documentation object the comment is a reply to.'), ), ); // Add an automatic join between the comment statistics table and // the API documentation table. $data['node_comment_statistics']['table']['join']['api_documentation'] = array( // Use an inner join. 'type' => 'INNER', // Field to join on in the API documentation table. 'left_field' => 'did', // Field to join on in the comment statistics table. 'field' => 'nid', ); } Providing a Style or Row Plugin to Views Another common custom Views programming need is to create new style or row plugins. 180 | Chapter 4: Drupal Programming Examples In Drupal 8, style plugins and row plugins use the Plugin API; see “The Basics of Drupal 8 Plugin Programming” on page 28 for details on how to define them. The information you’ll need: Namespace Style plugins go in the Plugin\views\style namespace, and row plugins in Plugin\views\row, under your main module namespace; therefore, in the src/ Plugin/views/style and src/Plugin/views/row subdirectories under your main mod‐ ule directory. Annotation Style plugins have \Drupal\views\Annotation\ViewsStyle annotation, and row plugins have \Drupal\views\Annotation\ViewsRow annotation. Base classes The base class for style plugins is \Drupal\views\Plugin\views\style\Style PluginBase, or you might want to extend one of the existing style plugins in core/ modules/views/src/Plugin/views/style. The base class for row plugins is \Drupal \views\Plugin\views\row\RowPluginBase, or you might want to extend one of the existing row plugins in core/modules/views/src/Plugin/views/row. Theming Style plugin annotation refers to a theme hook, and row plugins can also set up render arrays with new theme hooks. You’ll need to define your theme hooks using hook_theme(). In Drupal 7, row and style plugins are detected by Views using a hook, so the process is a bit more complicated. Here are the steps to follow, assuming you have already followed the steps in “Setting Up Your Module for Views in Drupal 7” on page 174: 1. Implement hook_views_plugins() in your mymodule.views.inc file, which must be located in the Views directory specified in your hook_views_api() implemen‐ tation. The return value tells Views about your style and row style plugin classes. For instance, you might have: function mymodule_views_plugins() { return array( // Overall style plugins 'style' => array( // First style plugin--machine name is the array key. 'mymodule_mystyle' => array( // Information about this plugin. 'title' => t('My module my style'), 'help' => t('Longer description goes here'), // The class for this plugin and where to find it. 'handler' => 'mymodule_views_plugin_style_mystyle', Creating Views Module Add-Ons | 181 'path' => drupal_get_path('module', 'mymodule') . '/views/plugins', // Some settings. 'uses row plugin' => TRUE, 'uses fields' => TRUE, ), // Additional style plugins go here. ), // Row style plugins. 'row' => array( // First row style plugin -- machine name is the array key. 'mymodule_myrowstyle' => array( // Information about this plugin. 'title' => t('My module my row style'), 'help' => t('Longer description goes here'), // The class for this plugin and where to find it. 'handler' => 'mymodule_views_plugin_row_myrowstyle', 'path' => drupal_get_path('module', 'mymodule') . '/views/plugins', // Some settings. 'uses fields' => TRUE, ), // Additional row style plugins go here. ), ); } 2. Create a file for each style or row style plugin class. For example, if you declared that your class is called mymodule_views_plugin_style_mystyle, create a file with the name mymodule_views_plugin_style_mystyle.inc. Put this file in the directory you specified in your hook_views_plugins() implementation (typi‐ cally, plugins are either put into your Views directory or a subdirectory called plugins). 3. List each class-containing include file in your mymodule.info file, so that the class will be automatically loaded by the Drupal class-loading system, with a line like: files[] = views/plugins/mymodule_views_plugin_style_mystyle.inc 4. In each class-containing include file, declare your plugin class, which should extend either the views_plugin_style, views_plugin_row, or another subclass of these classes. You will need to override the option_definition() and options_form() methods if your plugin has options, and (oddly enough) that is usually all you’ll need to override, because the work of formatting the output is done in the theme layer. 5. Set up hook_theme() to define a theme template and preprocessing function for your plugin. The theme template goes into the template directory specified in 182 | Chapter 4: Drupal Programming Examples your hook_views_info() implementation, and the name corresponds to the machine name you gave your plugin (in this example, mymodule-mystyle.tpl.php or mymodule-myrowstyle.tpl.php). Further reading and reference: • “Making Your Module Output Themeable” on page 25 • “Automatic Class Loading in Drupal” on page 12 Examples—plugin classes: • The Views module has several general-purpose plugins, which are good starting points and examples. In Drupal 7, the hook_views_plugins() implementation is in the includes/plugins.inc file, plugin class files are in the plugins directory, and template files are in the theme directory, with theme-preprocessing functions in the theme/theme.inc file. In Drupal 8, plugins are in subdirectories of core/ modules/views/src/Plugin/views in Drupal core. • There are several contributed module projects that provide Views plugin add-ons (they may not yet be ported to Drupal 8). Commonly used examples are Views Data Export, Calendar, and Views Slideshow. You can find others by browsing the “Views” category at https://www.drupal.org/project/modules (but note that only some of the Views-related modules in that list provide style or row plugins). Providing Default Views Once you have your module’s data integrated with Views—either because it is stored in entities using the Entity API module in Drupal 7, core entities, or fields or because you have provided a custom data source as described in the preceding sections—you may want to supply users of your module with one or more default views. These views can be used to provide administration pages for your module or sample output pages, and they can either be enabled by default or disabled by default (administra‐ tors can enable and modify them as needed). Here are the steps to follow to provide one or more default views in your Drupal 7 module, assuming you have already followed the steps in “Setting Up Your Module for Views in Drupal 7” on page 174: 1. Create a view using the Views user interface. 2. From the Views user interface, export the view. This will give you some PHP code starting with $view = new view;. 3. If you want to have the view disabled by default, find the line near the top that says $view->disabled = FALSE; and change FALSE to TRUE. Creating Views Module Add-Ons | 183 4. Implement hook_views_default_views() in a file called mymod‐ ule.views_default.inc, which must be located in the Views directory specified in your hook_views_api() implementation. 5. Put the exported view’s PHP code into this hook implementation: function mymodule_views_default_views() { // Return this array at the end. $views = array(); // Exported $view = new // ... rest // Exported view code starts here. view; of exported code ... code ends here. // Add this view to the return array. $views[$view->name] = $view; // You can add additional exported views here. return $views; } In Drupal 8, views are configuration. So, to provide a default view in a module, here are the steps: 1. Create the view in the Views user interface. 2. Export the configuration to a file. You can do this on the configuration export page in the administrative UI (example.com/admin/config/development/configu‐ ration/single/export), which tells you the filename to save it as. 3. Remove the UUID line near the top from the exported file. This is part of your site configuration, but it should not be set in configuration for other sites to import. 4. Put this file in your module’s config/install or config/optional directory. Further reading and reference: • “Programming with Entities and Fields” on page 129 • “Configuration file format and schema in Drupal 8” on page 42 Creating Rules Module Add-Ons in Drupal 7 The contributed Rules module lets you set up reaction rules, which are a set of actions that are executed in response to events under certain conditions on your website. For example, you could respond to a new comment submission event under the condi‐ 184 | Chapter 4: Drupal Programming Examples tion that the submitter is an anonymous user, by sending the comment moderator an email message. The configuration offered by the Rules user interface is quite flexible and powerful: • You can define the events, conditions, and actions for a reaction rule in the Views user interface, without any programming. • Conditions can be combined using Boolean AND/OR logic. • Actions can have parameter inputs and can provide data outputs, so you can chain actions together, with the output data provided by one action feeding in as a parameter for the next action. • Some actions provide arrays as output, and Rules has a special action that lets you loop over an array, doing one or more actions on each array element. • You can also set up components, which are basically reusable subsets of reaction rules. Components can have inputs and outputs, as well as their own events, con‐ ditions, and/or actions. • Once you have created a component, you can use it like an action in building a reaction rule. • Components can also be used as bulk operations in the Views Bulk Operations module, if they take an entity or a list of entities as their first input value. The Rules module comes with a set of standard events, conditions, and actions, including many related to entities and fields (in Drupal 7, these require the contrib‐ uted Entity API module). This means that if your module stores its custom data in entities and fields, you will be able to use the Rules module with your module’s data without any further programming. But you may occasionally find that you need to do some programming to add additional functionality to the Rules module; in my expe‐ rience, this has always been to add custom actions to Rules; this is described in “Pro‐ viding Custom Actions to Rules” on page 186. In Drupal 7, reaction rules and components that you compose using the Rules user interface can be exported into PHP code and shared with others. One way to do this is by using the Features contributed module. But sometimes Features is cumbersome, and there is a direct method for exporting and sharing reaction rules and compo‐ nents described in “Providing Default Reaction Rules and Components” on page 188. Drupal 8 As of September 2015, the API for Rules in Drupal 8 has not been finalized, but it will definitely be quite different from the Drupal 7 API. The following sections only apply to Drupal 7. Further reading and reference: Creating Rules Module Add-Ons in Drupal 7 | 185 • Rules module: https://www.drupal.org/project/rules. • Features module: https://www.drupal.org/project/features. • Views Bulk Operations module: https://www.drupal.org/project/views_bulk_opera tions. • “Programming with Entities and Fields” on page 129. • For programming with Rules not covered in this book, see the rules.api.php file distributed with the Rules module for documentation. For instance, it is possible to set up custom conditions and events, although it is unlikely you will ever need to, given the flexibility of the base Rules module and its entity integration. Providing Custom Actions to Rules Rules actions are responses to events and conditions detected by the Rules module, and they can take many forms. Built-in actions that come with the Rules module include sending an email message, displaying a message or warning, and altering content (publishing, unpublishing, etc.). As mentioned in the introduction to this section, you can chain together the input and output of several actions and you can also use action output for looping, so some so-called actions are really more like pro‐ cessing steps that exist solely to provide input for other actions that are actually doing the work (modifying content, sending email, etc.). Whether you are defining a processing step type of action or one that actually does work itself, here are the steps you will need to follow to provide a custom action to the Rules module in Drupal 7: 1. Create a file called mymodule.rules.inc in your main module directory, and imple‐ ment hook_rules_action_info() in that file. The return value tells Rules about your custom action: its machine name, a human-readable label for the Rules user interface, the data that it requires as parameters (if any), and the data that it pro‐ vides as output (if any). 2. Create a callback function that executes your action. You can either put this function in your mymodule.rules.inc file, or you can implement hook_rules_file_info() and specify a separate include file for callbacks. The name of the function is the same as the machine name you gave the action. As an example, here is the code to provide a processing-step-type action that takes a content item as input and outputs a list of users (you could then loop over the output list and send each user an email message, for instance): // Optional hook_rules_file_info() implementation. // This specifies a separate file for callback functions. // It goes into mymodule.rules.inc. 186 | Chapter 4: Drupal Programming Examples function mymodule_rules_file_info() { // Leave off the .inc filename suffix. return array('mymodule.rules-callbacks'); } // Required hook_rules_action_info() implementation. // This gives information about your action. // It goes into mymodule.rules.inc. function mymodule_rules_action_info() { $actions = array(); // Define one action. // The array key is the machine name of the action, and also the // name of the function that does the action. $actions['mymodule_rules_action_user_list'] = array( // Label and group in the user interface. 'label' => t('Load a list of users related to content'), 'group' => t('My Module custom'), // Describe the parameters. 'parameter' => array( 'item' => array( 'label' => t('Content item to use'), 'type' => 'node', ), // You can add additional parameters here. ), // Describe the output. 'provides' => array( 'user_list' => array( 'type' => 'list', 'label' => t('List of users related to content'), ), // You could describe additional output here. ), ); // Define other actions here. return $actions; } // Required callback function that performs the action. // This goes in mymodule.rules.inc, or the file defined in // the optional hook_rules_file_info() implementation. function mymodule_rules_action_user_list($item) { // Because the parameter defined for this action is a node, Creating Rules Module Add-Ons in Drupal 7 | 187 // $item is a node. Do a query here to find a list of // users related to this node. // As a proxy for your real code, return a list of one // user -- the author of the content. $ids = array($item->uid); // Load the users and return them to Rules. return array('user_list' => user_load_multiple($ids)); } Further reading and reference: • “The Basics of Module and Theme Hook Programming” on page 23 Providing Default Reaction Rules and Components In some cases, you may find that you want to put reaction rules or components that you have created into PHP code, so that you can use them on another site. You have three choices for how to do this in Drupal 7: • Define the reaction rule or component’s events, conditions, and reactions using pure PHP code. This is somewhat documented in the rules.api.php file dis‐ tributed with the Rules module, but it is not particularly recommended, as you’ll need to read a lot of Rules module code to figure out the machine names of all the pieces, and there isn’t really any documentation on how to put it all together. • Create the reaction rule or component using the Rules user interface, and use the contributed Features module to manage the export. • Create the reaction rule or component using the Rules user interface, export the definition to a text file, and implement a Rules hook to provide it as an in-code rule or component. This process is recommended if you do not want to use the Features module, and is described here. Assuming you want to use the export-to-text option, here are the steps to follow: 1. In the Rules user interface, create your reaction rule or component. If you do not want a reaction rule to be active by default, be sure to deactivate it. 2. From the Rules user interface, export your reaction rule or component, and save the exported text in a file. Put this file in a rules subdirectory of your main mod‐ ule directory, and name it sample_rule.txt (for example). 3. Implement hook_default_rules_configuration() mymodule.rules_defaults.inc, with the following code: 188 | Chapter 4: Drupal Programming Examples in a file named function mymodule_default_rules_configuration() { $configs = array(); // Read in one exported reaction rule. $file = drupal_get_path('module', 'mymodule') . '/rules/sample_rule.txt'; $contents = file_get_contents($file); $configs['mymodule_sample_rule'] = rules_import($contents); // Add other reaction rules and components here if desired. return $configs; } Further reading and reference: • Features module: https://www.drupal.org/project/features • “The Basics of Module and Theme Hook Programming” on page 23 Programming with CTools in Drupal 7 The contributed Chaos Tools (CTools) module is a suite of APIs and tools designed to be used by other modules, including a generic plugin system, a system for packaging configuration data for export into code, a context system for detecting conditions involving the site and its data, and other components. If you are writing a module for Drupal, look through the contents of CTools, and you may find that you can use its tools rather than writing your own code for some of the basic tasks your module needs to do. Implementing CTools Plugins for Panels The contributed Panels module, which is based heavily on CTools, allows you to set up custom page layouts, which are managed using the CTools page manager tool. Panels also uses the CTools context system, which allows the content displayed in the panel layout’s regions to respond to the URL path, properties of content being dis‐ played, the logged-in user’s role, and other conditions. Because Panels uses the CTools plugin system for almost all of its functionality, you can extend and alter the functionality of Panels by creating your own plugins (this process is known as imple‐ menting plugins, to distinguish it from the process of defining plugin types). Programming with CTools in Drupal 7 | 189 Drupal 8 The plugin system defined by the CTools module for Drupal 7 was not adopted for Drupal 8, which has its own plugin system in Dru‐ pal core. Thus, the information about CTools plugins provided here is only applicable to Drupal 7. As of September 2015, the Pan‐ els module has not been finalized for Drupal 8, but it will likely be using the Drupal core plugin system rather than the CTools plugin system described here. This section describes the steps in implementing a CTools plugin for Drupal 7. The example used is a plugin implementation that adds a custom relationship to the CTools context system, for use in Panels. Specifically, the example plugin implemen‐ tation provides a relationship from a user to the most recent node content item the user has authored. The steps to implement this plugin are described in the following sections. Panels relationships are not the same as Views relationships. Also, although in Drupal 7 the Views module depends on the CTools module, Views does not use the CTools plugin system for defining and detecting its plugin and handler types. See “Creating Views Module Add-Ons” on page 171 instead if you are interested in Views. Further reading and reference: • Panels module: https://www.drupal.org/project/panels • Chaos Tools (CTools) module: https://www.drupal.org/project/ctools • Both Panels and CTools have help available on their API, if you install the Advanced Help module, and CTools has a ctools.api.php file with hook documen‐ tation. The Advanced Help topics in Panels and CTools also describe how to use the Panels user interface to create panel pages and use contexts and relationships. Determining plugin background information A CTools plugin implementation consists of an array of definition data and usually one or more callback functions mentioned in the definition array. Plugins come in many varieties, known as types, and each plugin type has a specific format for its defi‐ nition array. So, before implementing a plugin in your module, you need to locate several pieces of background information about the plugin type: • Whether the functionality you want to define can even be provided by imple‐ menting a CTools plugin 190 | Chapter 4: Drupal Programming Examples • If so, which module defines the plugin type that provides this functionality • The machine name of the plugin type • The elements of the definition array that plugins of this type need to define • The callback functions that plugins of this type need to define, and their signa‐ tures • Whether there are any restrictions on the machine name you will choose for your plugin Unfortunately, modules that define plugin types do not have a uniform way of pro‐ viding this information to Drupal programmers, so the rest of this section describes some steps you can go through to locate the information you’ll need. If you already have this information in hand for the plugin you’re implementing, you can skip the rest of this section. A good starting point for determining if the functionality is covered by a plugin would be to look for a README.txt file, a *.api.php file, or Advanced Help in the module whose functionality you are trying to add to. In the present example of adding a custom relationship to Panels, the Panels Advanced Help “Working with the Panels API” topic tells you that relationship plugins are part of the context system provided by the CTools module. The next step is to find the plugin machine name. To do this, you’ll need to locate the implementation of hook_ctools_plugin_type() in the module that defines the plu‐ gin type (this function should be in the main module file). In this case, because it is a plugin type defined by the CTools module itself, you are looking for a function called ctools_ctools_plugin_type() in the ctools.module file; if you had determined that your plugin type was defined by the Panels module, you’d be looking for panels_ctools_plugin_type() in panels.module. In either case, the return value of the function is an array of plugin type definitions, where the keys are the machine names of the plugin types, and the corresponding values are defaults for the plugin definition arrays. Unfortunately, the CTools implementation of this hook does not include the defini‐ tions directly. Instead, it does this: ctools_passthrough('ctools', 'plugin-type', $items); The ctools_passthrough() utility (in the CTools includes/utility.inc file) delegates the work to functions called ctools*plugin_type() in include files called includes/ *plugin-type.inc. Scanning the list of files matching this pattern in the CTools module download, you can eventually find the plugin type definition in the ctools_con text_plugin_type() function in CTools the includes/context.plugin-type.inc file: Programming with CTools in Drupal 7 | 191 $items['relationships'] = array( 'child plugins' => TRUE, ); This plugin type definition tells you two things. First, the machine name of the plugin type is relationships (note the final s!). Second, because the plugin definition array is very simple, this plugin type uses the standard CTools implementation methods described in this section. Most plugins have a simple array like this in their hook_ctools_plugin_type() implementation; there are two elements to watch out for that could be present in a plugin definition array that make slight changes to the implementation method described here: extension The plugin definition should be placed in a file with a different extension than the default *.inc. info file The plugin definition should be provided in .info file format (like a module or theme .info file) instead of in a PHP array. The specifics of plugins using this for‐ mat are not covered in this book, so you’ll have to look for existing examples instead. The next step is to figure out what data and callbacks this plugin type expects. Look for this information in a README.txt file, an Advanced Help topic, or in existing plugin implementations from the module that defines the plugin type. For the present example, there is a CTools Advanced Help topic that includes a description of the def‐ inition array elements, and there are several relationship plugins in the CTools plu‐ gins/relationships directory that you can use as examples. Finally, you’ll need to choose a machine name for your plugin; this example uses the machine name mymodule_relationship_most_recent_content. As with other Dru‐ pal programming, machine names must be unique, so it is customary to use the mod‐ ule name as a prefix, followed by a descriptive name. Be careful though: some types of CTools plugins have implicit (and likely undocumented) machine name restrictions, because they get stored in a database table field of that length. So to avoid trouble, it is probably best to pick as short of a name as possible that is still unique and descrip‐ tive. For instance, CTools content type plugins (which provide content panes you can insert into Panels layouts) have an implicit machine name length maximum of 32 characters. Notifying CTools about plugin implementations With the necessary background information in hand, the next step in creating a CTools plugin is to tell CTools that your module includes plugin implementations, and where to find them. Each plugin goes in its own file, and plugin files that you create must be placed in directories that are specific to the plugin type. The usual 192 | Chapter 4: Drupal Programming Examples convention is to put CTools plugins into the plugins subdirectory under your main module directory, and organize them into subdirectories under that. To tell CTools where your plugin directory is, implement hook_ctools_plu gin_directory() in your main mymodule.module file: function mymodule_ctools_plugin_directory($module, $plugin) { return 'plugins/' . $module . '-' . $plugin; } The function parameters are the machine names of the module and plugin type; the return value is the directory name that you want to use for plugins of that type. You can make your directory structure from these inputs however you wish, as long as each plugin type you implement has its own directory. The example here means that an implementation of a relationship plugin defined by the CTools module goes into the plugins/ctools-relationships subdirectory under the main module directory. Further reading and reference: • “The Basics of Module and Theme Hook Programming” on page 23 Writing the plugin implementation code The next step in implementing a CTools plugin is to create a PHP file for your plugin implementation in your plugin directory. The filename is the machine name you chose for your plugin, with a .inc extension (plugins/ctools-relationships/mymod‐ ule_relationship_most_recent_content.inc in this example). The file starts with the definition of the $plugin array in the global scope, which con‐ tains the definition for your plugin implementation: $plugin = array( 'title' => t('My Module context relationship plugin'), 'description' => t('Locates the most recent content item authored by a user'), 'required context' => new ctools_context_required(t('User'), 'user'), 'context' => 'mymodule_relationship_most_recent_content', 'keyword' => 'node', ); Notes: • The specific elements of the definition array depend on the type of plugin you are implementing. • Many plugin types use title and description elements, and these values should be passed through the t() function so that they are translated for display in the Drupal user interface. Programming with CTools in Drupal 7 | 193 • The required context element is specific to CTools context plugins; it tells CTools what the context input is for your plugin implementation. In this exam‐ ple, the one input is the user whose most recent content is to be found. If your plugin takes multiple inputs, you can make this an array. • The context element is the name of the callback function that CTools will call when this relationship is selected as part of a Panels page. • The keyword element is a suggestion for the user interface, which provides the default name for the relationship result. The final step is to define any callback functions referenced in your definition array. These also go into your include file, and their signatures are specific to the plugin type. This example requires one function: function mymodule_relationship_most_recent_content($context = NULL, $config) { // Read the user ID from the context. If you have multiple context inputs, // $context will be an array of contexts. But there is only one here. if (empty($context) || empty($context->data) || empty($context->data->uid)) { // If there is a problem, return an empty CTools context. This is also // used by CTools to determine the output data type of this plugin. return ctools_context_create_empty('node', NULL); } $uid = $context->data->uid; // Locate the most recent content node created by this user. $nid = db_select('node', 'n') ->fields('n', array('nid')) ->condition('uid', $uid) ->orderBy('created', 'DESC') ->range(0,1) ->execute() ->fetchField(); // Load the node item if possible. if (!$nid) { return ctools_context_create_empty('node', NULL); } $node = node_load($nid); if (!$node) { return ctools_context_create_empty('node', NULL); } // Return the found node in a CTools context. return ctools_context_create('node', $node); } As usual, clear the cache: “The Drupal Cache” on page 10. 194 | Chapter 4: Drupal Programming Examples Providing Default CTools Exportables CTools defines a concept of exportables, in which a set of configuration, such as a view from the Views module or a panel from the Panels module, can be exported into code. The user can modify the configuration in the administrative user interface, and this overrides the configuration in code. If you want to create a panel, view, or other CTools exportable and save it to code in order to preserve it or share it, you can do so either using the contributed Features module, or directly. “Providing Default Views” on page 183 shows how to do a direct export of a view; this section describes how to do it for other exportables. As an example, assume that you have a mini panel that you’ve created in the Panels UI and want to export into code. Here are the steps you would follow to get it exported: 1. Each exportable has a hook that allows you to provide it in code, and you’ll need to locate the name of this hook. 2. The hook name is defined in some code that is in the defining module’s hook_schema() implementation in the module’s modulename.install file. In this example, mini panels come from the Mini Panels sub-module of Panels, so the function we’re looking for is panels_mini_schema(), and this is located in the panels/panels_mini/panels_mini.install file. 3. In the hook_install() implementation, locate the data table related to the item you’re trying to export, and within that, find the 'export' array. In this example: $schema['panels_mini'] = array( 'export' => array( 'identifier' => 'mini', 'load callback' => 'panels_mini_load', 'load all callback' => 'panels_mini_load_all', 'save callback' => 'panels_mini_save', 'delete callback' => 'panels_mini_delete', 'export callback' => 'panels_mini_export', 'api' => array( 'owner' => 'panels_mini', 'api' => 'panels_default', 'minimum_version' => 1, 'current_version' => 1, ), 4. If the 'export' array contains an 'api' section, that means that in order for CTools to recognize your module as a valid provider of these items, you will need to implement (usually) hook_ctools_plugin_api(). Some exportables may use a Programming with CTools in Drupal 7 | 195 different API hook; if so, hopefully they have documented this fact. Here’s the implementation: function mymodule_ctools_plugin_api() { return array( // The API version. 'api' => 1, ); } 5. The 'export' array may contain a 'default hook' element, which gives the name of the exportables hook we’re looking for. If this is not provided, as in this case, the default name is 'default_' followed by the table name ('panels_mini' here). This value does not include the 'hook' prefix. So, in our example, the hook is hook_default_panels_mini(). 6. To provide one or more exported items, implement this hook in your mymod‐ ule.module file. The return value is an array of exported items, keyed by the machine names of the items. Hopefully, the exportable will have a page that allows you to export the item into code, probably into a large text field on the page. Copy the exported code, and paste the value into your hook implementa‐ tion function. For example: function mymodule_default_panels_mini() { $minis = array(); // Paste exported code here. It starts out: $mini = new stdClass(); // ... // Find this line with the machine name in it: $mini->name = 'mymodule_test'; // ... // After the export is pasted, you'll have $mini holding one exported // mini panel. Put it into the return array. $minis['mymodule_test'] = $mini; // Add additional mini panels here. // Return them all. return $minis; } 7. As usual, clear the cache. 196 | Chapter 4: Drupal Programming Examples Further reading and reference: • “The Basics of Module and Theme Hook Programming” on page 23 • “Setting Up Database Tables: Schema API and hook_update_N()” on page 55 • “The Drupal Cache” on page 10 Programming with CTools in Drupal 7 | 197 CHAPTER 5 Programming Tools and Tips As you launch yourself into (or continue) what will hopefully be many productive years as a Drupal programmer, I hope that you will continue to keep the principles of Chapter 2 in mind and avoid the mistakes listed in Chapter 3. You should be able to keep returning to Chapter 4 and outside references such as Examples for Developers for examples and ideas. And for further study, there are suggestions sprinkled throughout this book. In closing, I’d like to offer a few final tips, tools, and suggestions that you should find useful in your endeavors. Where to Find More Information Drupal Site Building and General Drupal Information When I started using and programming with Drupal, there weren’t really any books available on using Drupal to build websites, so I don’t have any specific general Dru‐ pal book recommendations; the Drupal project maintains a list of current books about Drupal at https://www.drupal.org/books. Here is a list of online resources on site building and the Drupal project in general: https://www.drupal.org/project/user_guide The official User Guide for Drupal, which as of June 2015 is a brand-new work in progress, covers the basics of site building with Drupal. https://www.drupal.org/documentation The Drupal Community Documentation, a wiki-like compendium of pages about nearly everything in Drupal (installation, site building, programming, etc.). It has a lot of coverage, but because it is open to editing by all members of 199 the Drupal community, it is of varying quality and only somewhat organized. Within this documentation, the “Developing for Drupal” section and the “Them‐ ing” section are of most use to programmers; other sections are aimed at setting up sites with Drupal, configuring modules, and the like. https://www.drupal.org/planet Drupal Planet, which is an aggregated feed composed of many Drupal-related blogs. Subscribe to keep up to date on new developments in Drupal and to read blog posts on programming topics. https://groups.drupal.org Central place to find topical and geographical Drupal groups, each of which has a forum. Many of them also have meetings and events (online or in-person) that you can attend. https://www.drupal.org/irc The Drupal community uses IRC for online chatting, and this section of the Dru‐ pal website contains a channel list and background information. http://drupal.stackexchange.com Questions and answers about Drupal. https://association.drupal.org Website of the Drupal Association, the nonprofit organization behind the Drupal project. https://www.drupal.org/project/modules and https://www.drupal.org/project/themes Search for downloads of contributed Drupal modules and themes. Some people prefer the search interface of http://drupalmodules.com for finding modules. Drupal Programming Reference and Background The Drupal API changes often enough that if someone tried to write an API refer‐ ence book, it would probably be outdated before it was published. So, the following online resources are recommended (in addition, some of the general Drupal resour‐ ces of the previous section have programming information): https://api.drupal.org The API reference site for Drupal. As of this writing, this site only includes Dru‐ pal core and a few contributed modules; http://drupalcontrib.org is a similar refer‐ ence site that includes popular Drupal contributed modules. Use one of these sites to find documentation about a specific Drupal function, class, or constant whose name you know. See “Using api.drupal.org” on page 201 for more information. 200 | Chapter 5: Programming Tools and Tips https://www.drupal.org/developing/api Tutorials and conceptual explanations for core Drupal APIs. Use this reference if you do not know what function you need to use, or if you need more background information. https://www.drupal.org/project/examples The Examples for Developers project, which is a set of tutorial-like example modules that aim to illustrate all of the core Drupal APIs. https://www.drupal.org/writing-secure-code Documentation about writing secure code in Drupal. Also, Greg James Knaddi‐ son, one of the prominent members of the Drupal Security Team, has written Cracking Drupal: A Drop in the Bucket (Wiley), which is widely considered to be the definitive reference for Drupal security. https://www.drupal.org/coding-standards The coding standards for the Drupal project. https://www.drupal.org/new-contributors A list of tasks for people with a variety of skill sets, with step-by-step instructions, suitable for people who are new to contributing to the Drupal project. https://www.drupal.org/novice Detailed instructions on how to contribute patches (code fixes) to Drupal. Using api.drupal.org The https://api.drupal.org website is an invaluable reference for Drupal programmers (note: I may be biased in thinking this, as I currently maintain the software that the site runs on and am also the Drupal core committer/maintainer for API documenta‐ tion). Several contributed Drupal modules also run API reference sites that use the same software. Here are some features that you can take advantage of; not all of them may be avail‐ able on all API reference sites, depending on what version of the API software they are running: • Every Drupal core file, class, interface, constant, function, method, service, etc. (item) has its own documentation page on the site. As of this writing, there are plans to include all of the contributed modules on this site as well, but until that happens, the http://drupalcontrib.org site (which runs the same software and includes many popular contributed modules) can be used. • Most items are well documented and most of the documentation is accurate. The documentation is built from comments in the source code, so it tends to be upda‐ ted when code is updated. (If you find a problem, you can click a link on the page to report an issue.) Where to Find More Information | 201 • The source code of each item is shown, so if the documentation is unclear, you can read the code to see what’s really going on. • Hooks (places where modules can alter the Drupal core behavior) are also docu‐ mented. The function body of hook documentation gives a sample hook implementation. • You can search directly for any item by name, if you know the name of what you are looking for, using the search block in the sidebar. Note that when searching for function names, you do not include () after the name. • Topic pages provide additional documentation about the Drupal API: explana‐ tions and a list of related functions and classes. If you do not know the name of the specific Drupal function or class you want to use, try browsing the Topics list. • The landing pages for Drupal 8 and (to some extent) Drupal 7 provide links to important topics and overviews. • There is a lot of cross-linking: — File pages show all items defined in the file, and items link to their file. So if you are on a function page and want to find related functions, click on the file link to see what else is defined in the same file. — Topic pages show related items, and items link to topics. So if you are on a function page and it has a related topics link, try that link to find related functions. — Items in code listings link to their pages, as much as possible (e.g., names of called functions link to the function pages). — Each function page has a link to a page showing all the places in Drupal core that call the function. There are also pages showing string references to func‐ tions, where hooks are invoked, which classes use a particular class, etc. on their respective pages. — Classes and interfaces have extended hierarchy listings, showing which classes extend or implement them. PHP Resources There are hundreds of books about PHP, and you should be able to find one that suits your needs, background, and style preferences. My recommendations: • I learned PHP, MySQL, and the basics of web programming from PHP in a Nut‐ shell by Paul Hudson (O’Reilly) and Web Database Applications with PHP and MySQL by Hugh E. Williams and David Lane (O’Reilly). These books are fairly old by now, so you can probably find a more updated reference—Modern PHP by Josh Lockhart (O’Reilly) looks promising. 202 | Chapter 5: Programming Tools and Tips • The newest PHP language features like traits and namespaces, which are used extensively in Drupal 8, may not be familiar to all PHP programmers. You can learn about traits at http://bit.ly/php_traits and namespaces at http://bit.ly/ php_namespaces. • For reference information about specific PHP functions, see http://php.net (that is always the most up-to-date reference; you can also download the entire reference for local or offline access). • Coding practices for PHP: http://www.phptherightway.com (also created by Josh Lockhart). Database Resources Drupal can run on a variety of databases; most commonly, people use either MySQL, a MySQL clone such as MariaDB or Percona, or PostgreSQL. If you program with Drupal, you will need to use the Drupal Database API for maximum portability rather than writing MySQL or other database queries directly. Because of this, web‐ sites and references aimed at specific databases are of limited use to Drupal program‐ mers. But I do recommend the following: • Web Database Applications with PHP and MySQL (previously mentioned) as a good starting point for learning the basics of queries useful for web program‐ ming. Again, it is a bit old; you may be able to find a more recent book. • SQL Pocket Guide by Jonathan Gennick (O’Reilly), which highlights the similari‐ ties and differences between the various databases’ query syntax and capabilities. Other Web Technology Resources Again, Web Database Applications with PHP and MySQL is a good starting point for learning about how the web server, PHP scripting language, database, and browser interact in web applications in general. For reference on HTML, CSS, and JavaScript, I recommend: • http://www.webplatform.org—reference guide to HTML and CSS. • If you prefer a book format, the O’Reilly pocket references are handy: CSS Pocket Reference by Eric A. Meyer and HTML & XHTML Pocket Reference by Jennifer Niederst Robbins. • For JavaScript, I am continually pulling out my well-worn copy of JavaScript: The Definitive Guide (O’Reilly), which contains both the basics of JavaScript pro‐ gramming and an API reference. Where to Find More Information | 203 • Drupal makes extensive use of the jQuery JavaScript library, which has a compre‐ hensive online API reference at http://docs.jquery.com. Drupal Development Tools The Drupal community has developed several very useful development tools that can help you avoid making programming mistakes, adhere to the Drupal coding stand‐ ards, and debug your Drupal sites and Drupal code. Here is a list of the most useful development tools: Coder A set of modules that points out coding errors and violations of the Drupal cod‐ ing standards, and also helps you upgrade your code from one Drupal version to another. Some developers have, in the past, preferred to use the Drupal Code Sniffer project, which has now been merged into the Coder project. (https:// www.drupal.org/project/coder) Devel A set of modules containing a number of helpful functions for debugging and developing modules and themes, as well as a fake lorem ipsum content generator for testing. (https://www.drupal.org/project/devel) Drupal for Firebug A Firefox/Firebug or Chrome web browser plugin that works with the Devel module to display information about how each Drupal-generated page was built. (https://www.drupal.org/project/drupalforfirebug) Drush A project that provides a command-line shell that greatly speeds up the process of developing a site, with commands for downloading and installing modules, clearing the Drupal cache, and more. It also has an API for module developers that lets a module expose its administrative functions as Drush commands. Learning a few key Drush commands will save you a lot of time, because in one command and a few seconds of waiting, you can do things that would otherwise take you several clicks and page loads on a site’s administrative interface. (https:// www.drupal.org/project/drush) 204 | Chapter 5: Programming Tools and Tips Coding standards It is a very good idea to follow the Drupal coding standards in your Drupal programming. This practice has several benefits: • It makes your code more uniform, matching the style of exist‐ ing Drupal core and contributed module code. • It makes your code easier to read and maintain going forward. • If you plan to contribute your code to the Drupal project, it will eventually need to comply with these standards, so it’s best to start now. Further reading and reference: • Drupal project coding standards: https://www.drupal.org/coding-standards Discovering Drupal API Functions and Classes Programmers who are new to Drupal sometimes don’t know about all of the useful functions and classes available to them in the Drupal API. Most PHP programmers know that they can go to http://php.net to find documentation on built-in PHP func‐ tions; the Drupal project has a similar API site at https://api.drupal.org, which lists all of the functions, constants, classes, and files in Drupal core. See “Using api.dru‐ pal.org” on page 201 for more information. I’ll also just mention that a lot of useful functions are defined in the common.inc and bootstrap.inc files in the includes directory of Drupal core (core/includes in Drupal 8). You can search for either file on the API site; browsing their function lists is a great way to familiarize yourself with what’s available in the Drupal API. You might also want to browse the Topics list on the API site, and in particular the topics linked on the API site landing pages for Drupal 7 and 8, to get an idea of what general areas of functionality the API covers. To help you figure out which functions are the most useful, here is a list, in order, of the 20 most-often-called functions within Drupal core version 7 (with Drupal 8 equivalents and notes where there are differences): • t() • variable_get() [see \Drupal::config() and \Drupal::state() in Drupal 8] • db_query() • variable_set() [see \Drupal::config() and \Drupal::state() in Drupal 8] Discovering Drupal API Functions and Classes | 205 • url() • drupal_set_message() • db_select() • theme() [eliminated in Drupal 8; use render arrays instead] • db_update() • drupal_static() • check_plain() [eliminated in Drupal 8; theme system escapes unsafe text] • drupal_get_path() • db_delete() • user_access() • db_insert() • module_invoke_all() [see \Drupal::moduleHandler() in Drupal 8] • l() • watchdog() • drupal_static_reset() • drupal_alter() [see \Drupal::moduleHandler() in Drupal 8] Drupal 8 is a lot more object-oriented than Drupal 7. So, here is a list of the mostoften-used classes and interfaces in Drupal 8 (the ones with the most use declarations): • \Drupal\Core\Form\FormStateInterface • \Symfony\Component\DependencyInjection\ContainerInterface • \Drupal\Component\Utility\SafeMarkup • \Drupal\simpletest\WebTestBase • \Symfony\Component\HttpFoundation\Request • \Drupal\Tests\UnitTestCase • \Drupal\Core\Url • \Drupal\Core\Session\AccountInterface • \Drupal\Core\Language\LanguageInterface • \Drupal\Core\Entity\EntityInterface • \Drupal\views\Views • \Drupal\simpletest\KernelTestBase 206 | Chapter 5: Programming Tools and Tips • \Drupal\Core\Render\Element • \Drupal\Core\Routing\RouteMatchInterface • \Drupal\Core\Entity\EntityManagerInterface • \Drupal\Component\Utility\Unicode • \Drupal\Core\Extension\ModuleHandlerInterface • \Symfony\Component\Routing\Route • \Symfony\Component\Validator\Constraint • \Drupal\Core\Cache\Cache • \Drupal\views\ViewExecutable • \Drupal\Core\Form\FormBase • \Drupal\Core\Entity\EntityStorageInterface • \Drupal\Core\Database\Connection • \Drupal\Core\Entity\EntityTypeInterface • \Drupal\Core\Field\FieldStorageDefinitionInterface • \Drupal\Core\Access\AccessResult • \Drupal\Core\Field\FieldItemListInterface • \Symfony\Component\HttpFoundation\Response Other Programming Tips and Suggestions Here are a few final suggestions that will help you improve your Drupal code and programming experience: • Set up your own local development server with a LAMP stack (or WAMP, MAMP, etc.), rather than trying to develop using a remote server. • On your development server, edit your php.ini file (or equivalent) so that you are displaying all PHP notices, warnings, and errors. Do not consider your code to be working unless there are no notices at all, as they generally indicate bugs. Also use the Database Logging module during development, and check the “Recent log messages” report to see any errors you might have missed. • Get a good code editor that does syntax highlighting, automatic indenting, and parentheses matching. The classic editor choices are Emacs and Vim/Vi. • A more powerful alternative to a good code editor is an integrated development environment (IDE), which offers many advantages (debugging, type hinting, Other Programming Tips and Suggestions | 207 etc.). The most popular choice for Drupal development is PhpStorm, and most Drupal 8 developers would consider this to be an essential tool. • Follow the Drupal coding standards. You should be able to set up your code edi‐ tor to use the standard Drupal indentation practice (two spaces, never use tabs), and to display or remove extra end-of-line spaces. • Write thorough documentation for every function, constant, and class that you define, preferably before you write the code. This will help you or others main‐ tain your code going forward, and writing the documentation first will help ensure that you know what the function or class is really supposed to do before you start writing it. • Test your code, preferably by writing automated tests. • Use a revision control system, such as Git or Subversion, to keep track of the changes you make to modules and themes you develop. Further reading and reference: • Drupal coding standards: https://www.drupal.org/coding-standards • “Principle: Drupal Code Is Tested and Documented” on page 67 208 | Chapter 5: Programming Tools and Tips Index Symbols #access property, in form arrays, 111 #access_callback property, in form arrays, 111 #ajax property, in render arrays, 124 #attached property in Drupal 7 render arrays, 105 in Drupal 8 render arrays, 106 #autocomplete_path property in render arrays, 119 #autocomplete_route_name property in render arrays, 119 #contextual_links property, in render arrays, 97 #markup property, in render arrays, 104 #theme property, in render arrays, 103 #title property, in form arrays, 111 #type property in form arrays, 110 in render arrays, 103 $form_state parameter, 111 @, in service declarations, 31 @Annotation annotation, 31, 34 @Block annotation, 102 @ConfigEntityType annotation, 151 @ContentEntityType annotation, 139 @FieldFormatter annotation, 169 @FieldType annotation, 163 @FieldWidget annotation, 165 @FormElement annotation, 104 @RenderElement annotation, 104 @Translation() annotation, 34, 50 @ViewsArgument annotation, 178 @ViewsField annotation, 178 @ViewsRow annotation, 181 @ViewsStyle annotation, 181 @ViewsWizard annotation, 148 A access arguments, 91 access callback, 137 access() method, entity classes, 67 accessibility, 52 AccountProxyInterface interface, 65 addTag() method, database query classes, 59 administrative links, 95-98 for configuration entities, 158 administrative pages for Drupal 7 entries, 137 for Drupal 8 configuration entries, 157-160 for Drupal 8 content entries, 147 Ajax command-based callback functions, 127 form setup for, 124-126 programming with, 123-129 wrapper-based callback functions, 127 AjaxResponse class, 128 ajax_command_css() function, 128 ajax_command_html() function, 128 alter hooks, 18 plugin definitions, 33 altering forms, 121-123 alterRoutes() method on route subscriber classes, 99 annotation, 28 plugin, 34 annotation class, 32 defining, 30 anonymous users, 8, 9 API functions list, 205 209 arguments, in Views, 173 Asynchronous JavaScript and XML (AJAX), 124 authenticated users, 8, 9 autocomplete, 119-121 autoloading classes, 12-15 in hook_menu(), 90 B Bartik (core theme), 22 base fields, entities, 130 base tables, Views module, 175 base theme, 22 base_table annotation (entities), 142 Block annotation class, 101 block system, 3 BlockBase class, 101 BlockPlugInterface interface, 101 blocks defining theme regions for placement, 71-79 in HTTP requests, 9 plugin manager, 31 providing output in, 88 registering in Drupal 7, 100-101 registering in Drupal 8, 101 bug databases (issue queues), 83 build() method, block classes, 102 buildForm() method, form classes, 115 bundles, 130 Buytaert, Dries, 5, 82 C cache clearing, 10 page, 8 Cache API context, 12 Drupal 7, 11 Drupal 8, 11 get() method, 11 invalidate() method, 11 set() method, 11 storage bins, 11 cache service, 11 cache system, 10-12 Cache::invalidateTags() method, 12 CacheBackendInterface, 11 cache_clear_all() function, 11 210 | Index cache_get() function, 11 cache_set() function, 11 callback functions, Ajax command-based, 127 overview, 124 wrapper-based, 127 CCK (Content Construction Kit), 5 check_markup() function, 63 check_plain() function, 63 check_url() function, 63 class autoloading, 12-15 Classy (base theme), 22 clearing the cache, 10 CMF (content management framework), 1 CMS (content management system), 1 code committing, 85 contributing to Drupal community, 83-85 overexecuting, 79 Coder module, 204 CommandInterface Ajax interface, 129 comment entities, 130 comment system, 3 Composer, 13 condition() method database queries, 62 entity queries, 161 Config class, 45 config.factory service, 45 config/install directory, 42 config/optional directory, 43 ConfigEntityBase class, 151 ConfigEntityBundleBase class, 151 ConfigEntityInterface interface, 151 ConfigEntityListBuilder base class, 156 ConfigFormBase class, 115 Configuration API configuration entities, 46 file format, 42 get() method, 46 getEditable() method, 46 in Drupal 8, 42-47 overrides, 46 schema, 42, 150 simple configuration, 45 configuration data, 40 configuration entity type configuration schema for, 150 defining in Drupal 8, 149-160 entity interface and class for, 151-153 handlers for, 153-157 module enabling for, 160 overview, 130 routing/route controllers for, 157-160 configuration file format/schema, 42 Drupal 8, 42-44 configuration schema, 42, 43, 150 configuration storage in Drupal 8, 42 configuration YAML files, 42 configuration, making translatable, 51 confirmation forms, 66, 116-119 ConfirmFormBase class, 117 confirm_form() function, 116 container object, get() method, 33 container rebuilding, 38 ContainerInterface Symfony interface, 35 content displaying, 102-106 in content management system, 40 Content Construction Kit (CCK), 5 content entities, 130 content entity type defining entity interface and class, 139-143 defining handlers, 143-145 in Drupal 8, 139-149 module enabling for, 149 routing and links, 145-147 Views integration, 147-148 content fields, 3 content management framework (CMF), 1 content management system (CMS), 1 content types, 130 ContentEntityBase class, 139 ContentEntityDeleteForm class, 144 ContentEntityInterface interface, 139 context system, in CTools, 189 context, in HTTP requests, 9 contextual filters, in Views, 173 contextual links, 96 contributed modules, ix, 3 ControllerBase class, 93 controllers, 9, 19, 93 core entities, 130 core, hacking of, 19 core.services.yml file, 36 Cracking Drupal: A Drop in the Bucket (Knad‐ dison), 201 CSS adding to a page in Drupal 7, 105 adding to a page in Drupal 8, 106 adding to render arrays in Drupal 7, 105 adding to render arrays in Drupal 8, 106 CSS Pocket Reference (Meyer), 203 CTools (Chaos Tools) module Drupal 7 programming for, 189-196 exportables for, 195-196 implementation code for, 193 plugin background information for, 190 plugin implementation notifications, 192 plugins for Panels, 189-194 current_user service, 110 D Database API evolution, 5 querying databases with, 58-61 databases Drupal as database-independent, 5, 54-61 information resources for, 203 querying with Database API, 58-61 risks of saving PHP code in, 81 setting up tables for, 55-58 DBTNG (Database, the Next Generation), 54 db_query() function, 58 db_select() function, 59 decorating page content, 9 defaults, in route definitions, 93 defining plugin types, 29-31 delete form, 155 delivery methods, 8 dependency injection, 18, 35-38 Devel module, 204 development tools, 204 directories for modules/themes, 22 discovery mechanism, plugins, 28 dispatch() method on event dispatcher class, 38 dispatching events, 38 display plugins, in Views, 173 displaying content, 102-106 entity type setup for, 136 generating paged output, 107-109 render arrays for page/block output, 102-106 render arrays in Drupal 8, 106 distributions, 2 documentation, 67-70 Drupal Index | 211 basic elements of, 1-15 evolution of, 5-7 license, 1 overview, 1-5 Drupal Association, 200 Drupal community, 1, 2 engaging with, 82-86 reporting issues/contributing code to, 83-85 Drupal Community Documentation, 199 Drupal core, 2, 64-66 Drupal Planet, 200 Drupal Security Team, 201 Drupal user groups, 83 Drupal way of programming, vii Drupal.t() JavaScript function, 49 Drupal::cache() method, 11 Drupal::currentUser() method, 65, 110 Drupal::entityManager() method, 161 Drupal::entityQuery() method, 160 Drupal::entityQueryAggregate() method, 160 Drupal::formBuilder() method, 115 Drupal::getContainer() method, 35 Drupal::l() method, 63 Drupal::service() method, 35 drupal_autoload_class() function, 13 drupal_autoload_interface() function, 13 drupal_flush_all_caches() function, 11 drupal_get_form() function, 113, 116 drupal_render() function, 25 Drush, 38, 204 Drush command, 10 dynamic queries, 59-61 dynamic routes, 98 E ElementInterface, 28 ElementInterface interface, 104 entities, 75 and internationalizing user-entered text, 51 base fields, 130 defining configuration entity type in Drupal 8, 149-160 defining content entity type in Drupal 8, 139-149 defining entity type in Drupal 7, 132-139 defining fields for, 161-164 editing forms in Drupal 7, 137 editing forms, configuration entities in Dru‐ pal 8, 153 212 | Index editing forms, content entities in Drupal 8, 143 in Drupal 7, 5, 41, 130 in Drupal 8, 42 in Drupal core, 130 list builder classes, 147, 156, 159 permissions, 66 pre-existing vs. designing your own, 132 programming with, 129-171 querying and loading in Drupal 8, 160 route provider class, 146, 158 terminology, 129-132 EntityConfirmFormBase class, 155 EntityForm base class, 153 EntityStorageInterface interface, 161 EntityViewBuilder class, 144 EntityViewsData base class, 147 entity_autocomplete render element, 119 entity_keys annotation, 142 entity_view() function, 136 Event class, 38 event subscribers, 9, 38, 79, 98 getSubscribedEvents() method, 39 event system, 19, 38 events (Symfony definition), 38 EventSubscriberInterface Symfony interface, 39, 98 event_dispatcher service, 38 event_subscriber service tag, 39 evolution of Drupal, 5-7 Examples for Developers project, 201 exportables, for CTools module, 195-196 F field formatters programming with, 168-171 repurposing, 168 field instances, 131 field type, defining, 161-164 Field UI plugin manager, 31 field widgets programming with, 164-167 repurposing, 167 fieldable entity types, 130 fielded data, avoiding custom programming with, 75-77 FieldItemBase class, 163 FieldItemInterface interface, 163 fields adding to existing Views data source, 179 and entity types, 130 defining type, 161-164 for content text, 51 in Drupal 7, 5 in Drupal 8, 42 overview, 131 programming with, 129-171 terminology, 129-132 file entities, 130 Firebug, 204 Form API, 109-123 adding autocomplete to forms, 119-121 Ajax, 124-126 altering forms, 121-123 confirmation forms, 116-119 form arrays, 110 form generation/processing in Drupal 7, 112-113 form generation/processing in Drupal 8, 114-116 form state arrays, 111 form state objects, 111 getFormID() method, 115 permissions/security in, 66 setErrorByName() method, 115 validateForm() method, 115 form modes, 122, 122, 131 FormatterBase class, 169 FormatterInterface interface, 169 formatters, 131 FormBase class, 114 FormElementInterface interface, 104 FormStateInterface interface, 111 free and open-source software (FOSS), 1 fully qualified namespaces, 14 functional tests, 68 G generic hooks, 18 Gennick, Jonathan, 203 get() method cache objects, 11 configuration objects, 46 container class, 35 container object, 33 getDefaultOperations() method on entity list builder classes, 159 getEditable() method, configuration objects, 46 getForm() method on form builder classes, 115 getFormID() method, form objects, 115 getSubscribedEvents() method, event sub‐ scriber classes, 39 GNU General Public License (GPL), 1 groups.drupal.org, 83 H hacking, 19 hacking core, 19 handlers for configuration entity type, 153-157 for content entity type, 143-145, 172 for Views module, 177-179 hasPermission() method on user object, 110 hook(s) alter hooks, 18 defined, 18 documentation, 202 implementing and overriding, 19, 23 overly general, 80 hook_block_info(), 100 hook_block_view(), 100 hook_boot(), 79 hook_cache_flush(), 11 hook_ctools_plugin_api(), 195 hook_ctools_plugin_directory(), 193 hook_ctools_plugin_type(), 191 hook_default_rules_configuration(), 188 hook_element_info(), 28, 104 hook_entity_info(), 132, 137 hook_field_formatter_info(), 168 hook_field_formatter_view(), 168 hook_field_info(), 163 hook_field_schema(), 163 hook_field_widget_form(), 164 hook_field_widget_info(), 164 hook_field_widget_info_alter(), 167 hook_field_widget_WIDGET_TYPE_form_alter(), 167 hook_flush_caches(), 11 hook_form_alter(), 80 hook_form_FORM_ID_alter(), 80, 122, 122 hook_init(), 79 hook_install(), 135 hook_menu(), 65, 89, 90, 95, 96, 120, 137 hook_menu_alter(), 80, 91, 97 hook_menu_links_discovered_alter(), 97 hook_menu_local_actions_alter(), 97 Index | 213 hook_menu_local_tasks(), 97 hook_menu_local_tasks_alter(), 97 hook_permission(), 64, 137 hook_rebuild(), 11 hook_rules_action_info(), 186 hook_rules_file_info(), 186 hook_schema(), 23, 55, 57, 134, 195 hook_theme(), 26, 101, 144, 181, 182 hook_update_N(), 23 hook_views_api(), 174 hook_views_data(), 147, 175, 175 hook_views_data_alter(), 179 hook_views_default_views(), 184 hook_views_plugins(), 181 HTML & XHTML Pocket Reference (Robbins), 203 HTTP request handling, 9-10 Drupal 7, 7 Drupal 8, 9 Hudson, Paul, 202 I IDE (integrated development environment), 207 implementing plugins, 33 information storage and separation Drupal 7, 40 Drupal 8, 41 installation profile system, 2 integrated development environment (IDE), 207 international audience and user interface text, 49 user interface language translation for, 48-52 internationalization system, 3 invalidate() method, cache objects, 11 invoking hooks, 18 IRC, 83, 200 issue queues, 83 issues reporting to Drupal community, 83-85 subscribing to, 84 J JavaScript adding to a page in Drupal 8, 106 adding to render arrays in Drupal 7, 105 adding to render arrays in Drupal 8, 106 214 | Index and Ajax, 124 JavaScript Object Notation (JSON), 8, 120 JavaScript: The Definitive Guide (Flanagan), 203 K kernel, 9 Knaddison, Greg James, 201 L l() function, 63, 63 Lane, David, 202 libraries for JavaScript and CSS files, 106 libraries.yml files, 106 license, 1 LIKE function, 59 links annotation section configuration entities, 157 content entries, 145-147 links.action.yml files, 96, 157, 158 links.contextual.yml files, 97 links.menu.yml files, 95, 157, 158 links.task.yml files, 96, 146, 158 lists of content, using Views module, 76 loadMultiple() method on entity storage classes, 161 local actions for configuration entities, 158 local task links defining, 95 for configuration entities, 158 for content entities, 146 Lockhart, Josh, 202 M machine names, 4, 20 maintenance mode, 8, 9 major versions, releases of, ix management pages Drupal 7 entities, 137 Drupal 8 configuration entities, 156 Drupal 8 content entities, 147 manager, plugin, 31-33 MariaDB, 203 menu entry (router entry), 88 MENU_LOCAL_ACTION, 96 MENU_LOCAL_TASK, 95 Meyer, Eric A., 203 mobile-first orientation, 52 Modern PHP (Lockhart), 202 module .info file, 20 module .info.yml file, 20 module .install file, 23, 55 module .module file, 21 module directory naming conventions, 14, 20 module system, 3 and Drupal Core, 2 module-defined entities, 130 modules creating, 20-23 directories for, 22 hook programming, 23 implementing a plugin in, 33 making output themeable, 25-28 N namespace, 13 namespace declaration, 14 namespace, plugin, 32 navigation menu builder, 3 node, 130 node_access() function, 67 O overexecuting code, 79 overrides, configuration, 46 overriding services, 37 P page arguments, 91 page cache, 8 page controllers, 9, 19, 93 page delivery methods, 8 page loads, executing code on, 79 page.html.twig file, 78 page.tlp.php file, 78 paged output, generating, 107-109 PagerDefault query extender class, 107 PagerSelectExtender query extender class, 107 Panels module, CTools plugins for, 189-194 Panels relationships, Views relationships vs., 190 patch files, 85 paths, 89 PDO (PHP Data Objects), 5 Percona, 203 permissions, 62, 64-66 Drupal core system, 64-66 dynamic in Drupal 8, 65 in displaying/operating on content, 66 in forms, 66 permissions.yml file, 64, 142, 152 PHP class autoloading, 12-15 information resources, 202 risks of saving code in database, 81 PHP Data Objects (PDO), 5 PHP in a Nutshell (Hudson), 202 PhpStorm, 207 PHPUnit tests, 67 placeholders in hook_menu() in Drupal 7, 90 in queries, 58 plugin annotation, 34 plugin annotation class, 32 defining, 30 Plugin API in Drupal 8, 28-35 as alteration mechanism, 18 in evolution of Drupal, 3 plugin definition alter hook, 33 plugin managers, 31-33 plugin namespace, 32 plugin subdirectory, 32 plugin types, 28 plugin(s) data needed by implementers, 32 in CTools, 189 in Views, 172 PluginId annotation class, 30 PostgreSQL, 203 programming mistakes, 71-86 excessive programming, 71-79 overexecuting code, 79 overly general hooks, 80 saving PHP code in database, 81 with fielded data, 75-77 working alone, 82-86 programming principles, 17-70 accessibility and usability, 52 database independence, 54-61 security issues, 61-67 separation of content, configuration, and state data, 39-47 testing and documentation, 67-70 programming tips/suggestions, 207 properties of entities, 130 Index | 215 property keys, render API, 103 PSR-0/PSR-4, 13 Q queries addTag() method, 59 avoiding, 75 dynamic, 59-61 for entities in Drupal 8, 160 simple, 58 with Database API, 58-61 QueryAggregate interface for entity queries, 160 QueryInterface interface for entity queries, 160 R reaction components in Rules, 188 reaction rules, 184, 188 rebuild.php script, 38 rebuilding the container, 38 regions, theme, 77-79 registry, 13 relationships adding to existing Views data source, 179 in Views, 76 render arrays and URL registration, 94 for page/block output, 102-106 in Drupal 7, 8, 25 in Drupal 8, 9, 25, 106 render elements and, 104 theme functions/templates vs., 26 render element, 26, 104 render element plugins, 28 Response Symfony class, 94 revision key, in entity annotation, 142 revisions, 132 revision_table annotation, 142 Robbins, Jennifer Niederst, 203 roles, 65 route altering, 98-100 route collections, 99 route controllers, 93, 157-160 route identifiers, 93 router entry, 88 routes, 9, 19, 157-160 defining, 92 for adding entities, 159 for autocomplete, registering, 120 216 | Index for content entities, defining, 145 for form pages, 115 for forms, defining, 118 RouteSubscriberBase class, 98 routing system subscriber, 80 routing.yml files, 65, 92, 115, 118, 120, 145, 157, 159 row style plugins, in views, 173, 180-183 RowPluginBase views class, 181 RSS aggregation, 3 Rules module creating add-ons in Drupal 7, 184-189 custom actions for, 186-188 default reaction rules/components, 188 rules.inc files, 186 rules_default.inc files, 188 S Schema API, 55-58 schema.yml files, 43, 150 security and permissions, 64-66 and user input issues, 61-67 cleansing/checking user input, 62 in forms, 66 permissions in displaying/operating on con‐ tent, 66 SELECT statement, 58 semitrusted users, 61 service location, 35 ServiceModifierInterface interface, 37 ServiceProvider class, 37 services, 9 cache service, 11 config.factory service, 45 container get() method, 35 core, 3 current_user service, 110 default class, 37 defining, 31, 36 dependency injection container, 3, 9 entity.manager service, 161 entity.query service, 160 event_dispatcher service, 38 event_subscriber service tag, 39, 99 in Drupal 8, 18, 35-38 overriding, 37 state service, 47 string_translation service, 49 tags, 36 services.yml files, 36, 39, 99 set() method, cache classes, 11 setErrorByName() method, form state object, 115 settings.php file, 8, 9, 40, 42, 46 settings.yml files, 42 Seven (core theme), 22 short names, 20 SimpleTest tests, 67 site building, information sources for, 199 software license, 1 SQL Pocket Guide (Gennick), 203 staging directory (for configuration), 42 Stark (base theme), 23 State API, 47 state information, 40 state service, 47 StateInterface interface, 47 StringTranslationTrait trait, 49 string_translation service, 49 structured form arrays, 109 style plugins in Views, 173, 180-183 StylePluginBase views class, 181 sub-themes, 4, 22 Symfony and event system in Drupal 8, 38 and HTTP request handling in Drupal 8, 9 and routing system in Drupal 8, 19 Symfony class-loading system, 14 T t() function, 49-50, 63, 106 t() method, 106 taxonomy system, 3 taxonomy_term entities, 130 taxonomy_vocabulary entities, 130 template system, 2 template.php file, 21 testing, 67-70 theme .info file, 20, 77 theme .info.yml file, 20, 77 theme .theme file, 21 theme hooks, 18, 23, 26 theme preprocessing hooks, 18 theme processing hooks, 18 theme regions, defining for block placement, 77-79 theme system, 2, 3 theme template, 18, 26 theme template file, overriding, 23 theme template.php file, 21 theme() function, 18 and render arrays, 26 in Drupal 7, 25 theme(s) base themes, 22 creating, 20-23 creating sub-themes, 22, 77 defining regions for block placement, 77-79 directories for, 22 functions/templates vs. render arrays, 26 hook programming, 23 inheritance, 22 making module output themeable, 25-28 ticket systems (issue queues), 83 tips, programming, 207 top-level directories, 22 translations, 132 trusted users, 61 U Unicode, 48 unit tests, 68 update function, 56 update.php script, 56 Url class, 63 URL registration, 88-102 administrative links, 95-98 altering in Drupal 7, 89-91 altering in Drupal 8, 98-100 in Drupal 7, 89-91 in Drupal 8, 92-94 providing dynamic routes in Drupal 8, 98-100 usability, 52 use declarations, 13, 14 user entities, 130 User Guide for Drupal, 199 user interface, internationalizing, 49 user-entered text cleansing/checking, 62 insecurity of, 61-67 internationalizing in Drupal 7, 50 internationalizing in Drupal 8, 51 user_access() function, 65, 110 UUID (Universally Unique Identifier), 42, 130 Index | 217 V validateForm() method, form classes, 115 value form element type, 111 variable_get() function, 40 variable_set() function, 40 view modes, 131 Views data source adding fields and relationships to, 179 and Views integration, 175 Views integration, 147-148 Views module adding fields and relationships to existing Views data source, 179 adding handlers to, 177-179 creating add-ons for, 171-184 default views for, 183 defining base tables, 175 module setup in Drupal 7, 172 output construction, 173 programming terminology, 172 style/row plugins, 180-183 Views relationships, Panels relationships vs., 190 218 | Index views.inc files, 175 views_default.inc files, 184 vocabularies, 130 W Web Accessibility Initiative (WAI), 52 Web Database Applications with PHP and MySQL (Williams), 202, 203 WidgetBase base class, 165 WidgetInterface interface, 165 widgets, 131 Williams, Hugh E., 202 WizardPluginBase class, 148 World Wide Web Consortium (W3C), 52 wrapper-based callback functions, for Ajax, 127 X XML, Ajax and, 124 Y YAML format, 42 About the Author Jennifer Hodgdon wrote her first computer program in 1982 and has been a profes‐ sional software developer since 1994, using a wide variety of programming languages (FORTRAN, C, Java, PHP, JavaScript, and others). She started doing PHP/MySQL web programming professionally around 2002 and set up her first Drupal website in 2007. Soon after that, she started contributing volunteer time to the Drupal open source project and the Seattle Drupal User Group: organizing meet-ups and confer‐ ences, serving as the coleader and then the leader of the Drupal Documentation Team in 2011–2012, leading workshops, and making conference presentations. She is cur‐ rently a freelance Drupal site builder and module programmer, the volunteer main‐ tainer of several Drupal modules, the coorganizer of the Spokane Washington Drupal User Group, and the Drupal core maintainer/committer for API documentation and coding standards. She can be contacted through her business website, poplar‐ ware.com, or through her drupal.org user account (jhodgdon). Colophon The animal on the cover of Programmer’s Guide to Drupal is a French angelfish (Pomacanthus paru). The French angelfish is native to shallow reefs in the Atlantic Ocean, from New York in the north to Brazil and Ascension Island in the south. French angelfish typically pair up for life, beginning with an early morning rendez‐ vous between April and September. Playfully chasing each other leads to spawning and then tending stable abodes hidden among the coral during the night. Pairs of French angelfish fiercely defend the territory of their chosen hiding places against neighbors but can also demonstrate a charming curiosity in the presence of snorkelers. Juvenile banderitas, as they are called in Spanish, appear with markedly different col‐ oring from that of the adults, for whom the vertical yellow stripes of youth have dis‐ solved into pervasive, yellow-tinged black scales. When still young, French angelfish glean food by wiping ectoparasites with their pelvic fins from other fish on the reef who venture into “cleaning stations.” Adults of the species live less symbiotically, con‐ suming algae, sponges, and other invertebrates living on the reef. Many of the animals on O’Reilly covers are endangered; all of them are important to the world. To learn more about how you can help, go to animals.oreilly.com. The cover image is from Cuvier’s Animals. The cover fonts are URW Typewriter and Guardian Sans. The text font is Adobe Minion Pro; the heading font is Adobe Myriad Condensed; and the code font is Dalton Maag’s Ubuntu Mono.

Source Exif Data:
File Type                       : PDF
File Type Extension             : pdf
MIME Type                       : application/pdf
PDF Version                     : 1.5
Linearized                      : No
Author                          : Jennifer Hodgdon
Create Date                     : 2015:10:08 16:11:59Z
Modify Date                     : 2017:03:20 23:11:01-08:00
Page Count                      : 235
XMP Toolkit                     : Adobe XMP Core 5.2-c001 63.139439, 2010/09/27-13:37:26
Creator Tool                    : AH CSS Formatter V6.2 MR4 for Linux64 : 6.2.6.18551 (2014/09/24 15:00JST)
Metadata Date                   : 2015:10:08 16:14:12-04:00
Producer                        : Antenna House PDF Output Library 6.2.609 (Linux64)
Trapped                         : False
Format                          : application/pdf
Title                           : Programmer’s Guide to Drupal
Creator                         : Jennifer Hodgdon
Document ID                     : uuid:e41683d0-cb93-4640-b948-54b5ae135bde
Instance ID                     : uuid:2e0d0b0f-e11e-624a-aadd-0eee2395d420
Language                        : ±Ü
Page Layout                     : SinglePage
Page Mode                       : UseOutlines
EXIF Metadata provided by EXIF.tools

Navigation menu