text:
I am writing this quick tutorial in the hopes it helps someone else out there. There are a few guides out there to do similar tasks to this. They just are not quite what I wanted.
To give everyone an idea on the desired outcome, this is what I wanted to achieve:Example user profile with 2 custom tabs in it.
Before I dive into this, I will mention that you can do this with views, if all that you want to produce is content supplied by views. Ivan wrote a nice article on this. In my situation, I wanted a completely custom route, controller and theme function. I wanted full control over the output.
Steps to add sub tabs
Step 1 - create a new module
If you don't already have a module to house this code, you will need one. These commands make use of Drupal console, so ensure you have this installed first.
drupal generate:module --module='Example module' --machine-name='example' --module-path='modules/custom' --description='My example module' --package='Custom' --core='8.x'
Step 2 - create a new controller
Now that you have a base module, you need a route
drupal generate:controller --module='example' --class='ExampleController' --routes='"title":"Content", "name":"example.user.contentlist", "method":"contentListUser", "path":"/user/{user}/content"'
Step 3 - alter your routes
In order to use magic autoloading, and also proper access control, you can alter your routes to look like this. This is covered in the official documentation.
# Content user tab.
path: '/user/{user}/zones'
_controller: '\Drupal\example\Controller\ExampleController::contentListUser'
_title: 'Content'
_permission: 'access content'
_entity_access: 'user.view'
user: \d+
type: entity:user
# Reports user tab.
path: '/user/{user}/reports'
_controller: '\Drupal\example\Controller\ExampleController::reportListUser'
_title: 'Reports'
_permission: 'access content'
_entity_access: 'user.view'
user: \d+
type: entity:user
Step 4 - create
This is the code that actually creates the tabs in the user profile. No Drupal console command for this unfortunately. The key part of this is defining
base_route: entity.user.canonical.
title: 'Content'
route_name: example.user.contentlist
base_route: entity.user.canonical
weight: 1
title: 'Reports'
route_name: example.user.reportList
base_route: entity.user.canonical
weight: 2
Step 5 - enable the module
Don't forget to actually turn on your custom module, nothing will work until then.
drush en example
Example module
The best (and simplest) example module I could find that demonstrates this is the Tracker module in Drupal core. The Tracker module adds a tab to the user profile.
PHP 7.3.0 was released in December 2018, and brings with it a number of improvements in both performance and the language. As always with Drupal you need to strike a balance between adopting these new improvements early and running into issues that are not yet fixed by the community.
Why upgrade PHP to 7.3 over 7.2?
What hosting providers support PHP 7.3?
All the major players have support, here is how you configure it for each.
Somewhere around April 2019 the option to choose PHP 7.3 was released. You can opt into this version by changing a value in Acquia Cloud. This can be done on a per environment basis.The PHP version configuration screen for Acquia Cloud
Pantheon have had support since the April 2019 as well (see the announcement post). To change the version, you update your
pantheon.yml file (see the docs on this).
# Put overrides to your pantheon.upstream.yml file here.
# For more information, see: https://pantheon.io/docs/pantheon-yml/
api_version: 1
php_version: 7.3
pantheon.yml file
On a side note, it is interesting that PHP 5.3 is still offered on Pantheon (end of life for nearly 5 years).
Unsure when Platform.sh released PHP 7.3, but the process to enable it is very similar to Pantheon, you update your
.platform.app.yaml file (see the docs on this).
# .platform.app.yaml
type: "php:7.3"
.platform.app.yaml file
PHP 7.3 is also available on Dreamhost, and can be chosen in a dropdown in their UI (see the docs on this).The 'Manage Domains' section of Dreamhost
Dreamhost also win some award for also allowing the oldest version of PHP that I have seen in a while (PHP 5.2).
When can you upgrade PHP 7.3
Drupal 8
As of Drupal 8.6.4 (6th December 2018), PHP 7.3 is fully supported in Drupal core (change record). I have been running PHP 7.3 with Drupal 8 for a while now and have seen no issues, and this includes running some complex installation profiles such as Thunder and Lightning.
Any Drupal 8 site that is reasonably up to date should be fine with Drupal 8 today.
Drupal 7
Slated for support in the next release of Drupal 7 - being Drupal 7.68 (see the drupal.org issue), however there are a number of related tasks that seem like deal breakers. There also is not PHP 7.3 and Drupal 7 tests running daily either.
It seems like for the mean time, it is probably best to hold off on the PHP 7.3 upgrade until 7.68 is out the door, and also contributed modules have had a chance to upgrade and release a new stable release.
A simple search on Drupal.org yields the following modules that look like they may need work (more are certainly possible):
- composer_manager (issue)
- scald (issue) [now fixed and released]
- video (issue)
- search_api (issue) [now fixed and released]
Most of the issues seem to be related to this deprecation - Deprecate and remove continue targeting switch. If you know of any other modules that have issues, please let me know in the comments.
Drupal 6
For all you die hard Drupal 6 fans out there (I know a few large websites still running this), you are going to be in for a rough ride. There is a PHP 7 branch of the d6lts Github repo, so this is promising, however the last commit was September 2018, so this does not bode well for PHP 7.3 support. I also doubt contributed modules are going to be up to scratch (drupal.org does not even list D6 versions of modules anymore).
To test this theory, I audited the current 6.x-2.x branch of views
$ phpcs -p ~/projects/views --standard=PHPCompatibility --runtime-set testVersion 7.3
............................................................ 240 / 261 (92%)
..................... 261 / 261 (100%)
3 errors in views alone. The errors are show stoppers too
Function split() is deprecated since PHP 5.3 and removed since PHP 7.0; Use preg_split() instead
If this is the state of one of the most popular modules for Drupal 7, then I doubt the lesser known modules will be any better.
If you are serious about supporting Drupal 6, it would pay to get in contact with My Drop Wizard, as they at least at providing support for people looking to adopt PHP 7.
Mass.gov dev team releases open source project
The Mass.gov development team is proud to release a new open source project, Drupal Test Traits (DTT). DTT enables you to run PHPUnit tests against your Drupal web site, without wiping your database after each test class. That is, you test with your usual content-filled database, not an empty one. We hope lots of Drupal sites will use DTT and contribute back their improvements. Thanks to PreviousNext and Phase2 for being early adopters.
Mass.gov is a large, content-centric site. Most of our tests click around and assert that content is laid out properly, the corresponding icons are showing, etc. In order to best verify this, we need the Mass.gov database; testing on an empty site won't suffice. The traditional tool for testing a site using an existing database is Behat. So we used Behat for over a year and found it getting more and more awkward. Behat is great for facilitating conversations between business managers and developers. Those are useful conversations, but many organizations are like ours — we don't write product specs in Gherkin. In fact, we don't do anything in Gherkin beside Behat.
Meanwhile, the test framework inside Drupal core improved a lot in the last couple of years (mea culpa). Before Drupal Test Traits, this framework was impossible to use without wiping the site's database after each test. DTT lets you keep your database and still test using the features of Drupal's BrowserTestBase and friends. See DrupalTrait::setUp() for details (the bootstrap is inspired by Drush, a different open source project that I maintain).Zakim Bridge at Night, North End Boston. Photo by David Fox.
Using DTT in a Test
- Our test cases extend ExistingSiteBase, a convenience class from DTT that imports all the test traits. We will eventually create our own base class and import the traits there.
- Notice calls to $this->createNode(). This convenience method wraps Drupal's method of the same name. DTT deletes each created node during tearDown().
- Note how we call Vocabulary::load(). This is an important point — the full Drupal and Mink APIs are available during a test. The abstraction of Behat is happily removed. Writing test classes more resembles writing module code.
More Features
- DTT also supports testing through a real browser using headless Chrome or Selenium. So, testing client-side interactions like autocomplete, #states, viewports, and drag/drop is easy.
- Like Drupal core, DTT can save HTML snapshots for each URL that it navigates to. These files are very useful when debugging test failures.
- See the DTT repo for details on how to install and run tests
- Typically, one does not run tests against a live web site. Tests can fail and leave sites in a "dirty" state so it's helpful to occasionally refresh to a pristine database.
If you have questions or comments about DTT, please comment below or submit issues/PRs in our repository.
More from Moshe: Our modern development environment at Mass.gov
Interested in a career in civic tech? Find job openings at Digital Services.
Follow us on Twitter | Collaborate with us on GitHub | Visit our site
PHP 5.6 will officially be no longer supported through security fixes on December 31, 2018. This software has not been actively developed for a number of years, but people have been slow to jump on the bandwagon. Beginning in the new year, no bug fixes will be released for this version of PHP. This opens the door for a dramatic increase in security risks if you are not beginning the new year on a version of PHP 7. PHP 7 was released back in December 2015 and PHP 7.2 is the latest version that you can update to. PHP did skip over 6; so don't even try searching for it.
Drupal 8.6 is the final Drupal version that will support PHP 5.6. Many other CMS's will be dropping their support for PHP 5.6 in their latest versions as well. Simply because it is supported in that version does not mean that you will be safe from the security bugs; you will still need to upgrade your PHP version before December 31, 2018. In addition to the security risks, you have already been missing out on many improvements that have been made to PHP.
What Should You Do About This?
You are probably thinking "Upgrade, I get it." It may actually be more complicated than that and you will need to refactor. 90-95% of your code should be fine. The version your CMS is may affect the complexity of your conversion. Most major CMS's will handle PHP 7 right out of the box in their most recent versions.
By upgrading to a version of PHP 7, you will see a variety of performance improvements; the most dramatic being speed. The engine behind PHP, Zend Technologies, ran performance tests on a variety of PHP applications to compare the performance of PHP 7 vs PHP 5.6. These tests compared requests per second across the two versions. This relates to the speed at which code is executed, and how fast queries to the database and server are returned. These tests showed that PHP 7 runs twice as fast and you will see additional improvements in memory consumption.
How Can Mobomo Help?
Mobomo's team is highly experienced, not only in assisting with your conversion, but with the review of your code to ensure your environment is PHP 7 ready. Our team of experts will review your code and uncover the exact amount of code that needs to be converted. There are a good number of factors that could come into play and affect your timeline. The more customizations and smaller plugins that your site contains, the more complex your code review and your eventual conversion could be. Overall, depending on the complexity of the code, your timeline could vary but this would take a maximum of 3 weeks.
Important Things to Know:
- How many contributed modules does your site contain?
- How many custom modules does your site contain?
- What does your environment look like?
What are Spectre and Meltdown?
Have you noticed your servers or desktops are running slower than usual? Spectre and Meltdown can affect most devices we use daily. Cloud servers, desktops, laptops, and mobile devices. For more details go to: https://meltdownattack.com/
How does this affect performance?
We finally have some answers to how this is going to affect us. After Pantheon patched their servers they released an article showing the 10-30% negative performance impact that servers are going to have. For the whole article visit: https://status.pantheon.io/incidents/x9dmhz368xfz
I can say that I personally have noticed my laptop's CPU is running at much higher percentages than before the update for similar tasks.
Security patches are still being released for many operating systems, but traditional desktop OSs appear to have been covered now. If you haven't already, make sure your OS is up to date. Don't forget to update the OS on your phone.
Next Steps?
So what can we do in the Drupal world? First, you should follow up with your hosting provider and verify they have patched your servers. Then you need to find ways to counteract the performance loss. If you are interested in performance recommendations, Four Kitchens offers both frontend and backend performance audits.
As a quick win, if you haven't already, upgrade to PHP7 which should give you a performance boost around 30-50% on PHP processes. Now that you are more informed about what Spectre and Meltdown are, help with the performance effort by volunteering or sponsoring a developer on January 27, 2018 and January 28, 2018 for the Drupal Global Sprint Weekend 2018, specifically on performance related issues: https://groups.drupal.org/node/517797
Chris Martin is a support engineer at Four Kitchens. When not maintaining websites he can be found building drones, computers, robots, and occasionally traveling to China.
Blog posts about backend engineering, frontend code work, programming tricks and tips, systems architecture, apps, APIs, microservices, and the technical side of Four Kitchens.Read more Development
How many web development challenges can the Laravel PHP framework help you solve? How much easier/faster does it make it for you to tackle common developer tasks?
These are the 2 main questions that we'll try to answer in this blog post here.
For the faster you'll be solving inevitable problems rising during your web app's development process, the more time you'll gain for sparking your creativity working on the app-specific logic!
So, speaking of the common development challenges and how they can make the best criteria for choosing your most suitable web framework, let us enlist them! Outlining, as well, how precisely Laravel can help you easily overcome them:
1. Addressing Technical Vulnerabilities
The challenge you're facing: fixing technical (and therefore "security", as well) vulnerabilities that will inevitably "infiltrate" your web app during its development process (e.g. cross-site scripting, SQL injection etc.)
Laravel's solution:
- it comes equipped with a shield against all major security vulnerabilities
- its codebase has been constantly examined and monitored by hundreds of developer eyes
- it restricts unauthorized users' access to your apps' crucial data or paid resources
2. Error and Exception Handling
The challenge you're facing:
- configuring your app so that it properly handles errors (data-entry forms being the most frequent "error generators")
- triggering prompt notifications (error messages) to let the user know that he/she has entered incorrect data
Laravel's solution:
- error handling is already configured
- moreover, the PHP framework already incorporates the Monolog logging library, a powerful support for a multitude of long handlers
- it runs regular updates in order to minimize errors on your website/app
3. Creating a Custom and Secure Authentication and Authorization System
The challenge you're facing: putting together an user authorization and authentication system so that unauthorized users shouldn't gain access to your app's paid resources/critical data
Laravel's solution:
- it puts at your disposal, right out-of-the-box, everything you need for building your authentication system
- in addition, it makes structuring your authorization logic very simple
- it grants you full control to all the needed resources
4. Separating Client Side/Presentation Code from Server-Side/Business Logic Code
The challenge you're facing:
- helping your web designers "grow independent" from their developer co-workers by making all the needed changes to your web app's HTML layout themselves
- also, by drawing a clear line between presentation and logic from an early stage in the web development process you'll be enabling developers, too, to fix bugs and to add on new features a lot faster
Laravel's solution: being an MVC framework, the "presentation logic-business logic separation issue" is solved by default!
5. Configuring a Scheduler and Managing Scheduled Tasks
The challenge you're facing: putting in place some sort of task scheduling system (and by "task" we do refer to automated database table cleanups, to regular emails to be sent out automatically to subscribers from your list etc.)
Laravel's solution:
- its built-in command scheduler enables you to configure your schedule from within Laravel
- just one Crone entry on your server will be more than enough
6. Configuring Delivery Delays, Managing Queued Messages
The challenge you're facing:
- putting together a message queue mechanism in the early stage of development for better handling that "heavy" load of requests (per second) that your web server's challenged with
- a queue mechanism that should minimize risks of data loss and improve page loading time by speeding up web requests
Laravel's solution: its queue services "spoils" you with a unified API, irrespective of the queue back-end type
7. Scheduling Automated Tests With Laravel PHP Framework
The challenge you're facing: initializing automated testing sessions, which are much more time-effective and usually more accurate than the standard manual ones
Laravel's solution:
- luckily for you the Laravel PHP framework comes with out-of-the-box PHPunit tests and a phpunit.xml file
- moreover, it's equipped with functionalities enabling expressive testing by simulating common user behaviors (link clicking, filling out forms and so on)
8. Configuring URL Routing
The challenge you're facing: defining a clean and simple URL routing so that your app should clearly understand the user's intention, specifically which page he/she'd like to navigate to
Laravel's solution:
- you'll find all your Laravel PHP framework's routes in its app/Http/routes.php file (don't worry, it will load by default)
- the basic ones accept Closure and URI, allowing you to easily configure your routes
9. Integrating Your Web App With Mail Services
The challenge you're facing: building a system sending out notifications to your subscribers following key events
Laravel's solution:
- it provides a simple, yet powerful and clean API over the SwiftMailer library
- it also provides you with drivers for Mandrill, SparkPost, Mailgun, SMTP, Amazon SES, Sendmail, PHP's mail function; a "plug and play" solution for your app to instantly send out emails either via a local or via a cloud-based service
- it also offers you support for sending out notifications (so not just emails) via multiple channels: Slack, SMS via Nexmo...
The challenge you're facing: boosting your web app's performance by integrating it with cache back-ends
Laravel's solution:
- Laravel PHP framework supports robust cache backends such as Redis and Memcached
- it's built to use the file cache driver by default (storing the cached components in its file system)
- moreover, it's even configured to efficiently handle multiple cache configurations
Summing Up
The Laravel PHP framework does streamline your basic development tasks (turning them from "challenges" into simple "to Do" tasks) helping you gain priceless time! Time that you can invest in "crafting code".
Now we're more than curious to read your own thoughts about this web framework!
Putting this here because I didn't see it mentioned elsewhere and it might be useful for others. Thinking about the history of the Islandora solution packs for different media types, the Basic Image Solution Pack was probably the first one written. Displaying a JPEG image, after all, is — well — pretty basic. I'm working on an Islandora project where I wanted to add a viewer to Basic Image objects, but I found that the solution pack code didn't use them. Fortunately, Drupal has some nice ways for me to intercede to add that capability!
Step 1: Alter the /admin/islandora/solution_pack_config/basic_image form
The first step is to alter the solution pack admin form to add the Viewers panel. Drupal gives me a nice way to alter forms with hook_form_FORM_ID_alter().
/** * Implements hook_form_FORM_ID_alter(). * * Add a viewers panel to the basic image solution pack admin page */ function islandora_ia_viewers_form_islandora_basic_image_admin_alter(&$form, &$form_state, $form_id) { module_load_include('inc', 'islandora', 'includes/solution_packs'); $form += islandora_viewers_form('islandora_image_viewers', 'image/jpeg', 'islandora:sp_basic_image'); }
Step 2: Insert ourselves into the theme preprocess flow
The second step is a little trickier, and I'm not entirely sure it is legal. We're going to set a basic image preprocess hook and in it override the contents of $variables['islandora_content']. We need to do this because that is where the viewer sets its output.
/** * Implements hook_preprocess_HOOK(&$variables) * * Inject ourselves into the islandora_basic_image theme preprocess flow. */ function islandora_ia_viewers_preprocess_islandora_basic_image(array &$variables) { $islandora_object = $variables['islandora_object']; module_load_include('inc', 'islandora', 'includes/solution_packs'); $params = array(); $viewer = islandora_get_viewer($params, 'islandora_image_viewers', $islandora_object); if ($viewer) { $variables['islandora_content'] = $viewer; } }
I have a sneaking suspicion that the hooks are called in alphabetical order, and since islandora_ia_viewers comes after islandora_basic_image it all works out. (We need our function to be called after the Solution Pack's preprocess function so our 'islandora_content' value is the one that is ultimately passed to the theming function. Still, it works!
Most of the information I have come across about migrating from Drupal 6 to Drupal 8 is about migrating content, however before tackling this problem another one must be solved, maybe it is obvious and hence understated, so let's spell it out loud: preserving the site functionality.
That means checking if the contrib modules need to be ported to Drupal 8, and also checking if the solution used in the previous version of the site can be replaced with a completely different approach in Drupal 8.
Let's take ao2.it as a study case.
When I set up ao2.it back in 2009 I was new to Drupal, I choose it mainly to have a peek at the state of Open Source web platforms.
Bottom line, I ended up using many quick and dirty hacks just to get the blog up and running: local core patches, theme hacks to solve functional problems, and so on.
Moving to Drupal 8 is an opportunity to do things properly and finally pay some technical debt.
For a moment I had even thought about moving away from Drupal completely and use a solution more suited to my usual technical taste (I have a background in C libraries and linux kernel programming) like having the content in git and generate static web pages, but once again I didn't want to miss out on what web frameworks are up to these days, so here I am again getting my hands dirty with this little over-engineered personal Drupal blog, hoping that this time I can at least make it a reproducible little over-engineered personal Drupal blog.
In this series of blog posts I'll try to explain the choices I made when I set up the Drupal 6 blog and how I am re-evaluating them for the migration to Drupal 8.
The front page view
ao2.it was also an experiment about a multi-language blog, but I never intended to translate every content, so it was always a place where some articles would be in English, some in Italian, and the general pages would be actually multi-language.
This posed a problem about what to show on the front page:
- If every node was shown, there would be duplicates for translated nodes, which can be confusing.
- If only nodes in the current interface language were shown, the front page would list completely different content across languages, which does not represent the timeline of the blog content.
So a criterion for a front page of a partially multi-lingual site could be something like the following:
- If a node has a translation in the current interface language, show that;
- if not, show the original translation.
The "Select translation" module
In Drupal 6 I used the Select translation module which worked fine, but It was not available for Drupal 8.
So I asked the maintainers if they could give me the permission to commit changes to the git repository and I started working on the port myself.
The major problem I had to deal with was that Drupal 6 approached the multi-language problem using by default the mechanism called "Content translations" where separate nodes represented different translations (i.e. different rows in the
node table each with its own
nid), tied together by a
tid field (translation id): different nodes with the same tid are translations of the same content.
Drupal 8 instead works with "Entity translations", so one single node represents all of its translations and is listed only once in the
node table, and actual translations are handled at the entity field level in the
node_filed_data table.
So the SQL query in Select translation needed to be adjusted to work on the
node_filed_data rather than of the
node table, as it can be seen in commit 12f70c9bb37c.
While at it I also took the chance to refactor and clean up the code, adding a drush command to test the functionality from the command line.
The code looks better structured thanks to the Plugin infrastructure and now I trust it a little more.
Preserve language
On ao2.it I also played with the conceptual difference between the "Interface language" and the "Content language" but Drupal 6 did not have a clean mechanism to differentiate between the two.
So I used the Preserve language module to be able to only switch the interface language when the language prefix in the URL changed.
It turns out that an external module is not needed anymore for that because in Drupal 8 there can be separate language switchers, one for the interface language and one for the content language.
However there are still some issues about the interaction between them, like reported in Issue #2864055: LanguageNegotiationContentEntity: don't break interface language switcher links, feel free to take a look and comment on possible solutions.
More details about the content language selection in a future blog post.
After implementing some larger enterprise Drupal 8 websites, I would like to share some insights, how to solve common issues in the deployment workflow with Drupal 8 CMI.
Introduction to Drupal CMI
First of all, you need to understand, how the configuration management in Drupal 8 works. CMI allows you to export all configurations and its dependencies from the database into yml text files. To make sure, you never end up in an inconsistent state, CMI always exports everything. By default, you cannot exclude certain configurations.
If you change some configuration on the live database, these configurations will be reverted in the next deployment when you use
drush config-import
This is helpful and will make sure, you have the same configuration on all your systems.
How can I have different configurations on local / stage / live environments?
Sometimes, you want to have different configurations on your environments. For example, we have installed a "devel" module only on our local environment but we want to have it disabled on the live environment.
This can be achieved by using the configuration split module: https://www.drupal.org/project/config_split
What does Configuration Split?
This module slightly modifies the CMI by implementing a Config Filter (https://www.drupal.org/project/config_filter). Importing and exporting works the same way as before, except some configuration is read from and written to different directories. Importing configuration still removes configuration not present in the files. Thus, the robustness and predictability of the configuration management remains. And the best thing is: You still can use the same drush commands if you have at least Drush 8.1.10 installed.
Configuration Split Example / Installation Guide
Install config_split using composer. You need need at least "8.x-1.0-beta4" and > drush 8.1.10 for this guide.
composer require drupal/config_split "^1.0"
Enable config_split and navigate to "admin/config/development/configuration/config-split"
drush en config_split -y
Optional: Installing the chosen module will make the selection of blacklists / greylists way more easier. You can enable chosen only on admin pages.
composer require drupal/chosen "^1.0"
I recommend you to create an "environments" subfolder in your config folder. Inside this folder you will have a separate directory for every environment:
Now you can configure your environments:
The most important thing is, that you set every environment to "Inactive". We will later activate them according to the environment via settings.php
Here is my example where I enable the devel module on local:
Activate the environments via settings.php
This is the most important part of the whole setup up. Normally, we never commit the settings.php into git. But we have a [environment]-settings.php in git for every environment:
settings.php (not in git) variables-dev.php (in git and included in the settings.php of dev) variables-live.php (in git and included in the settings.php of live) settings.local.php (in git and included locally)
You need to add the following line to the variables-[environment].php. Please change the variable name according to your environment machine name:
// This enables the config_split module $config['config_split.config_split.dev']['status'] = TRUE;
If you have done everything correctly and cleared the cache you will see "active (overriden)" in the config_split overview next to the current environment.
Now you can continue using
drush config-import -y drush config-export -y
and config_split will do the magic.
How can I exclude certain Config Files and prevent them to be overridden / deleted on my live environment?
The most prominent candidates for this workflow are webforms and contact forms. In Drupal 7, webforms are nodes and you were able to give your CMS administrator the opportunity to create their own forms.
In Drupal 8 webforms are config entities, which means that they will be deleted while deploying if the yml files are not in git.
After testing a lot of different modules / drush scripts, I finally came up with an easy to use workflow to solve this issue and give CMS administrators the possibility to create webforms without git knowledge:
Set up an "Excluded" environment
First of all, we need an "excluded" environment. I created a subfolder in my config-folder and added a .htaccess file to protect the content. You can copy the .htaccess from an existing environment, if you are lazy. Don't forget to deploy this folder to your live system before you do the next steps.
Now you can exclude some config files to be excluded / grey-listed on your live environment:
webform.webform.* contact.form.*
Set the excluded environment to "Inactive". We will later enable it on the live / dev environment via settings.php.
Enable "excluded" environment and adapt deployment workflow
We enable the "excluded" environment on the live system via variables-live.php (see above):
// This will allow module config per environment and exclude webforms from being overridden $config['config_split.config_split.excluded']['status'] = TRUE;
In your deployment workflow / script you need to add the following line before you do a drush config-import:
#execute some drush commands echo "-----------------------------------------------------------" echo "Exporting excluded config" drush @live config-split-export -y excluded echo "-----------------------------------------------------------" echo "Importing configuration" drush @live config-import -y
The drush command "drush @live config-split-export -y excluded" will export all webforms and contact forms created by your CMS administrators into the folder "excluded". The "drush config-import" command will therefore not delete them and your administrators can happily create their custom forms.
Benefit of disable "excluded" on local environment
We usually disable the "excluded" environment on our local environment. This allows us to create complex webforms on our local machine for our clients and deploy them as usual. In the end you can have a mix of customer created webforms and your own webforms which is quite helpful.
Final note
The CMI is a great tool and I would like to thank the maintainers of the config_split module for their great extension. This is a huge step forward making Drupal 8 a real Enterprise CMS Tool.
If you have any questions, don't hesitate to post a comment.
The twelfth issue of 2016 is now available! This month we look at how to write good tests with Behat and using Test Driven Development. This issue also includes articles on using HTTPlug to decouple your HTTP Client, Decoupled Blocks with Drupal and JavaScript. Our columnists have articles on writing a Chat bot, advice on securing your application's secrets, making better bug reports, respecting diversity, and a look back at 2016.
Download your issue and read a FREE article today.Oscar still remembers downloading an early version of the Apache HTTP server at the end of 1995, and promptly asking "Ok, what's this good for?" He started learning PHP in 2000 and hasn't stopped since. He's worked with Drupal, WordPress, Zend Framework, and bespoke PHP, to name a few. Follow him on Google+.
There, there, no need to get yourself stressed out over all the new Javascript libraries trying to lure you with their irresistible features. It's impossible even to test all of them!
Why should you allow this whole array of equally tempting choices to "sabotage" your efficiency by keeping you from creating new amazing websites and innovative web apps?
So, how can you keep your focus? By keeping a close eye on these 10 (lesser known) JavaScript libraries that we're convinced that will gain popularity and become the 10 most influential ones in 2017.
Find out what our predictions rely on!
1. Node.js
A bit sick and tired of hearing everyone in the web community keep talking about Node? No wonder it's one of their main topic:
- it's one of those JS libraries that keeps on growing and growing at a mind-blowing speed, no doubt about that
- it makes that reliable "boost", the ideal environment, for any developer to get his web development project started with
- it turns local packages management into a "child's play" in the command line
- it eases your unit testing (in Mocha.js) work
- it puts the Sails.js framework at your disposal for building your front-end interface
2. Riot.js
Now here's another JS library that will be wrapped in glory in 2017! Mostly front-end developers will get all excited around it!
Where will the excitement come from? Its helps you create powerful digital interface libraries!
But what makes it a strong alternative to React, one that you should even consider?
Here are just 3 answers to this legitimate question:
- the whole community of developers backing up Riot.js, that you get to rely on, will make getting the answers to your questions much more time-efficient
- its simple syntaxh makes it easier for you to control it while you're access DOM
- it makes the perfect choice for customizing the elements of your app
3. Keystone.js
We could say that Node.js "passes on the torch" to Keystone.js.
Once you've used all of Node.js' capabilities in your web development process, reach out for Keystone. It will empower your website/web app with a 100% JavaScript, full-scale CMS engine!
4. D3.js
What do you currently rely on for creating eye-pleasing visualizations of your data?
Whatever you're using, you should definitely let D3.js stir your curiosity.
It has no rival among the JavaScript visualization tools. It will help you add the modern edge to your graphs, dynamic visualizations and charts in no time.Give it a try! Don't let the trends in the big data industry pass you by, be the one who crafts the trends!
5. Create.js
On a constant look for the best toolkit to rely on when you create all your web animations and digital media "awesomeness"?
Well, you should consider Create.js for the role of "assistant" in your work. It's so much more than just "another JavaScript library": it's a whole collection of libraries in fact. Each one of these "sub-libraries" spoils you with certain features and help you target certain parts of your digital media projects, so that that you should pick the ones that specialize on what you want to achieve in 3D.
For instance, one library/feature will help you build custom animations for the web, while others will help you handle the HTML5 canvas elements. Got the idea?
6. Meteor.js
What's your future web development/web design project? And your second one? How about the third one in your schedule?
Well, learn that you can practically build all these platforms on Meteor! All of them, plus the ones that are still in the phase of ideas populating your imagination!
Being an open source project, it empowers you with unlimited freedom of creation and innovation. From chat apps, to social media platforms, from custom dashboards to social voting website you can build anything from the ground up on Meteor and React.
Unlimited possibilities? Who could say no to that?
It's true though that Meteor is for the skilled web developers, it's not one of the easiest JS libraries to learn! Therefore, expect to have your brain muscle challenged a bit before you get to play with its whole array of great features!
7. Vue.js
Are you in the Angular fans team or in the Ember addicts team?
Now what if I told you that a new "actor" will be stepping on the stage and stealing the spotlight: Vue.js?
For front-end developers it will be more than just "lucky no. 3". It's a MVVM front-end framework and it's Javacript (how else!)! Therefore, it steps away from the standard MCV architecture.Although learning it might turn out to be quite a challenge, don't let that discourage you: this is going to be the two typical front-end frameworks' (Amber and Ember) big "rival" in 2017, so you'd better be one step ahead of trends and start learning it now!
8. WebVR
How's your VR projects coming along?
It's still under testing (and being an open source you can just imagine the "army" of developers testing all its weaknesses, checking how it works on VR devices and in the latest browsers), but even so, dare and rely on our prediction: you want to keep an eye on it in 2017!
9. Chart.js
Name a type of chart that you need to integrate in your website/web app and we'll tell you that you can easily put it together with Chart.js.
Besides the cool data graphs that you can build, we've put this Javascript library on our list due to its other tempting features:
- it's so easy to customize
- it's easy peasy to set it up, too
- it comes already upgraded with great options for animations
- it's an open source, meaning that you gain access to helpful documentation, too!
10. Three.js
And here's a more than useful JavaScript resource whenever you feel the urge to pull off some:
- unbelievably realistic motion-sensitive backgrounds
- mind-blowing 3D effects
- amazing 3D web graphics
Don't you look forward to 2017 now, knowing what cool JavaScript libraries will get perfected and ready to help you enhance your full potential as a developer?
In this blog post I will present how, in a recent e-Commerce project built on top of Drupal7 (the former version of the Drupal CMS), we make Drupal7, SearchAPI and Commerce play together to efficiently retrieve grouped results from Solr in SearchAPI, with no indexed data duplication.
We used the SearchAPI and the FacetAPI modules to build a search index for products, so far so good: available products and product-variations can be searched and filtered also by using a set of pre-defined facets. In a subsequent request, a new need arose from our project owner: provide a list of products where the results should include, in addition to the product details, a picture of one of the available product variations, while keep the ability to apply facets on products for the listing. Furthermore, the product variation picture displayed in the list must also match the filter applied by the user: this with the aim of not confusing users, and to provide a better user experience.
An example use case here is simple: allow users to get the list of available products and be able to filter them by the color/size/etc field of the available product variations, while displaying a picture of the available variations, and not a sample picture.
For the sake of simplicity and consistency with Drupal's Commerce module terminology, I will use the term "Product" to refer to any product-variation, while the term "Model" will be used to refer to a product.
Solr Result Grouping
We decided to use Solr (the well-known, fast and efficient search engine built on top of the Apache Lucene library) as the backend of the eCommerce platform: the reason lies not only in its full-text search features, but also in the possibility to build a fast retrieval system for the huge number of products we were expecting to be available online.
To solve the request about the display of product models, facets and available products, I intended to use the feature offered by Solr called Result-Grouping as it seemed to be suitable for our case: Solr is able to return just a subset of results by grouping them given an "single value" field (previously indexed, of course). The Facets can then be configured to be computed from: the grouped set of results, the ungrouped items or just from the first result of each group.
Such handy feature of Solr can be used in combination with the SearchAPI module by installing the SearchAPI Grouping module. The module allows to return results grouped by a single-valued field, while keeping the building process of the facets on all the results matched by the query, this behavior is configurable.
That allowed us to:
- group the available products by the referenced model and return just one model;
- compute the attribute's facets on the entire collection of available products;
- reuse the data in the product index for multiple views based on different grouping settings.
Result Grouping in SearchAPI
Due to some limitations of the SearchAPI module and its query building components, such plan was not doable with the current configuration as it would require us to create a copy of the product index just to apply the specific Result Grouping feature for each view.
The reason is that the features implemented by the SearchAPI Grouping are implemented on top of the "Alterations and Processors" functions of SearchAPI. Those are a set of specific functions that can be configured and invoked both at indexing-time and at querying-time by the SearchAPI module. In particular Alterations allows to programmatically alter the contents sent to the underlying index, while the Processors code is executed when a search query is built, executed and the results returned.
Those functions can be defined and configured only per-index.
As visible in the following picture, the SearchAPI Grouping module configuration could be done solely in the Index configuration, but not per-query.
As the SearchAPI Grouping module is implemented as a SearchAPI Processor (as it needs to be able to alter the query sent to Solr and to handle the returned results), it would force us to create a new index for each different configuration of the result grouping.
Such limitation requires to introduce a lot of (useless) data duplication in the index, with a consequent decrease of performance when products are saved and later indexed in multiple indexes.
In particular, the duplication is more evident as the changes performed by the Processor are merely an alteration of:
- the query sent to Solr;
- the handling of the raw data returned by Solr.
This shows that there would be no need to index multiple times the same data.
Since the the possibility to define per-query processor sounded really promising and such feature could be used extensively in the same project, a new module has been implemented and published on Drupal.org: the SearchAPI Extended Processors module. (thanks to SearchAPI's maintainer, DrunkenMonkey, for the help and review :) ).
The Drupal SearchAPI Extended Processor
The new module allows to extend the standard SearchAPI behavior for Processors and lets admins configure the execution of SearchAPI Processors per query and not only per-index.
By using the new module, any index can now be used with multiple and different Processors configurations, no new indexes are needed, thus avoiding data duplication.
The new configuration is exposed, as visible in the following picture, while editing a SearchAPI view under "Advanced > Query options".
The SearchAPI processors can be altered and re-defined for the given view, a checkbox allows to completely override the current index setting rather than providing additional processors.
Conclusion: the new SearchAPI Extended Processors module has now been used for a few months in a complex eCommerce project at Liip and allowed us to easily implement new search features without the need to create multiple and separated indexes.
We are able to index Products data in one single (and compact) Solr index, and use it with different grouping strategies to build both product listings, model listings and model-category navigation pages without duplicating any data.
Since all those listings leverages the Solr FilterQuery query parameter to filter the correct set of products to be displayed, Solr can make use of its internal set of caches and specifically the filterCache to speed up subsequent searches and facets. This aspect, in addition to the usage of only one index, allows caches to be shared among multiple listings, and that would not be possible if separate indexes were used.
For further information, questions or curiosity drop me a line, I will be happy to help you configuring Drupal SearchAPI and Solr for your needs.
Development started on Drupal 8 features back in March of 2011. Since then, the developer and application framework world has looked forward to the outcomes of every development, feature completion, clean-up, API completion, beta, and release candidate (RC) phase with baited breath. In November of 2015, Drupal 8.0.0 was released. Sighs of relief turned to curious murmers—what's this all about?
Drupal 8 takes an already terrific content management framework to ever greater heights for users, administrators, and developers. There's a seriously sharp focus on user-friendliness, but content presentation, new ways to create data structures, build APIs, multilingual capabilities, and the delivery of mobile accessibility out of the box? Drupal 8 brings those to the table too.
Looking for help with Drupal 8?
There are 16 Drupal 8 features worth knowing.
While Symfony 2 powers the Drupal 8 backend, a lighter and faster core offers tons more capabilities for modules and themes. Plus, the Drupal 8 migration and the onward curve is significantly reduced. These changes and more are key reasons to consider that switch to Drupal 8. But I'm getting ahead of myself, here are the 16 top Drupal 8 features:
1. New Theme Engine
Drupal 8 includes a brand new theming engine called Twig, which is PHP-based, flexible, fast, and secure. It's much easier to create beautiful and more functional Drupal websites using Twig, as its templates are written in a syntax that's less complex than a PHP template or others while being more secure.
2. Mobile First From The Get-Go
Drupal 8 is mobile first in its approach. All the built-in themes that come with Drupal 8 are responsive, along with an admin theme that adapts to different screen sizes, and a 'Back To Site' button to go back to the front page. Tables fit into any screen size without a hitch, and the new admin toolbar works well on mobile devices.
3. More HTML5 Power to You
HTML5 is now more or less the de facto standard when it comes to writing web markup. The same is now available natively in Drupal 8, giving you access to input fields like date, e-mail, phone, etc., and even more functionality and compatibility with mobile and handheld devices.
4. Multilingual Ready
Drupal 8 boasts extensive multilingual features right out of the box. The admin interface has built-in translations. You can also create pages with language-based Views filtering and block visibility. Translation updates from the community are automatically facilitated.
5. Manage Your Configuration
Drupal 8 has configuration management built into it at the file-system level so that carrying over configuration elements (like content type, views, or fields, etc.) from local development to the server is a breeze. You can use a version-control system to keep track of configuration changes. Configuration data is stored in files, separate from the site database(s).
6. Easy Authoring
New Drupal 8 features bring unprecedented power into the hands of the Content Editor, with WYSIWYG editor CKEditor now bundled with the core. However, the most touted improvement remains the in-place editing capability that Drupal 8 will afford users, a result of the Spark Initiative.
Site and content creators or editors can edit text on any page without having to switch to the full edit form. Drafts are now much easier to create, and web security is now better implemented as a result.
7. Quick Edits
There's something great about seeing something that needs changing and having the ease of access to change it—directly and quickly. Now Quick Edit is a backport of the Drupal 8 in-place editing for Fields. So if you're logged into Drupal content is in front of you, edit the text directly for quick fixes and additions from the front-end.
8. Views Now Part of Core
Views sit high up in the Drupal module hierarchy, as it is an integral part of most website projects, and a lot is pretty much impossible without it. Site designers have used use this hitherto-contributed module to output galleries, maps, graphs, lists, posts, tables, menus, blocks, reports, and what-have-you.
With this Drupal 8 feature, Views is part of and firmly integrated with the core. The front page and several administration pages are now Views, and users will now be able to quickly create pages, blocks, admin sections, etc., and modify existing ones just as effortlessly.
9. Better Support for Accessibility
Drupal 8 has excellent support for industry standard accessibility technologies, like WAI-ARIA. ARIA Live Announcements API and TabManager are significant improvements in Drupal 8, which provide control for rich Internet applications. Bells and whistles like better font sizes, tweaked color contrasts, jQuery UI's autocomplete, and modal dialogs go a long way towards making Drupal 8 a breeze to use.
10. Web Services Built-in
Drupal 8 now makes it possible to use itself as a data source, and output content as JSON or XML. You can even post data back to Drupal 8 from the front end. Hypertext Application Language (HAL) is implemented in Drupal 8 and makes exploitation of web service capabilities less painful.
11. Fields Galore
Drupal 8 ships with bucket-loads of field types in the core, thus taking its content structure capabilities up a notch. New field types like entity reference, link, date, e-mail, telephone, etc., aid content creation, and now you can attach fields to more content types, as well as create custom contact forms by attaching fields to them.
12. Guided Tour
Now the descriptive text is right under the help link. Users can click and then take the tour; pop-ups appear, explaining how this all works, one of the most helpful Drupal 8 features to newcomers. This user-friendly boost is well-received as it's making the CMS easier for everyone to understand.
13. Loading Speed
14. Industry Standards
Drupal 8 aligns with the latest PHP 7 standards like PSR-4, namespaces, and traits, and uses top notch, outstanding external libraries like Composer, PHPUnit, Guzzle, Zend Feed Component, Assetic to name a few. Meanwhile, underlying Drupal 8 features modern, object-oriented code that's the order of the day, by Symfony 2.
15. JavaScript Automated Testing
Automated testing is not possible for front-end, so JaveScript (JS) automated testing is now possible with Drupal 8.1. Now QA'ers can test the JavaScript front-end automatically, saving time and making continuous integration that much easier.
16. Big Pipe in Core
With Big Pipe part of Drupal core, developers can optimize the site load performance for the end-user significantly. While this feature has nothing to with actual performance and is only perceived, it's a great feature to have since the end user is able to see a difference in site load times.
Enough Drupal 8 features to think about?
These 16 Drupal 8 features are some of the most important reasons that this upgrade is so worth celebrating; it's the collective work of over 3,000 contributors. But more importantly to you, this might be that big, bright answer you've been searching for.Got Drupal 8 your mind?
More Drupal 8 resources:
This article was originally published in July, 2014. It has since been updated.
I feel really excited to have cleared the mid-Term requirement for my project in Google Summer of Code (GSoC). The results of the mid-Term evaluations were announced June 28, 00:30 IST. This was the evaluation for the first phase of GSoC. In this evaluation process, set up by GSoC organisers, students and mentors have to share their feedback about the current progress of the project. Mentors need to give a pass/ fail grade. Students can continue coding once they clear the evaluations successfully.
I have been working on Porting Search Configuration module to Drupal 8. Please go through my previous posts if you would like to have a look into the past activities in this port process.
Last week I worked on testing some of the units of this module using the Php unit tests framework. Testing is an important process when it comes to any software development process. It plays a crucial role for any software. It helps us to understand the improve our software to the required level by making use of various test cases. We input various values and check whether the tests are passed according to the requirement. If any condition fails to our expectations, we need to make the required changes to suit the application needs.
Php unit tests are generally used to test some units of an application. To check whether the functions implemented gives the expected output, behaviour of the functions in various test cases, giving different types of arguments as inputs to check the errors or flaws for improving the application.
We need to install the Php unit for this process. You could follow this documentation for this process. Furthermore, they give a detailed analysis of the Php Unit Tests.
Once the installation is completed, we can start writing the unit tests for the functionalities we have implemented. The tests are generally stored in the tests/src/Unit directory of the module. The name of the unit test file will be of the format xyzTest.php. All tests are suffixed by 'Test'. 'xyz' can be replaced by the functionality you are going to test.
The following is a simple test to check the sum of two numbers: sumTest.php
<?php class SampleTest extends PHPUnit_Framework_TestCase { public function testSum() { $this->assertEquals(2+2, 4); } } ?>
As mentioned in this above code snippet, we need to create a class, with class name suffixed by 'Test' which is an extension of PHPUnit_Framework_TestCase. Now, we need to write the tests inside as member functions. The functions starting with the name test are executed. Here we are checking the sum of the two numbers. This is a very simple demonstration.
The tests are run by using the command PHPUnit. i.e,
$ phpunit tests/src/Unit/sumTest.php
The output generated on running the above test is:
PHPUnit 5.4.6 by Sebastian Bergmann and contributors.
. 1 / 1 (100%)
Time: 252 ms, Memory: 13.25MB
OK (1 test, 1 assertion)
Stay tuned for future updates on this module port.
This is part 2 of a series on using XHProf for profiling Drupal modules.
After you've installed XHProf, what's next? How can you make use of its recommendations to tune a module or a website? Unfortunately there are no hard-and-fast rules for optimizing code to run more efficiently. What I can offer here is my own experience trying to optimize a D8 module using XHProf.
Understanding an XHProf run report
The XHProf GUI displays the result of a given profiler run, or a group of runs. It can even compare 2 runs, but we'll get to that in a minute. If you followed my previous post, you should have the
xhprof_html directory symlinked into the root web directory for your Drupal site; so visiting
<my-local-site>/xhprof/ should give you a list of all available stored run IDs, and you can click through one of those to view a specific run report.
You can also go directly to a specific run report via the URL
<my-local-site>/xhprof/index.php?run=<run-id>&source=<source-id> (which you should have been logging already via an
echo statement or
dblog if you followed the last post).
The core of the run report is a table of each function or method which your code called while it was being profiled, along with a set of statistics about that function. This allows you to understand which parts of your code are most resource-intensive, and which are being called frequently in the use case that you're profiling. Clicking on any one of the column headers will sort the list by that metric. To understand this report, it's helpful to have some terminology:
- Incl. Wall Time - The total clock time elapsed between when a function call started and when the function exited. Note that this number is not a great basis for comparisons, since it can include other processes which were taking up CPU time on your machine while the PHP code was running, from streaming music in the background to PHPStorm's code indexing, to random web browsing.
- Incl. CPU Time - In contrast to wall time, CPU time tracks only the time which the CPU actually spent executing your code (or related system calls). This is a more reliable metric for comparing different runs.
- Excl. Wall/CPU Time - Exclusive time measurements only count time actually spent within the given method itself. They exclude the time spent in any method/function called from the given function (since that time will be tracked separately).
In general, the inclusive metrics (for CPU time and memory usage) will give you a sense of what your expensive methods/functions are – these are the methods or functions that you should avoid calling if possible. In contrast, the exclusive metrics will tell you where you can potentially improve the way a given method/function is implemented. For methods which belong to Drupal Core or other contrib modules, inclusive and exclusive metrics are basically equivalent, since you don't usually have the option of impacting the implementation details of a function unless you're working on its code directly. Note also that because your overall parent method and any other high-level methods in your code will always show up at the top of the inclusive time chart, you may have better luck really understanding where your performance hits come from by sorting by exclusive CPU time.
Take a step back and plan your test scenarios
Before digging in to optimizing your module code, you need to take a step back and think about the big picture. First, what are you optimizing for? Many optimizations involve a tradeoff between time and memory usage. Are you trying to reduce overall run-time at the expense of more memory? Is keeping the memory footprint of a given function down more important? In order to answer these questions you need to think about the overall context in which your code is running. In my case, I was optimizing a background import module which was run via cron, so the top priority was that the memory usage and number of database optimizations were low enough not to impact the user-facing site performance.
Second, what use case for your code are you profiling? If this is a single method call, what arguments will be passed? If you're profiling page loads on a website, which pages are you profiling? In order to successfully track whether the changes you're making are having an impact on the metrics you're concerned about, you need to be able to narrow down the possible use cases for your code into a handful of most-likely real world scenarios which you'll actually choose to track via the profiler.
Keep things organized
Now it's time to get organized. Write a simple test script so that you can easily run through all your use cases in an automated way – this is not strictly necessary, but it will save you a lot of work and potential error as you move through the process. In my case, I was testing a drush command hook, so I just wrote a bash shell script which executed the command three times in each of two different ways. For profiling page loads, I would recommend using Apache JMeter - and you'll need to consider whether you want to force an uncached page load by passing a random dummy query parameter. Ideally, you should be running each scenario a few times so that you can then average the results to account for any small variations in run-time.
Keeping your different runs organized is probably the most important part of successfully profiling module code using XHProf! Each run has a unique run ID, but you are solely responsible for knowing which use case scenario and which version of the codebase that run ID corresponds to. I set up a basic spreadsheet in OpenOffice where I could paste in run numbers and basic run stats to compare (but there's almost certainly a nicer automated way to do this than what I used).
Once you have a set of run IDs for a given use case + codebase version, you can generate a composite xhprof report using the query string syntax
http://<your-local-site>/xhprof/index.php?run=<first-run-id>,<second-run-id>,<third-run-id>&source=<source-string> Averaging out across a few runs should give you more precise measurements for CPU time and memory usage, but beware that if parts of your code involve caching you may want to either throw out the first run's results in each version of the code base, since that's where the cache will be generated, or clear the cache between runs.
Go ahead and test your run scripts to make sure that you can get a consistent baseline result at this point – if you're seeing large differences in average total CPU times or average memory usage across different runs of the same codebase, you likely won't be able to compare run times across different versions of the code.
Actually getting to work!
After all this set-up, you should be ready to experiment and see what the impact of changes in your code base are on the metrics that you want to shift. In my case, the code I was working on used a streaming JSON parser class, and I noticed that one of the top function calls in the inital profiler report was the
consumeChar method of the parser.
It turns out that the JSON files I was importing were pretty-printed, thus containing more whitespace than they needed to. Sine the
consumeChar method gets called on each incoming character of the input stream, that added up to a lot of unnecessary method calls in the original code. By tweaking the JSON file export code to remove the pretty print flag, I cut down the number of times this method was called from 729,099 to 499,809, saving .2 seconds of run time right off the bat.
That was the major place where the XHProf profiler report gave me insights I would not have had otherwise. The rest of my optimizing experience was mostly testing out some of the common-sense optimizations I had already thought of while looking at the code – caching a table of known Entity IDs rather than querying the DB to check if an entity existed each time, using an associative array and
is_empty() to replace
in_array() calls, cutting down on unnecessary
$entity->save() operations where possible.
It's useful to mention that across the board the biggest performance hit in your Drupal code will probably be database calls, so cutting down on those wherever possible will save run-time (sometimes at the expense of memory, if you're caching large amounts of data). Remember, also, that if DB log is enabled each logging call is a separate database operation, so use the log sparingly – or just log to
syslog and use service like Papertrail or Loggly on production sites.
The final results
As the results below show, using XHProf and some thoughtful optimizations I was able to cut total run time significantly in one use case (Case 2) and slightly in another use case (Case 1). Case 1 was already running in a reasonable amount of time, so here I was mostly interested in the Case 2 numbers (assuming I didn't make anything too much worse).
Think of the big picture, part 2
Remember that controlled experimental metrics are just a means to understanding and improving real-world performance (which you can also measure directly using tools like blackfire, but that's another blog post). In my case, at the end of the day we decided that the most important thing was to ensure that there wasn't a performance impact on the production site while this background import code was running; so one of the optimizations we actually ended up implementing was to force this code to run slower by throttling
$entity->save() operations to maximally 1 every 1/2 second or so, as a way to minimize the number of requests MySQL was having to respond to from the importer. XHProf is a powerful tool, but don't lose the forest for the trees when it comes to optimization.
Google summer of code (GSoC) seems to be a venue for students to get in touch with new technologies and be a part of many interesting open source organisations. Thanks to google for co- ordinating this initiative.
The last week was really a productive one for me in all aspects. I could manage time better to focus more towards my project. The climate here seems to have improved a lot. It's now rainy here which has reduced the hot and humid climate to a large extent. My geographical location, Kerala, the southern part of India usually faces a fair climate.
If you are searching for a flashback of my previous GSoC' 16 ventures, please have a look at these posts.
So, as you were expecting, now let's talk about my activities in the second week of GSoC. The second week commenced with a bit more elaborative planning of the tasks to be carried out in the coming days. My main intention for the week was to discover more Drupal hooks and adapt it to my project code.
Wondering, what are hooks?
Hooks, in the simple definition, are PHP functions which have an inbuilt meaning given by Drupal to interact with modules. They also give us the freedom to extend the functionalities. The api.drupal.org gives wonderful explanations about the various hooks in action and their modifications that have come in the different Drupal versions.
Topics I have covered:
I would like to take this opportunity to share with you some of the concepts I could grasp from the previous week of GSoC.
- hook_install
- This performs the setup tasks when the module is installed.
- hook_schema
- This hooks the database schema for the module. This is invoked once the module is enabled. This resides in the .install file of the module.
- hook_theme
- This is for enhancing the module's theme implementations.
- hook_permission
- This hook defines the user permissions of the module; granting and restricting permission to the roles.
- Configuration API
- The Drupal variables are replaced by the configuration API. You need to define the properties and default values in the new format.
Hoping to learn more Drupal concepts in the days ahead. I will be posting the updates regularly. Stay tuned for more Drupal concepts.
XHProf is a profiling tool for PHP code – it tracks the amount of time and memory your code spends in each function call, allowing you to spot bottlenecks in the code and identify where it's worth spending resources on optimization. There are have been a number of PHP profilers over the years, and XDebug has a profiler as well, but XHProf is the first one I've successfully managed to configure correctly and interpret the output of.
I had run across a number of blog posts about using XHProf + Drupal, but never actually got it to work sucessfully for a project. Because so much of the documentation online is incomplete or out-of-date, I thought it would be useful to document my process using XHProf to profile a Drupal 8 custom module here. YMMV, but please post your thoughts/experiences in the comments!
How to find documentation
I find the php.net XHProf manual entry super-confusing and circular. Part of the problem is that Facebook's original documentation for the library has since been removed from the internet and is only accessible via the WayBack Machine.
If there's only one thing you take away from this blog post, let it be: read and bookmark the WayBack machine view of the original XHProf documentation, which is at http://web.archive.org/web/20110514095512/http://mirror.facebook.net/facebook/xhprof/doc.html.
Install XHProf in a VM
If you're not running DrupalVM, you'll need to install XHProf manually via PECL. On DrupalVM, XHProf is already installed and you can skip to the next step.
sudo pecl install xhprof-beta
Note that all these commands are for Ubuntu flavors of linux. If you're on Red Hat / CentOS you'll want to use the
yum equivalents. I had to first install the
php5-dev package to get PECL working properly:
sudo apt-get update
sudo apt-get install php5-dev
And, if you want to view nice callgraph trees like the one below you'll need to install the graphviz package
sudo apt-get install graphviz
Configure PHP to run XHProf
You need to tell PHP to enable the xhprof extension via your php.ini files. Usually these are in
/etc/php5/apache2/php.ini and
/etc/php5/cli/php.ini. Add the following lines to the bottom of each file if they're not there already. You will also need to create the
/var/tmp/xhprof directory if it doesn't already exist.
; directory used by default implementation of the iXHProfRuns
; interface (namely, the XHProfRuns_Default class) for storing
; XHProf runs.
Lastly, restart Apache so that the PHP config changes take effect.
Set up a path to view the XHProf GUI
The XHProf GUI runs off a set of HTML files in the
xhprof_html directory. If you've been following the install steps above, you should be able to find that directory at
/usr/share/php/xhprof_html. Now you need to set up your virtual host configuration to serve the files in the
xhprof_html directory.
I find the easiest way to do this is just to symlink the
xhprof_html directory into the existing webroot of whatever site you're working on locally, for example:
ln -s /usr/share/php/xhprof_html /var/www/my-website-dir/xhprof
If you're using DrupalVM, a separate vhost configuration will already be set up for XHProf, and the default URL is
http://xhprof.drupalvm.dev/ although it can be changed in your
config.yml file.
Hooking XHProf into your module code
Generally, the process of profiling a chunk of code using XHProf goes as follows:
- Call
- Run the code you want profiled
- Once the code has finished running, call
xhprof_disable(). That function will return the profiler data, which you can either display to the screen (not recommended), or…
- Store the profiler data to a file by creating a new
XHProfRuns_Default();object and calling its
In the case below, I'm profiling a module that implements a few Drush commands from the command line which I'd like to optimize. So I created
_modulename_xhprof_enable() and
_modulename_xhprof_disable() functions – the names don't matter here – and then added a
--profile flag to my Drush command options which, when it is set to true, calls my custom enable/disable functions before and after the Drush command runs.
Here's what those look like in full:
* Helper function to enable xhprof.
function _mymodule_enable_xhprof() {
if (function_exists('xhprof_enable')) {
// Tell XHProf to track both CPU time and memory usage
xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY,
// Don't treat these functions as separate function callss
// in the results.
'ignored_functions' => array('call_user_func',
* Helper function to disable xhprof and save logs.
function _mymodule_disable_xhprof() {
if (function_exists('xhprof_enable')) {
$xhprof_data = xhprof_disable();
// Saving the XHProf run
// using the default implementation of iXHProfRuns.
include_once "/usr/share/php/xhprof_lib/utils/xhprof_lib.php";
include_once "/usr/share/php/xhprof_lib/utils/xhprof_runs.php";
$xhprof_runs = new XHProfRuns_Default();
// Save the run under a namespace "xhprof_foo".
// By default save_run() will automatically generate a unique
// run id for you. [You can override that behavior by passing
// a run id (optional arg) to the save_run() method instead.]
// .
$run_id = $xhprof_runs->save_run($xhprof_data, 'xhprof_mymodule');
echo "---------------\nAssuming you have set up the http based UI for \nXHProf at some address, you can view run at \nhttp://mywebsiteurl.dev/xhprof/index.php?run=$run_id&source=xhprof_mymodule\n---------------\n";
echo command here works fine for a Drush command, but for other tasks you could log the run url using watchdog.
Note: Another way to run XHProf on a Drupal site is using the XHProf module, but I haven't had great luck with that.
Viewing profiler results
If everything is configured correctly, when you run your module you should get a run ID output either to the screen (via
echo, as above, or however you've configured this logging). Visit the URL you configured above for xhprof, and you should see a list of all the stored runs. Clicking on a run will bring up the full profiler report.
Now what?
Now you've got all this data – how to make sense of it? What to do with it? Stay tuned for more discussion of how to interpret XHProf results and a real-world example of profiling a D8 module, next week!
Every expert from every field has his or her own tools of the trade and web development is no different – web development tools are one of the most important factors to every project and these tools could mean the difference between finishing your project on time or passing multiple deadlines. Luckily enough, plenty of top web development tools are available for just about every platform. Here is our list of OS X apps which you should include in your web development tools box:
Browsers for web development
One of the most important tools for web development is a browser – you'll need to view your website after you created it right? But it's not so simple – you will need to test your website for many browser versions, including beta and nightly builds. With that taken into consideration, you'll most likely need Opera Next, Chromium, Chrome Canary, Firefox Aurora, and Firefox Nightly.
Koala, apart from its cute name, is another nifty web development tool which is used to compile CoffeeScript, Compass, Sass, and LESS into a browser-compliant format. If you're using any of these techs, Koala is definitely a must.
Anvil for web development
Anvil is an interesting free app which allows web developers to create .dev domains. You'll see it as a new icon in the OS X menu bar and you can turn it on or off, create domains or add your projects to it. If you're looking for a tiny web development tool to manage or work on your local websites with, Anvil is perfect.
When handling multiple files, you need a reliable compression service to help you along. Most web developers may use ortar, rar or zip but Mac has its own built-in feature to extract and manage these formats – this service is rather limited so you're better off with Keka. Keka is a free open source OS X app for file compression and extraction.
Keka supports zip, rar, ortar, ISO, DMG, Tar, Gzip, 7z and it can extract to PAX, CAB, EXE and RAR formats. Keka allows you to split compressed files into different parts of specified sizes, apply password restrictions or automatically delete source files after extraction or compression.
Web development & Web Sharing
Since Apple removed the Web Sharing option from the OS X system preference, you might want to install the Web Sharing plugin. This little plugin will allow you to host local websites under the local host address. Once you install it you'll be able to see a new panel in System Preferences – here you'll be able to switch web sharing off or on.
File compression, One of The Very Top Web Development Tools for Mac OS X
We talked about Keka before but there are two ways you can use it to compress files. You can either download this small web development tool and install the services contextual menu or you can drag and drop your files directly into the Keka app window.
Sequel Pro
MySQL is one of the most popular databases in the world – plenty of websites rely on MySQL for storing and managing databases. If you are using OS X, Sequel Pro is a must-have for web development. It's a native OS X app built in Cocoa and it also features a nice and friendly GUI. With Sequel Pro you can create, filter databases, import, export, remove, create users and execute MySQL queries.
The iPhone Emulator
All web developers need to take mobile users into account when building their websites, hence the need for a web development tool that can be used to test websites for specific platforms. Devs can use physical tablets or phones or they can go for the easier option – a simulator like iPhone Simulator. All you need to do is just install Xcode from the AppStore and you're done – you have another web development tool in your little toolbox.
If you're looking to use icons, Icons8 is one of the top web development tools that you can use. Just install Icons8 and you'll be able to search through its over 2800 collections of icons. Once you find something you like, you'll be able to copy it to Finder, Xcode or Photoshop.
Github and SourceTree
Even if you're building a small website or a presentation website, you'll need a control version system to track your revisions and changes to the project's code. Github is the most popular service for version control and you can install it on OS X as well. SourceTree is another mentionable service – both of these services are the backbone of any respectable web development agency.
Poedit is the best web development tool you can use for translating websites into multiple languages. Not only that but you can translate plugins and Wordpress Themes as well with this little app.
Automator is a web development tool that's pre-installed with OS X. This little app allows users to automate tasks such as cropping multiple images at once, creating thumbnail images, changing file extensions and renaming files in batch.
Dash collects documentation for a lot of the popular programming languages, including LESS, Sass, jQUery, CSS3 and HTML5. Users can search through code snippets, functions, syntax, and other valuable information.
PHP has been the fourth most popular coding language in the world for seven years straight. More than 200 million websites and around 82% of public websites are powered by PHP.
PHP 7 Released
PHP took a huge leap forward with the release of the first major update since 2004. PHP 7 boasts massively improved performance – up to three times better performance than PHP 5.6 when running WordPress.
PHP 7 boasts other changes as well - return type declarations, reduced memory usage, the spaceship operator and many more.
PHP also features some breaking changes such as the removal of magic quotes and the PHP safe mode as well as a number of new reserved keywords.
Web apps such as WordPress needed a few touches in order to be able to support PHP 7 but now it's fully compatible with the new programming language.
While PHP 7 has been released very recently, it may take a few years until it's widely adopted by the majority of websites. Most applications tend to take a few years to adapt to upgrades but we hope that the increased performance will make these upgrades come sooner.
PHP 7 comes with a new and improved version of the Zend Engine, featuring numerous perks such as:
General performance: The new PHP 7 is up to three times as fast as the older version
Zero cost asserts
Anonymous Classes
Return and Scalar Type Declaration
The null coalescing operator
Removal of old and unsupported SAPIs and extensions
Secure random number generator
Many fatal errors converted to Exceptions
Improved Exception hierarchy
Consistent 64 bit support
Abstract Syntax Tree
Memory usage is significantly reduced
In the previous article of this series we've started our dive into the Entity Validation and Typed Data APIs. We've seen how
DataType plugins interact with data definitions and how various constraints can be added to the latter at multiple levels and extension points.
In this part, we will cover the aspect of actual validation and violation handling. In addition, we will write our own constraint and validator so that we can use custom behaviors in the data validation process.
Validation and Violation Handling
Even though we don't yet know exactly how constraints are built, we've seen how they can be added to Typed Data definitions, including entity fields. Let us now see how we can validate the entities and handle possible violations we find.
When talking about Typed Data we've already seen how the
validate() method can be called on the
DataType plugin instance which holds a data definition. When it comes to entities, this can happen both at entity and field levels.
For instance, we can validate the entire entity using the
validate() method:
$entity->set('title', 'this is too long of a title');
$violations = $entity->validate();
In our previous article, we added the
Length constraint to Node titles to prevent title strings longer than 5 characters. If that is still in place and we run the code above, the validation should obviously fail. The
$violations object is now, however, an
EntityConstraintViolationListInterface instance which provides some helper methods for accessing violation data specific to Drupal content entities. It's worth looking into that interface for all the helper methods available.
To get a list of Entity level violations we can use the
getEntityViolations() method but we can also loop through all of them. Once we have our individual
ConstraintViolationInterface instances, we can inspect them for what went wrong. For instance, we can get the error message with
getMessage(), the property path that failed with
getPropertyPath() and the invalid value with
getInvalidValue(), among other useful things.
When it comes to fields, the property path is in the following format:
title.0.value. This includes the field name, the key (delta) of the individual field item in the list and the actual property name. This represents the property path of our violation above.
Apart from calling validation on the entire entity (which may be superfluous at times), we can also do so directly on each field:
$entity->set('title', 'this is too long of a title');
$violations = $entity->get('title')->validate();
In this case,
$violations is again an instance of
ConstraintViolationListInterface and can be looped over to inspect each violation. This time, though, the property path changes to no longer include the field name:
And lastly, we can even validate the individual items in the list:
$violations = $entity->get('title')->get(0)->validate();
As we can expect, the difference now is that the property path will only show
value in the violation since we know exactly what we are validating: the first data definition in the list.
Constraints and Validators
We only touched upon the aspect of constraints and validators but let us better understand how they work by creating one ourselves. We will create one single constraint and validator but that can be used for both Node entities and their fields. It's always better to have constraints targeted to the data definition we want but for the sake of brevity, we'll do it all in one constraint to see how all these options could be handled.
The business case of our example is to have a constraint that we can apply to any string-based content entity field that would force the string to contain a certain alphanumeric code. The latter will be passed on as an option so it can be reused. If it's applied to a Node entity, we want to make sure the title of the node contains the code. So let's get started.
First, inside our
demo module, which can be found in this git repository, we need to create the
Validation folder inside the
Plugin folder of our namespace (the
src/ directory). Inside that, we need the
Constraint folder. This is because constraints are plugins expected to be defined in there.
Second, inside
Constraint/, we can create our constraint class:
namespace Drupal\demo\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\MissingOptionsException;
class HasCodeConstraint extends Constraint {
public $messageNoCode = 'The string <em>%string</em> does not contain the necessary code: %code.';
public $code;
public function __construct($options = NULL) {
if ($options !== NULL && is_string($options)) {
parent::__construct(['code' => $options]);
if ($options !== NULL && is_array($options) && isset($options['code'])) {
if ($this->code === NULL) {
throw new MissingOptionsException('The code option is required', __CLASS__);
As we mentioned earlier, the constraint plugin class is relatively simple. It has the expected annotation which, among boilerplate metadata, also specifies what type of data this constraint can be applied to. We chose both a simple string and the Node entity type. We then declare two public properties: a message to be used when the constraint fails (with placeholders being populated inside the validator) and the actual code to be checked for (populated by the constructor of the parent
Constraint class). Inside our constructor, we check if the options passed are a string or array (just to be a bit flexible) and throw an exception if the code parameter is not passed as an option in any shape or form.
One of the tasks of the parent
Constraint class is to specify which class will be used to validate this constraint. By default, it is a class named like the constraint itself but with
Validate appended at the end (
HasCodeConstraintValidator). If we wanted to create a differently named and/or namespaced class, we would have to override the
validatedBy() method and return the fully qualified name of the class we want.
Let's now see the
HasCodeConstraintValidator class since for us the default is fine:
namespace Drupal\demo\Plugin\Validation\Constraint;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\node\NodeInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;
class HasCodeConstraintValidator extends ConstraintValidator {
protected $context;
public function validate($data, Constraint $constraint) {
if (!$constraint instanceof HasCodeConstraint) {
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\HasCodeConstraint');
if ($data instanceof NodeInterface) {
$this->validateNodeEntity($data, $constraint);
if ($data instanceof FieldItemListInterface) {
foreach ($data as $key => $item) {
$violation = $this->validateString($item->value, $constraint);
if ($violation instanceof ConstraintViolationBuilderInterface) {
if (is_string($data)) {
$violation = $this->validateString($data, $constraint);
if ($violation instanceof ConstraintViolationBuilderInterface) {
protected function validateNodeEntity($node, $constraint) {
foreach ($node->title as $item) {
$violation = $this->validateString($item->value, $constraint);
if ($violation instanceof ConstraintViolationBuilderInterface) {
protected function validateString($string, $constraint) {
if (strpos($string, $constraint->code) === FALSE) {
return $this->context->buildViolation($constraint->messageNoCode, array('%string' => $string, '%code' => $constraint->code));
The main job of this class is to implement the
validate() method and build violations onto the current execution context if the data passed to it is somehow invalid. The latter can be more than one type of data, depending on what the constraint is applied to.
In our example, we use the constraint for entities, field item lists and primitive data as well. This is just to save us some space. But the logic is that if a Node entity is passed, the code is checked in its title while for the the field items an iteration is performed to check the values of the fields. And of course, the constraint can also be added to individual field items in which case the
$data variable would be the value of the data definition.
$context property has a handy
buildViolation() method which allows us to specify a number of things related to what actually failed (path, message, etc).
So if we want to make use of this constraint, we can apply what we learned earlier and do one of the following 3 things:
$node = $entity_types['node'];
$node->addConstraint('HasCode', ['code' => 'UPK']);
This adds the constraint to the Node entities so all node titles now have to contain the code
hook_entity_base_field_info_alter() or
hook_entity_bundle_field_info_alter(), one of the following two lines:
$title->addConstraint('HasCode', ['code' => 'UPK']);
$title->addPropertyConstraints('value', ['HasCode' => ['code' => 'UPK']]);
$title is a field definition and the first case adds the constraint to the entire list of items while the latter adds it straight to the individual item.
These three possibilities cover the three cases the constraint validator handles in our example. And that is pretty much it. Not too difficult.
In this article, we've continued our dive into the Entity Validation API by looking at two things: validation and violation handling, and the creation of custom constraints. We've seen that once applied onto entities or field data definitions, constraints can be very easily validated and inspected for violations. This API borrows a lot from Symfony and makes it a breeze to decouple validation from the Form API.
Also easy, as we've seen, is to extend the existing pool of validation criteria available. This is done via constraint plugins, all coupled with their own validators and which can be written in very reusable ways. It's very interesting to play around with these and see how all the pieces interact. And it is also a great learning experience.
Meet the authorDaniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
Data validation is a very important part of any application. Drupal 7 has a great Form API that can handle complex validation of submitted data, which can then be turned into entities. However, form level validation is problematic. For example, it becomes difficult to handle entity (data) validation programmatically. We have to either reimplement validation logic or mimic form submissions in code. This makes any data interaction dependent on the form system and this is a bad idea.
With the introduction of subsystems such as the REST API, Drupal 8 needed something better to handle this problem. The Symfony Validation component was chosen and Drupal 8 builds on top of it to tailor for the realties of the Typed Data and plugin based Entity system. So now, form submissions validate entities just like REST calls do or any other programmatic interaction with the entities easily can.
In this article, and its followup, we will explore the Drupal 8 Entity Validation API, see how it works, and how it can be extended. To better understand this, we will also take a look at the Typed Data API which underpins the entity system in Drupal 8. There will be some code examples that already exist in core but we will also write a bit of our own code which can be found in this git repository within the
demo module.
Typed Data
Typed Data is the Drupal 8 API that was built to provide a consistent way of interacting with data or metadata about the data itself. Why is this important for our topic? Because validation is defined and invoked on typed data objects.
Two important components of this API stand out: the data definition and the
DataType plugins. The role of the former is to define data and how interaction works with it (including things like settings or validation constraints). The role of the latter is to provide a way to get and set values from that type of data. When they are instantiated, data type plugins make use of data definition instances passed on by the plugin manager. The latter can also infer which data definition needs to be used by a
DataType plugin type.
Let's see an example:
$definition = DataDefinition::create('string')
->addConstraint('Length', array('max' => 20));
We created a
string data definition and applied the
Length constraint to it. Constraints are a key part of the validation API and they define the type of validation that will run on the data. They are coupled with a validator that actually performs the task, but we will see more about constraints and validators in the second part of this series.
Next, we use the
DataType plugin manager to create the actual data type plugin instance:
$string_typed_data = \Drupal::typedDataManager()->create($definition, 'my string');
We loaded the manager service statically here for brevity but you should use dependency injection in your project if you are in a class context. The
create() method on the
TypedDataManager takes the data definition as the first parameter, the actual value as its second and returns a
DataType plugin instance of the type that matches the definition: in our case
StringData. For complex data,
DataType plugins can specify in their annotations which data definition class they need to use. And for more information about plugins in Drupal 8, make sure you check out my previous articles on the topic.
One of the methods on this plugin instance is
validate(), which triggers data validation against all the constraints that have been applied to the definition and returns an instance of Symfony's ConstraintViolationList. By iterating over it or using methods like
count() or
get() we can check if the data is valid and which constraints have failed if not.
In our example, the violation list should have no validation errors because the string we used is under 20 characters. Had we used a longer string when creating the plugin, we would have had one violation represented by a Symfony
ConstraintViolationInterface instance with a message, offending value and even a property path.
Typed Data and Content Entities
Now that we know a bit about the Typed Data API, let's see how this applies to content entities.
Entity data in Drupal 7 is split between entity properties (usually the columns on the entity table) and Field API fields that are configured through the UI. In Drupal 8, they have been brought under the same umbrella so the old properties become fields as well. However, a difference still remains in that some fields (mainly the old entity properties) are defined as base fields while the rest are configurable fields.
The data definitions for these fields are
BaseFieldDefinition and
FieldConfig, but they are both implementors of the same
FieldDefinitionInterface (which is a complex extender of the
DataDefinitionInterface – the interface directly implemented by
DataDefinition we saw earlier).
Each individual field holds data in a special
FieldItemListInterface implementation and is always a list of individual
FieldItem plugins (even if there is only one real value in the field). Each of these plugins extends a
DataType plugin and uses a type of
DataDefinitionInterface implementation itself (usually
FieldItemDataDefinition). Things are quite complex at this level so it's well worth taking a closer look at the makeup of entity fields to better understand this architecture.
Adding Entity and Field Constraints
Constraints in Drupal 8 are also plugins which usually hold a small amount of information about how data is actually being validated, what error message should be used in case of failure and any additional options the validator needs. The validator class (which is referenced by the constraint) is responsible for checking the data. We've seen one example,
Length, which is in fact the
LengthConstraint class validated directly by Symfony's
LengthValidator class. We will see in the second part how to create our own constraint and validator. For now, though, let's see how we can add existing constraints to content entities and fields.
Entity level constraints
Entity level constraints are added in the annotation of the entity class itself. For example, this is how the
Comment entity has defined a constraint for its name/author fields combination:
constraints = {
"CommentName" = {}
In this example,
CommentName is the plugin ID of the constraint that will be validated against when saving a comment entity. The opening and closing braces means that the plugin takes no options.
If we wanted to add to or remove a constraint from an existing entity, we'd have to implement
function demo_entity_type_alter(array &$entity_types) {
$node = $entity_types['node'];
$node->addConstraint('ConstraintPluginName', ['array', 'of', 'options']);
In this example, we are adding a fictitious constraint to the Node entity and passing an array of options to it.
Field level constraints
There is more than one way to add field level constraints depending on whether the content entity type is defined in our module and whether the type of field we are talking about is a base field or configurable.
If we are defining our own entity type, one of the methods on the actual entity class that we have to implement is
baseFieldDefinitions(). This is where we return an array of
BaseFieldDefinition field definitions and we can easily add our constraints there. For example, this is how the Node ID base field is being defined:
$fields['nid'] = BaseFieldDefinition::create('integer')
->setLabel(t('Node ID'))
->setDescription(t('The node ID.'))
->setSetting('unsigned', TRUE);
Similarly to how we added constraints to the
DataDefinition instance earlier, the Node entity could also add constraints to the Node ID field, more specifically:
- to the
BaseFieldDefinitonitself, which is, as we saw, the definition for the
FieldItemListInterfaceimplementation (the list)
- to the individual
FieldItemDataDefinitionitems, which are, as we saw, a type of complex data definition for the
FieldItemInterfaceimplementations (the items)
If we want to add a constraint to a Node base field (or of any other content entity type not defined by our module), we have to implement
hook_entity_base_field_info_alter() and add our constraint there:
function demo_entity_base_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) {
if ($entity_type->id() === 'node') {
$title = $fields['title'];
$title->addPropertyConstraints('value', ['Length' => ['max' => 5]]);
In the example above, we are adding a constraint to the Node title field to make sure that no title can be longer than 5 characters. To notice is that we are using the
addPropertyConstraint() method instead of the
addConstraint() one we saw earlier. This is because we are not targeting the definition for the list of items but the individual item definitions themselves (
By using the
addConstraint() method, we are adding a constraint to the entire list of items. This means that when the constraint gets validated, the validator receives the entire list not just the value of the individual item.
If we want to add a constraint to a configurable field, the process is quite similar. The difference is that we need to implement
hook_entity_bundle_field_info_alter() and work with
FieldConfig instances instead of
If we want to inspect the constraints that are already set on the field, we can do something like this:
$title = $fields['title'];
$constraints = $title->getConstraints();
$property_constraints = $title->getItemDefinition()->getConstraints();
In this example,
$title is either an instance of
BaseFieldDefinition or
FieldConfig. The
$constraints array is, as expected, a list of constraints applied to the entire list of field items while the
$property_constraints is an array of constraints applied to the individual field items themselves. We can notice, though, that if we run this code after we've applied the
Length constraint, we will find inside
$property_constraints a
ComplexData constraint which wraps over the individual constraints applied to the field values. This is because there can be multiple individual data definitions for a single field item and Drupal by default groups them like so.
In this article, we've started looking at the Entity Validation API in Drupal 8. To this end, we also had to get a sense of the Typed Data API which is used as a foundation for the entity system. Once that became a bit clearer, we've seen how constraints can be added to various types of data definitions, including those used by entities and fields.
In the next part, we will look at how the actual validation works and how handling the violations that may occur should be done. Additionally, we will create our own constraint and validator and apply our knowledge from this part to various data definition types. Fun!
Meet the authorDaniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
Most web development start-ups have the same problem – What programming language to pick? Which language should we specialize in? Technology selection can be a real drag, especially when dealing with larger teams of specialists with different favourites in mind. Here's food for thought: do successful companies become successful because of the technology they used or the technology itself becomes popular because it's used by a successful company?
Currently, PHP is not a popular choice for web development start-ups – most web development teams opt for Django or Ruby on Rails but in recent years PHP technology has evolved substantially - even to the point of becoming a direct rival for Ruby on Rails or Django.
PHP is actually an incredible tool for web development start-ups – it offers all the functionality of RoR at a much lower cost. PHP is great for web development projects which involve multiple components and rely on multiple tech solutions. Here is a list of reasons why PHP is a great tool for web development start-ups:
PHP allows fast web development
PHP is actually one of the fastest programming languages which you can use to code, deploy and execute. The whole language was designed on finding the fastest and shortest solutions to any web development problems. Apart from that, PHP's lovely community has been moving it towards modular design.
PHP frameworks offer libraries and bundles which are ready to use, mouldable and easily configurable. If you're interested in using PHP for web development you should definitely check out the Sonata Project for the Symfony framework – it offers ecommerce tools, content management features, technical utilities and admin bundles which in turn enable web development companies to lower their work load and shorten their path to the finished product.
PHP is cost effective
Web development companies won't have any trouble with finding replacements for their team members, hiring new members or project stuffing. PHP developers are much cheaper than developers specialized in other programming languages – this can lead to a more affordable and competitive finished product. Certain very useful tools are free for use with PHP – these include integration tools like Deployer, Phing Project, Capifony, the PHPUnit framework and the Composer dependency manager.
Programming quality
PHP offers plenty of educational materials and best practices materials regarding common or complex programming tasks. While some may voice criticism of PHP unpredictability and inconsistency it's good to remind users that these are problems of the past. The newest versions of PHP offer great frameworks such as Symfony and Laravel and great efforts have been made to define precise basic coding standards and guidelines.
PHP is a great tool for any web development start up, given its guidelines, processes and conventions. A solid team with plenty of experience will get your start up going in no time!
Views is in Drupal 8 core. We all know that by now. Twig is the new templating engine in Drupal 8. This we also know. But do we know how to interact programmatically with the first in order to theme a View using the second? Aside from overriding View templates like with any other subsystem, we have available a more powerful alternative in the form of Views plugins (Display, Style, Row and Field).
In this article, we are going to look at how we can create a custom Style plugin for Views in Drupal 8. We will use the Bootstrap tab markup as a goal and implement a tabbed output for our View results. In the View configuration, the Style settings will allow us to specify which field will be used as the tab navigation copy, leaving the rest of the fields shown in the respective tab panes. Basically, each View result will represent a tab – so this example is not suited for Views which have more than a few results. The main goal is to illustrate how we can create our own Views Style plugins in Drupal 8.
We will not cover the details on how you can use Bootstrap in your project. However, you can check out the documentation page on assets or even this article on how to make sure anonymous users can benefit from jQuery being loaded on the page. And if you want to see the code we write ahead of time, you can find it in this repository within the Demo module.
What Is the Style Plugin?
The Views Style plugin is the one responsible for rendering the listing. Notable examples of core Style plugins are Unformatted List, HTML List, Table or Grid. They are used by the Display plugin and they in turn use Row plugins that represent one item in the listing.
In Drupal 8, all Views plugin types are built using the new Plugin system and share some common functionality (they always extend from the same Views
Let's now create our own such Style plugin that can be used by most Display types (Page, Block, etc) and which uses the Field row plugin.
The Bootstrap Tabs Style Plugin
The first step is to create our plugin class located in the
Plugin/views/style folder of our module:
namespace Drupal\demo\Plugin\views\style;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\style\StylePluginBase;
class BootstrapTabs extends StylePluginBase {
protected $usesRowPlugin = TRUE;
protected $usesGrouping = FALSE;
protected function defineOptions() {
$options = parent::defineOptions();
$options['tab_nav_field'] = array('default' => '');
return $options;
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
$options = $this->displayHandler->getFieldLabels(TRUE);
$form['tab_nav_field'] = array(
'#title' => $this->t('The tab navigation field'),
'#description' => $this->t('Select the field that will be used as the tab navigation. The rest of the fields will show up in the tab content.'),
'#type' => 'select',
'#default_value' => $this->options['tab_nav_field'],
'#options' => $options,
The Drupal plugin type we are creating an instance of is
ViewsStyle with some basic configuration passed in the annotation. Leaving aside the obvious ones, we have the
theme and
display_types keys that are worth mentioning. The first declares which theme function this Style plugin will use to render its data while the second declares which kinds of Display plugins this Style can be used by (in our case all Display types which don't otherwise specify a custom type: normal). For more information on all the available annotation configuration for this plugin type, check out the
Drupal\views\Annotation\ViewsStyle annotation class.
Using the two class properties, we declare that our Style uses row plugins but does not allow grouping. Make sure you check out the parent classes to learn more about what other options can be specified like this. For example, the class we are extending already declares that Views fields can be used with the Style plugin.
As mentioned before, using the two methods we create a plugin option and form element to be able to specify which field should act as the tab navigation. Using the current display handler (
$this->displayHandler) we can load up all the available View fields the site builder has added to it. And this new form element will be available on the Style settings form:
Since we are extending from the
StylePluginBase class, there is nothing more we need to do. For the markup output we can rely on the
demo_bootstrap_tabs theme which receives the relevant variables from the executed View. If we want, we can override any of the render methods and add more variables, change the theme, or whatever we need. We are good with the defaults, especially since we will implement a preprocessor to handle the variables that the template receives.
The Theme
It's time to define the
demo_bootstrap_tabs theme as we normally do (inside our
.module file):
function demo_theme($existing, $type, $theme, $path) {
return array(
'demo_bootstrap_tabs' => array(
'variables' => array('view' => NULL, 'rows' => NULL),
'path' => drupal_get_path('module', 'demo') . '/templates',
The Style plugin passes the
$view object and the resulting
$rows by default to the template. It is up to the preprocessor to do a bit of handling of these variables (if needed) before they are sent to the template:
function template_preprocess_demo_bootstrap_tabs(&$variables) {
$view = $variables['view'];
$rows = $variables['rows'];
$variables['nav'] = array();
$field = $view->style_plugin->options['tab_nav_field'];
if (!$field || !isset($view->field[$field])) {
$nav = array();
foreach ($rows as $id => $row) {
$nav[$id] = array(
'#theme' => 'views_view_field',
'#view' => $view,
'#field' => $view->field[$field],
'#row' => $row['#row'],
$variables['nav'] = $nav;
So what's happening here? First, we check the Style plugin options for the field name to be used (the one that was selected when configuring the View). If one is not there, we return, but not before doing a bit of default preprocessing that the
template_preprocess_views_view_unformatted function already does well. So we delegate to it. Then, we loop through the Views results and build an array of content for our tab navigation. For this, we use the default Views
views_view_field theme function to render the selected field. Finally, we pass this array to the template and also run the default preprocessor of the unformatted list style.
The Template
In Drupal 8 there are no more theme functions, everything is now handled in Twig templates. So let's see how the
demo-bootstrap-tabs.html.twig file looks like in our module's
templates folder:
<ul class="nav nav-tabs" role="tablist">
{% for tab in nav %}
{% set active = '' %}
{% if loop.index0 == 0 %}
{% set active = 'active' %}
{% endif %}
<li role="presentation" class="{{ active }}"><a href="#tab-{{ loop.index0 }}" aria-controls="profile" role="tab" data-toggle="tab">{{ tab }}</a></li>
{% endfor %}
<div class="tab-content">
{% for row in rows %}
{% set active = '' %}
{% if loop.index0 == 0 %}
{% set active = 'active' %}
{% endif %}
<div role="tabpanel" class="tab-pane {{ active }}" id="tab-{{ loop.index0 }}">{{ row.content }}</div>
{% endfor %}
As you can see, this is the necessary markup for the Bootstrap tabs. It won't work, of course, without making sure the relevant Bootstrap styles and script are loaded in your theme first.
The first thing we render are the tab navigation items (from our
nav variable). While looping through this array, we also make use of the loop index value in order to default the first item as active and be able to target the tab content panes below using unique IDs. For the actual value of the items, we just print the render array we created in our preprocessor and Drupal takes care of rendering that. That being said, it is probably a good idea to make sure that the field you use here is relatively short, without a link and plain markup. Titles would probably work just fine. But this is a matter of configuring the View accordingly.
Below the navigation, we print the actual view rows, using the same loop index to default the first row as the active tab pane and identify them uniquely so the navigation above can control their visibility. As for the content, we print the entire
row.content variable (which is prepared inside
template_preprocess_views_view_unformatted) and which contains all the fields in our View. And if we want to not include the field we used for the navigation, we can just exclude that one from display in the View configuration. It will still appear in the navigation (because we explicitly print it there) but not in the main tab pane.
And there we have it. A Views Style plugin to output the View results as Bootstrap tabs. All we need now is to make sure the Bootstrap assets are loaded and simply configure our View to use the new Style plugin. Do keep in mind that this is not meant for Views with lots of results and it only serves as an example to demonstrate how to create Style plugins.
If you have questions, comments, or suggestions, please leave them below!
Meet the authorDaniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
Migrate is one of the most established modules in the Drupal ecosystem. So much so that with Drupal 8, a decision has been made to get some of its functionality ported and added to Drupal core. An important reason was that the traditional upgrade between major releases was replaced with a migration of Drupal 6 or 7 content and configuration to Drupal 8.
Not all the functionality of the Migrate module has been moved into core though. Rather, we have the basic framework within the core
migrate module and the functionality serving the upgrade path from Drupal 6 and 7 within the core
migrate_drupal module. What's left can be found in a host of contrib modules. The most important is Migrate Tools which contains, among other things, drush commands and the UI for running migrations. Additionally, the Migrate Source CSV, Migrate Source XML and Migrate Source JSON modules provide plugins for the most used types of migration sources.
In this article we are going to look at how migration works in Drupal 8 by migrating some content into node entities. For simplicity, the data we play with resides in tables in the same database as our Drupal installation. If you want to see the final code, you can check it out in this repository.
Drupal 8 Migration Theory
The structure of a migration in Drupal 8 is made up of three main parts: the source, the process and the destination, all three being built using the new Drupal 8 plugin system. These three parts form a pipeline. The source plugin represents the raw data we are migrating and is responsible for delivering individual rows of it. This data is passed to one or more process plugins that perform any needed manipulation on each row field. Finally, once the process plugins finish preparing the data, the destination plugins save it into Drupal entities (either content or configuration).
Creating a migration involves using such plugins (by either extending or directly using core classes). All of these are then brought together into a special migration configuration entity. This is shipped as module config that gets imported when the module is first enabled or can be constructed using a template (a case we won't be exploring today). The migration configuration entity provides references to the main plugins used + additional metadata about the migration.
Movie Migration
Let us now get down to it and write a couple of migrations. Our data model is the following: we have two tables in our database:
movies and
movies_genres. The former has an ID, name and description while the latter has an ID, name and movie ID that maps to a movie from the first table. For the sake of simplicity, this mapping is one on one to avoid the complication of a third table. Here is the MySQL script that you can use to create these tables and populate with a few test records (if you want to follow along):
CREATE TABLE `movies` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`description` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
CREATE TABLE `movies_genres` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`movie_id` int(11) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
INSERT INTO `movies` (`id`, `name`, `description`)
(1, 'Big Lebowsky', 'My favorite movie, hands down.'),
(2, 'Pulp fiction', 'Or this is my favorite movie?');
INSERT INTO `movies_genres` (`id`, `movie_id`, `name`)
(1, 1, 'Comedy'),
(2, 1, 'Noir'),
(3, 2, 'Crime');
What we want to achieve is migrate the movies into basic Drupal Article nodes and the genres into taxonomy terms of the Tags vocabulary (which the Article nodes reference via a field). Naturally, we also want the migration to mirror the association between the movies and the genres.
Let us first take care of the genre migration because in Drupal, the movies will depend on them (they will reference them).
Inside the
config/install folder of our module, we need the following configuration entity file called
id: genres
label: Genres
migration_group: demo
plugin: genres
key: default
plugin: entity:taxonomy_term
plugin: default_value
default_value: tags
name: name
The first three elements of this configuration are the migration ID, label and group it belongs to. The following three keys are responsible for the three main parts of the migration pipeline mentioned earlier. Let's talk about the latter three separately.
Under the source key we specify which plugin the migration should use to represent and deliver the source data (in our case the plugin with the ID of
genres). The
key key is used to specify which database should our source query use (that is where our data is).
So in the
Plugin/migrate/source folder of our module, let's create our SQL based source plugin for our genres:
namespace Drupal\demo\Plugin\migrate\source;
use Drupal\migrate\Plugin\migrate\source\SqlBase;
class Genres extends SqlBase {
public function query() {
$query = $this->select('movies_genres', 'g')
->fields('g', ['id', 'movie_id', 'name']);
return $query;
public function fields() {
$fields = [
'id' => $this->t('Genre ID'),
'movie_id' => $this->t('Movie ID'),
'name' => $this->t('Genre name'),
return $fields;
public function getIds() {
return [
'id' => [
'type' => 'integer',
'alias' => 'g',
Since we are using a SQL source, we need to have our own source plugin class to provide some information as to how the data needs to be read. It's not enough to use an existing one from core. The
query() method creates the query for the genre data, the
fields() method defines each individual row field and the
getIds() method specifies the source row field that acts as the unique ID. Nothing complicated happening here.
We are extending from
SqlBase which, among other things, looks for the plugin configuration element named
key to learn which database it should run the query on. In our case, the default one, as we detailed in the migration configuration entity.
And this is all we need for our simple source plugin.
For the migration destination, we use the default core taxonomy term destination plugin which handles everything for us. No need to specify anything more.
Under the
process key of the migration, we list each destination field we want populated and one or more process plugins that transform the source row fields into data for the destination fields. Since we want the genres to be all terms of the Tags vocabulary, for the
vid field we use the
default_value plugin which accepts a
default_value key that indicates the value each record will have. Since all will be in the same vocabulary, this works well for us.
Lastly, for the term name field we can simply specify the source row field name without an explicit plugin name. This will, under the hood, use the
get plugin that simply takes the data from the source and copies it over unaltered to the destination.
For more information on how you can chain multiple process plugins in the pipeline or what other such plugins you have available from core, I recommend you check out the documentation.
Now that our genres are importable, let's take a look at the movies migration configuration that resides in the same folder as the previous (
id: movies
label: Movies
migration_group: demo
plugin: movies
key: default
plugin: entity:node
plugin: default_value
default_value: article
title: name
body: description
plugin: migration
migration: genres
source: genres
- genres
We notice the same metadata as before, the three main parts of the migration (source, process and destination) but also the explicit dependency which needs to be met before this migration can be successfully run.
Like before, let's take a look at the
movies source plugin, located in the same place as the
genres source plugin (
Plugin/migrate/source ):
namespace Drupal\demo\Plugin\migrate\source;
use Drupal\migrate\Plugin\migrate\source\SqlBase;
use Drupal\migrate\Row;
class Movies extends SqlBase {
public function query() {
$query = $this->select('movies', 'd')
->fields('d', ['id', 'name', 'description']);
return $query;
public function fields() {
$fields = [
'id' => $this->t('Movie ID'),
'name' => $this->t('Movie Name'),
'description' => $this->t('Movie Description'),
'genres' => $this->t('Movie Genres'),
return $fields;
public function getIds() {
return [
'id' => [
'type' => 'integer',
'alias' => 'd',
public function prepareRow(Row $row) {
$genres = $this->select('movies_genres', 'g')
->fields('g', ['id'])
->condition('movie_id', $row->getSourceProperty('id'))
$row->setSourceProperty('genres', $genres);
return parent::prepareRow($row);
We have the same three required methods as before, that do the same thing: query for and define the data. However, here we also use the
prepareRow() method in order to alter the row data and available fields. The purpose is to select the ID of the movie genre that matches the current row (movie). That value is populated into a new source field called
genres, which we will see in a minute how it's used to save the Tags taxonomy term reference.
In this case, we use the node entity destination plugin and we need nothing more.
There are four fields on the Article node we want populated with movie data. First, for the node
type we use the same technique as before for the taxonomy vocabulary and set
article to be the default value. Second and third, for the title and body fields we map the movie name and description source fields unaltered.
Lastly, for the tags field we use the migration process plugin that allows us to translate the ID of the genre (that we added earlier to the
genres source row field) into the ID of its corresponding taxonomy term. This plugin does this for us by checking the migration mapping of the genres and reading these IDs. And this is why the genres migration is also marked as a dependency for the movies import.
Activating and Running the Migration
Now that we have our two migration configuration entities and all the relevant plugins, it's time to enable our module for the first time and have the configuration imported by Drupal. If your module was already enabled, uninstall it and then enable it again. This will make the config import happen.
Additionally, in order to run the migrations via Drush (which is the recommended way of doing it), install the Migrate Tools module. Then all that's left to do is to use the commands to migrate or rollback the movies and genres.
To see the available migrations and their status:
drush migrate-status
To import all migrations:
drush migrate-import --all
To roll all migrations back:
drush migrate-rollback --all
And there we have it – a simple migration to illustrate how we can now import, track and roll back migrations in Drupal 8. We've seen how the plugin system is used to represent all these different components of functionality, and how the migration definition has been turned into configuration that brings these elements together.
There is much more to learn, though. You can use different source plugins, such as for data in CSV or JSON, complex process plugins (or write your own), or even custom destination plugins for whatever data structure you may have. Good luck!
Meet the authorDaniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
The recommended approach to getting started with Drupal 8 is now via Composer. An official project template has been created for this. We will create our project directly using the template, which is also available on Packagist.
To create a new project based on this template we can run the following Composer command:
composer create-project drupal-composer/drupal-project:8.x-dev my_project --stability dev --no-interaction
This Composer command will pull in the template from Packagist and run a few Drupal specific scripts to prepare our project for installation. The only thing left to do is point our browser to the
web/ directory (since that is where the
index.php file is) and run the installer as usual.
This template comes with a
/web folder that contains, among other things, the main folders of a Drupal installation that are no longer considered part of Drupal core (such as the
index.php file or the modules and themes folders). Additionally, it comes with an
autoload.php file used by Drupal that simply points to the Composer
vendor/ directory, one folder up. So all PHP libraries are now handled from one single place.
The template's
composer.json file requires the latest stable Drupal core + some additional helper tools such as Drush and the Drupal Console. Additionally, it adds the Drupal specific Packagist repository from where we can install Drupal contributed modules, themes and profiles (that get automatically installed in the right place).
If we want to add a Drupal contributed module, we need to find it on the Drupal Packagist and require it in our project via Composer:
composer require drupal/ctools
This will add the Ctools module directly to our
web/modules/ directory and update our
composer.json file.
The project template also comes with a
.gitignore file that keeps Drupal core and all the contributed packages outside of Git, similar to the regular
vendor/ packages. So based on an updated
composer.json file, we can maintain a smaller Git repository and recreate our project any time. A lot of the benefits of Drush Make have now been incorporated into a Composer flow.
Drupal 8 has come a long way in catching up with other major PHP software. The possibility of fully managing it via Composer, either as a main project or even just as part of a bigger set of applications, is a testament to the community effort that went in.
Meet the authorDaniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
In an earlier tutorial, we looked at the Drupal 8 plugin system and how to create our very own custom plugin type. We've seen that much of the functionality declared via
_info hooks in Drupal 7 has been replaced by these plugins. Our use case was very basic and it allowed each instance of such functionality to be declared manually via a new plugin class and associated form.
But what if we needed such instances declared dynamically depending on some factors external to our little subsystem? For example, when declaring
_info hooks in Drupal 7, we can get a list of something, loop over it and declare a new item in the returned array for each individual something. The menu system does this in order to provide a new block for each menu that either comes with Drupal core or is later created through the UI.
So what about Drupal 8? We've seen that for each plugin of a certain type we need to declare a different PHP class. To create a new block, we need a new class. To create another block, we need another class. So where would that looping we see in Drupal 7 take place? The short answer to this is: within a plugin derivative.
In this article we will explore the long answer to that and learn what derivates are and how we can use them. For the latter, we will build an example inside the
demo module that can be found in this git repository and which should hopefully help us better understand what's going on. For a slightly more complex example, the Menu system is great as it provides an individual block for each of its menus (similar to Drupal 7 but using plugins).
What we are going to do is actually very simple. We are going to implement basic Node Block functionality by which for all the article nodes on our site we will have a block. Ridiculous? Sure. Should we be doing this for all the nodes on our site? Definitely not! But it's a very basic implementation meant to keep things short and demonstrate the use of the plugin derivatives.
Plugin Derivatives
Plugin derivatives are the way through which a plugin of a certain type can be represented in the system as multiple instances of itself. In other words, a plugin can reference a deriver class which is responsible for providing a list of plugin definitions that are based on the initial plugin (start from the same base definition) but have slightly different configuration or definition data. The
SystemMenuBlock we referred to above is a great example. It's a single plugin which has as many derivatives as there are menus on the site.
To go a bit deeper, when a list of all the plugins of a certain type is requested, the plugin manager uses its discovery mechanism to load all the plugins of this type. If that mechanism is decorated with the
DerivativeDiscoveryDecorator, the manager will be able to also retrieve derivatives. In order to do this, the derivative discovery looks for a deriver class on each plugin and, if it finds one, asks it for this list.
Plugin type managers that extend the
DefaultPluginManager base class should normally have the derivative discovery mechanism decorating the default discovery (annotations). This is the most common pattern in the Drupal core plugin system: annotated discovery wrapped by derivatives.
The Derivative Class
Now that we know what the role of plugin derivatives is, let's create our first deriver class that will be used by our block plugin (which we will create in a minute).
src/Plugin/Derivative/NodeBlock.php of the
demo module we have the following:
* @file
* Contains \Drupal\demo\Plugin\Derivative\NodeBlock.
namespace Drupal\demo\Plugin\Derivative;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
* Provides block plugin definitions for nodes.
* @see \Drupal\demo\Plugin\Block\NodeBlock
class NodeBlock extends DeriverBase implements ContainerDeriverInterface {
* The node storage.
* @var \Drupal\Core\Entity\EntityStorageInterface
protected $nodeStorage;
* Constructs new NodeBlock.
* @param \Drupal\Core\Entity\EntityStorageInterface $node_storage
* The node storage.
public function __construct(EntityStorageInterface $node_storage) {
$this->nodeStorage = $node_storage;
* {@inheritdoc}
public static function create(ContainerInterface $container, $base_plugin_id) {
return new static(
* {@inheritdoc}
public function getDerivativeDefinitions($base_plugin_definition) {
$nodes = $this->nodeStorage->loadByProperties(['type' => 'article']);
foreach ($nodes as $node) {
$this->derivatives[$node->id()] = $base_plugin_definition;
$this->derivatives[$node->id()]['admin_label'] = t('Node block: ') . $node->label();
return $this->derivatives;
All our class needs to implement is the
DeriverInterface and implement its two methods. We use the
ContainerDeriverInterface instead because we want to make our deriver container aware. Why? Because we use dependency injection to load Drupal's entity manager so that we can access the Node storage (this is what the constructor and the
create() method do). Additionally, our deriver class extends from the
DeriverBase class because that already takes care of one of the required methods (
getDerivativeDefinitions() is the method responsible for providing an array of plugin definitions that derive from the plugin which uses this class. It receives the
$base_plugin_definition as an argument (the definition of the actual plugin which uses this deriver) and we use that to build up our derivative definitions. In our case, we indiscriminately load all the Article nodes and, for each of them, create a separate definition which differs only by having a different
admin_label (this is a property on the
Drupal\Core\Block\Annotation\Block annotation class). The array of derivatives is keyed by the ID of the derivative (in our case the Node ID which we will use later).
A very important point we need to make here is that loading all the nodes and creating plugins out of them is never a good idea. What would be maybe interesting is to implement functionality by which individual nodes can be exposed as blocks via a checkbox or something like that.
The Block Plugin
Now that we have our deriver class, let's create a simple block plugin that uses it to generate multiple instances of itself (one for each Article node).
namespace Drupal\demo\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityViewBuilderInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\node\NodeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
* Provides a 'NodeBlock' block plugin.
* @Block(
* id = "node_block",
* admin_label = @Translation("Node block"),
* deriver = "Drupal\demo\Plugin\Derivative\NodeBlock"
* )
class NodeBlock extends BlockBase implements ContainerFactoryPluginInterface {
* @var EntityViewBuilderInterface.
private $viewBuilder;
* @var NodeInterface.
private $node;
* Creates a NodeBlock instance.
* @param array $configuration
* @param string $plugin_id
* @param array $plugin_definition
* @param EntityManagerInterface $entity_manager
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->viewBuilder = $entity_manager->getViewBuilder('node');
$this->nodeStorage = $entity_manager->getStorage('node');
$this->node = $entity_manager->getStorage('node')->load($this->getDerivativeId());
* {@inheritdoc}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
* {@inheritdoc}
public function build() {
if (!$this->node instanceof NodeInterface) {
$build = $this->viewBuilder->view($this->node, 'full');
return $build;
* {@inheritdoc}
public function blockAccess(AccountInterface $account, $return_as_object = FALSE) {
return $this->node->access('view', NULL, TRUE);
The first thing we notice in this plugin's annotation is the
deriver key which points to the class we created before. And that is basically all we need to couple the two. The derivative discovery decorator handles the heavy lifting.
Much of the rest is basic block building we should be familiar with. What's interesting is that we can use the
getDerivativeId() method to retrieve the node ID we used also as the ID of the derivative being displayed and, using that, we load the node object and build the block as the actual node output. Lastly, inside the
blockAccess() method we make sure that this block has the same access checks as the actual node itself. So if the current user doesn't have access to view the current node, the block won't even show up.
Now if we clear the caches and navigate to the Block Layout interface we should see some blocks called
Node Block: [Node title]. You can place these where you want and they will render the relevant node.
In this article, we've looked at plugin derivatives and seen a simple example of how they work. The key take away on this topic is that plugin derivatives are the way we dynamically declare multiple instances of the same plugin. They usually help us transform user configured functionality (e.g. menus) into plugins (e.g. menu blocks).
To illustrate the use of derivatives, we've seen a very simple technique which allows us to render Article nodes as blocks. We should remember though not to try this out on a website with many Article nodes but rather implement additional functionality that limits the number of nodes that get exposed. You know, so we don't crash our site.
Questions? Comments? Anything you'd like explained further? Let us know!
Meet the authorDaniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
Code reviews are a regular part of our project process and give us the opportunity to catch bugs and standardize code before work is tested by our project leads or clients. You can read more about our code review philosophy in our last post.
This post aims to give an overview of some of the code review tools we use for PHP code reviews.
Tool Time
Git history
For smaller reviews, using Git history to look at a code change is all you need. We use a post-commit Git hook that posts commit hashes to their related tickets in our project management software, so when you're assigned a ticket to review, you can easily see the commit IDs and run "
git show [the hash]" to see the change. With some other ticket management tools you may even be able to see the code changes right along with the ticket comments.
The PHP CodeSniffer (PHPCS) utility reviews PHP code for adherence to a given code standard. For Drupal projects, we can check code against Drupal's standards. There are a few ways to run this, but first, you'll need to install a few things.
How to install PHP CodeSniffer
- Download the PHPCS package using Composer.
- For Drupal 7 projects:
composer global require squizlabs/PHP_CodeSniffer:\<2
- For Drupal 8 projects:
composer global require squizlabs/PHP_CodeSniffer:\>=2
- Or, you can install PHPCS with Drush.
- For Drupal 7 projects:
- Download the Drupal Coder module (7.x-2.x branch – this part is important, don't choose the 1.x branch). Move this to your central Drush directory (
$HOME/.drush) – that allows it to be used on all your Drupal projects.
- Configure PHPCS to use Drupal standards:
phpcs --config-set installed_paths $HOME/.drush/coder/coder_sniffer
phpcs --config-set default_standard Drupal
Run PHP CodeSniffer in phpStorm IDE
If you use an IDE, there's probably a plugin for running PHPCS. I set it up in phpStorm like this:
- Follow the directions above to install CodeSniffer with the Drupal standards.
- Set the path to your CodeSniffer installation in phpStorm (Preferences > Languages & Frameworks > PHP > CodeSniffer). Click the Validate button there to make sure it works.
- Enable CodeSniffer (Preferences > Editor > Inspections): Select "PHP CodeSniffer validation", then select Drupal as the standard to use.
Once that's hooked up, you'll start to see inline alerts of your rule breaking. You can also run PHPCS against a whole file, directory or project (Code > Run inspection by name > PHPCS). This will give you a list of all the issues PHPCS finds, with a synopsis of the problem and how to fix it.
There are a lot more Drupal-specific features in phpStorm that are worth trying out, especially in Drupal 8 – check out the JetBrains site for more information.
Run CodeSniffer on the command line
If you don't use an IDE or just prefer a CLI, you can run PHPCS with terminal commands. You can do this with Drush, like this:
drush drupalcs path/to/your/file
Or, without Drush, like this:
phpcs --standard=Drupal path/to/your/file
The command will return a list of errors and the line numbers where they occur.
Drupal Coder Review module
If you prefer a UI, you can still make use of the Coder module by way of the accompanying Coder Review module.
- Download the Coder module to your site's module directory and enable coder and coder_review.
- Browse to admin/config/development/coder/settings.
- Choose which modules or themes to review.
- Review your results, and if needed, make the suggested changes to your code.
Further Reading
Best practices for Drupal code are well-documented on Drupal.org:
These are some other blog posts on the topic:
Do you use any other code review tools?
How do you use code review tools in your project process?
The Queue API in Drupal allows us to handle a number of tasks at a later stage. What this means is that we can place items into a queue which will run some time in the future and process each individual item at that point and at least once. Usually, this happens on CRON runs, and Drupal 8 allows for a quick set up for cronjob based queues. It doesn't necessarily have to be CRON, however.
In this article, we will look at using the Queue API in Drupal 8 by exploring two simple examples. The first will see the queue triggered by Cron while the second will allow us to manually do so ourselves. However, the actual processing will be handled by a similar worker. If you want to follow along, clone this git repository where you can find the
npq module we will write in this article.
The module we'll work with is called Node Publisher Queue and it automatically adds newly created nodes that are saved unpublished to a queue to be published later on. We will see how later on can be the next CRON run or a manual action triggered by the site's administrator. First, let's understand some basic concepts about queues in Drupal 8.
The theory
There are a few components that make up the Queue API in Drupal 8.
The most important role in this API is played by the
QueueInterface implementation which represents the queue. The default queue type Drupal 8 ships with is currently the
DatabaseQueue which is a type of reliable queue that makes sure all its items are processed at least once and in their original order (FIFO). This is in contrast to unreliable queues which only do their best to achieve this (something for which valid use cases do exist).
The typical role of the queue object is to create items, later claim them from the queue and delete them when they have been processed. In addition, it can release items if processing is either not finished or another worker needs to process them again before deletion.
QueueInterface implementation is instantiated with the help of a general
QueueFactory. In the case of the
DatabaseQueue, the former uses the
DatabaseQueueFactory as well. Queues also need to be created before they can be used. However, the
DatabaseQueue is already created when Drupal is first installed so no additional setup is required.
The Queue Workers are responsible for processing queue items as they receive them. In Drupal 8 these are
QueueWorker plugins that implement the
QueueWorkerInterface. Using the
QueueWorkerManager, we create instances of these plugins and process the items whenever the queue needs to be run.
The Node Publish Queue module
Now that we've covered the basic concepts of the Queue API in Drupal 8, let's get our hands dirty and create the functionality described in the introduction. Our npq.info.yml file can be simple:
name: Node Publish Queue
description: Demo module illustrating the Queue API in Drupal 8
core: 8.x
type: module
Queue item creation
Inside the
npq.module file we take care of the logic for creating queue items whenever a node is saved and not published:
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\Queue\QueueInterface;
* Implements hook_entity_insert().
function npq_entity_insert(EntityInterface $entity) {
if ($entity->getEntityTypeId() !== 'node') {
if ($entity->isPublished()) {
/** @var QueueFactory $queue_factory */
$queue_factory = \Drupal::service('queue');
/** @var QueueInterface $queue */
$queue = $queue_factory->get('cron_node_publisher');
$item = new \stdClass();
$item->nid = $entity->id();
Inside this basic
hook_entity_insert() implementation we do a very simple task. We first retrieve the
QueueFactoryInterface object from the service container and use it to get a queue called
cron_node_publisher. If we track things down, we notice that the
get() method on the
DatabaseQueueFactory simply creates a new
DatabaseQueue instance with the name we pass to it.
Lastly, we create a small PHP object containing the node ID and create an item in the queue with that data. Simple.
The CRON queue worker
Next, let's create a
QueueWorker plugin that will process the queue items whenever Cron is run. However, because we know that we will also need one for manual processing that does the same thing, we will add most of the logic in a base abstract class. So inside the
Plugin/QueueWorker namespace of our module we can have the
NodePublishBase class:
* @file
* Contains Drupal\npq\Plugin\QueueWorker\NodePublishBase.php
namespace Drupal\npq\Plugin\QueueWorker;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Queue\QueueWorkerBase;
use Drupal\node\NodeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
* Provides base functionality for the NodePublish Queue Workers.
abstract class NodePublishBase extends QueueWorkerBase implements ContainerFactoryPluginInterface {
* The node storage.
* @var \Drupal\Core\Entity\EntityStorageInterface
protected $nodeStorage;
* Creates a new NodePublishBase object.
* @param \Drupal\Core\Entity\EntityStorageInterface $node_storage
* The node storage.
public function __construct(EntityStorageInterface $node_storage) {
$this->nodeStorage = $node_storage;
* {@inheritdoc}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
* Publishes a node.
* @param NodeInterface $node
* @return int
protected function publishNode($node) {
return $node->save();
* {@inheritdoc}
public function processItem($data) {
/** @var NodeInterface $node */
$node = $this->nodeStorage->load($data->nid);
if (!$node->isPublished() && $node instanceof NodeInterface) {
return $this->publishNode($node);
Right off the bat we can see that we are using dependency injection to inject the
NodeStorage into our class. For more information about dependency injection and the service container, feel free to check out my article on the topic.
In this base class we have two methods:
publishNode() and the obligatory
processItem(). The former publishes and saves a node that is passed to it. The latter loads the node using the node ID contained in the
$data object and publishes it if it's unpublished.
Now, let's create a
CronNodePublisher plugin that will use this logic on Cron runs:
namespace Drupal\npq\Plugin\QueueWorker;
* A Node Publisher that publishes nodes on CRON run.
* @QueueWorker(
* id = "cron_node_publisher",
* title = @Translation("Cron Node Publisher"),
* cron = {"time" = 10}
* )
class CronNodePublisher extends NodePublishBase {}
And that is all. We don't need any other logic than what already is in our base class. Notice that, in the annotation, we are telling Drupal that this worker needs to be used by Cron to process as many items as it can within 10 seconds. How does this happen?
Whenever Cron runs, it uses the
QueueWorkerManager to load all its plugin definitions. Then, if any of them have the
cron key in their annotation, a Queue with the same name as the ID of the worker is loaded for processing. Lastly, each item in the queue is claimed and processed by the worker until the specified time has elapsed.
If we now save an unpublished node, it will most likely become published at the next Cron run.
The manual worker
Let's create also the possibility for the Queue to be processed manually. First, let's adapt the
hook_entity_insert() implementation from before and change this line:
$queue = $queue_factory->get('cron_node_publisher');
to this:
$queue = $queue_factory->get('manual_node_publisher');
You can of course provide an admin screen for configuring which type of node publisher the application should use.
Second, let's create our
ManualNodePublisher plugin:
namespace Drupal\npq\Plugin\QueueWorker;
* A Node Publisher that publishes nodes via a manual action triggered by an admin.
* @QueueWorker(
* id = "manual_node_publisher",
* title = @Translation("Manual Node Publisher"),
* )
class ManualNodePublisher extends NodePublishBase {}
This is almost the same as with the CRON example but without the
cron key.
Third, let's create a form where we can see how many items are in the
manual_node_publisher queue and process them all by the press of a button. Inside
npq.routing.yml in the module root folder:
path: '/npq'
_form: '\Drupal\npq\Form\NodePublisherQueueForm'
_title: 'Node Publisher'
_permission: 'administer site configuration'
We define a path at
/npq which should use the specified form that lives in that namespace and that we can define as such:
* @file
* Contains \Drupal\npq\Form\NodePublisherQueueForm.
namespace Drupal\npq\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\Queue\QueueInterface;
use Drupal\Core\Queue\QueueWorkerInterface;
use Drupal\Core\Queue\QueueWorkerManagerInterface;
use Drupal\Core\Queue\SuspendQueueException;
use Symfony\Component\DependencyInjection\ContainerInterface;
class NodePublisherQueueForm extends FormBase {
* @var QueueFactory
protected $queueFactory;
* @var QueueWorkerManagerInterface
protected $queueManager;
* {@inheritdoc}
public function __construct(QueueFactory $queue, QueueWorkerManagerInterface $queue_manager) {
$this->queueFactory = $queue;
$this->queueManager = $queue_manager;
* {@inheritdoc}
public static function create(ContainerInterface $container) {
return new static(
* {@inheritdoc}.
public function getFormId() {
return 'demo_form';
* {@inheritdoc}.
public function buildForm(array $form, FormStateInterface $form_state) {
/** @var QueueInterface $queue */
$queue = $this->queueFactory->get('node_publisher');
$form['help'] = array(
'#type' => 'markup',
'#markup' => $this->t('Submitting this form will process the Manual Queue which contains @number items.', array('@number' => $queue->numberOfItems())),
$form['actions']['#type'] = 'actions';
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Process queue'),
'#button_type' => 'primary',
return $form;
* {@inheritdoc}
public function submitForm(array &$form, FormStateInterface $form_state) {
/** @var QueueInterface $queue */
$queue = $this->queueFactory->get('manual_node_publisher');
/** @var QueueWorkerInterface $queue_worker */
$queue_worker = $this->queueManager->createInstance('manual_node_publisher');
while($item = $queue->claimItem()) {
try {
catch (SuspendQueueException $e) {
catch (\Exception $e) {
watchdog_exception('npq', $e);
We are again using dependency injection to inject the
QueueFactory and the manager for
QueueWorker plugins. Inside
buildForm() we are creating a basic form structure and using the
numberOfItems() method on the queue to tell the user how many items they are about to process. And finally, inside the
submitForm() method we take care of the processing. But how do we do that?
First, we load the Queue and instantiate a Queue worker (in both cases we use the
manual_node_publisher id). Then we run a
while loop until all the items have been processed. The
claimItem() method is responsible for blocking a queue item from being claimed by another queue and returning it for processing. After it gets processed by the worker, we delete it. In the next iteration, the next item is returned and on like this until no items are left.
Although we have not used it, the
SuspendQueueException is meant to indicate that during the processing of the item, the worker found a problem that would most likely make all other items in the queue fail as well. And for this reason it is pointless to continue to the next item so we break out of the loop. However, we also release the item so that when we try again later, the item is available. Other exceptions are also caught and logged to the watchdog.
Now if we create a couple of nodes and don't publish them, we'll see their count inside the message if we navigate to
/npq. By clicking the submit button we process (publish) them all one by one.
This has been a demonstration example only. It's always important to take into account the potential load of processing a large number of items and either limit that so your request doesn't time out or use the Batch API to split them into multiple requests.
In this article we've looked at the Queue API in Drupal 8. We've learned some basic concepts about how it is built and how it works, but we've also seen some examples of how we can work with it. Namely, we've played with two use cases by which we can publish unpublished nodes either during Cron runs or manually via an action executed by the user.
Have you tried out the Queue API in Drupal 8? Let us know how it went!
Meet the authorDaniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
Without question, Display Suite is one of the most popular modules in Drupal's contributed modules history. It allows the creation of layouts, fields and exposes all sorts of other powerful tools we use to build the presentation layer of our Drupal sites.
One of the more powerful features of Display Suite (DS) is the ability to create custom fields that can be displayed inside DS layouts alongside the actual core field values. In Drupal 7, this has been a very popular way of building layouts and showing dynamic data that is not strictly related to the output of any Field API field on the node (or other) entity.
Display Suite has been ported and is being maintained for Drupal 8. Depending on another contributed module called Layout Plugin, the D8 version offers much of what we have available in Drupal 7 and probably even more.
In this article, we are going to look at how we can create our own Display Suite field in Drupal 8 using the new OOP architecture and plugin system. To demonstrate this, we are going to create a DS field available only on the Article nodes that can be used to display a list of taxonomy terms from a certain vocabulary. And we're going to make it so that the latter can be configured from the UI, namely admins will be able to specify which vocabulary's terms should be listed. Not much usefulness in this example, I know, but it will allow you to understand how things work.
If you are following along, the code we write is available in this repository inside the
Demo module. So feel free to check that out.
Drupal 8 plugins
Much of the functionality that used to be declared using an
_info hook in Drupal 7 is now declared using plugins in Drupal 8. For more information on using plugins and creating your own plugin types, make sure you check out a previous Sitepoint article that talks about just that.
Display Suite also uses the new plugin system to allow other modules to define DS fields. It exposes a
DsField plugin type which allows us to write and maintain all the necessary logic for such a field inside a single plugin class (+ any services we might inject into it). So we no longer implement hook_ds_field_info() and return an array of field information per entity type, but create a plugin class with data straight in its annotation and the relevant logic inside its methods.
VocabularyTerms class
Let us start by creating our plugin class called
VocabularyTerms inside the
src/plugins/DsField folder of our custom module and annotating it for our purposes:
namespace Drupal\demo\Plugin\DsField;
use Drupal\ds\Plugin\DsField\DsFieldBase;
* Plugin that renders the terms from a chosen taxonomy vocabulary.
* @DsField(
* id = "vocabulary_terms",
* title = @Translation("Vocabulary Terms"),
* entity_type = "node",
* provider = "demo",
* ui_limit = {"article|*"}
* )
class VocabularyTerms extends DsFieldBase {
This class alone will hold all of our logic for our very simple DsField plugin. But here are a couple of remarks about what we have so far:
- The annotation is quite self explanatory: it provides meta information about the plugin.
- The class extends
DsFieldBasewhich provides base functionality for all the plugins of this type.
- At the time of writing, the
ui_limitannotation has just been committed to HEAD so it might not be available in the release you are using. Limiting the availability of the field on content types and view modes can be done by overriding the
isAllowed()method of the base class and performing the logic there.
Default configuration
We want our field to be configurable: the ability to select from a list of existing vocabularies. So let's start off by providing some defaults to this configuration so that if the user selects nothing, the
Tags vocabulary which comes with core will be used. For this, we have to implement the
defaultConfiguration() method:
* {@inheritdoc}
public function defaultConfiguration() {
$configuration = array(
'vocabulary' => 'tags',
return $configuration;
And since we only have one configuration option, we return an array with one element keyed by the configuration name. That's about it.
We also want to have the ability to specify from the UI if the list of taxonomy terms is a series of links to their term pages or formatter as plain text. We could implement this within the configuration realm but let's do so using formatters instead. And it's very simple: we implement the
formatters() method and return an array of available formatters:
* {@inheritdoc}
public function formatters() {
return array('linked' => 'Linked', 'unlinked' => 'Unlinked');
These will be available for selection in the UI under the
Field heading of the Manage Display page of the content type. And we'll be able to see the choice when we are building the actual field for display. But more on that in a second.
Configuration summary
It's also recommended that if we are using UI defined settings, we have a summary of what has been selected as a simple string that describes it. This gets printed under the
Widget heading of the Manage Display page of the content type.
To do this, we need to implement the
settingsSummary() method and return said text:
* {@inheritdoc}
public function settingsSummary($settings) {
$config = $this->getConfiguration();
$no_selection = array('No vocabulary selected.');
if (isset($config['vocabulary']) && $config['vocabulary']) {
$vocabulary = Vocabulary::load($config['vocabulary']);
return $vocabulary ? array('Vocabulary: ' . $vocabulary->label()) : $no_selection;
return $no_selection;
Here we start getting more intimate with the actual configuration that was stored with the field, available by calling the
getConfiguration() method on our plugin class. What we do above, then, is check if the vocabulary setting has been set, we load it based on its machine name using the
Vocabulary class and return an array of strings that need to be printed.
Since we are referencing the
Vocabulary class, we also need to use it at the top:
use Drupal\taxonomy\Entity\Vocabulary;
Important to note: I am using
Vocabulary statically here to load an entity for the sake of brevity. It is highly recommended you inject the relevant storage using dependency injection and use that to load entities. The same goes for most classes you'll see me referencing statically below.
Settings form
Now that we display which configuration has been chosen from the UI, it's time to provide the actual form which will allow the user to do so. This will be made available by clicking the cogwheel under the
Operations heading of the Manage Display page of the content type.
* {@inheritdoc}
public function settingsForm($form, FormStateInterface $form_state) {
$config = $this->getConfiguration();
$names = taxonomy_vocabulary_get_names();
$vocabularies = Vocabulary::loadMultiple($names);
$options = array();
foreach ($vocabularies as $vocabulary) {
$options[$vocabulary->id()] = $vocabulary->label();
$settings['vocabulary'] = array(
'#type' => 'select',
'#title' => t('Vocabulary'),
'#default_value' => $config['vocabulary'],
'#options' => $options,
return $settings;
Like before, we need to implement a method for this. And what we do inside is load all the taxonomy vocabulary names and prepare an array of options to be used with a Form API select list. The latter is the only element we need for this form.
Rendering the field
The last thing left to do is implement the
build() method responsible for rendering the contents of our field:
* {@inheritdoc}
public function build() {
$config = $this->getConfiguration();
if (!isset($config['vocabulary']) || !$config['vocabulary']) {
$query = \Drupal::entityQuery('taxonomy_term')
->condition('vid', $config['vocabulary']);
$tids = $query->execute();
if (!$tids) {
$terms = Term::loadMultiple($tids);
if (!$terms) {
return array(
'#theme' => 'item_list',
'#items' => $this->buildTermList($terms),
So what do we do here? First, we access the chosen vocabulary from the configuration. Then we run an EntityQuery to find all the terms in this vocabulary. Next, we load all these terms and finally we return a render array that uses the
item_list theme to print our terms.
Although we don't need it here, in most cases you'll need to access the node entity that is currently being rendered. That is available inside the configuration array under the
entity key. Moreover, under the
build key you have the actual render array of the node being built. So keep this in mind and do inspect the other elements of the configuration array on your own for more information.
I would like to mention a few more things before we take a look at the actual
buildTermList() method. First, for brevity, we used the EntityQuery service statically. In your project, you should inject it. Second, we used the
Term class statically to load the taxonomy term entities. Again, you should inject its storage and use that for this purpose. And lastly, we should import the
Term class at the top with
use Drupal\taxonomy\Entity\Term;
Now that this is clear, let's take a look at our own
buildTermList() method:
private function buildTermList(array $terms) {
$config = $this->getConfiguration();
$formatter = isset($config['field']['formatter']) && $config['field']['formatter'] ? $config['field']['formatter'] : 'unlinked';
$items = array();
foreach ($terms as $term) {
$items[] = $this->buildTermListItem($term, $formatter);
return $items;
This method is responsible for getting the field formatter, looping through the term entities and building an array of term information that can be printed using the
item_list theme. As you can see, though, the individual term entity and formatter are passed to yet another helper method to keep things nice and tidy:
private function buildTermListItem(Term $term, $formatter) {
if ($formatter === 'linked') {
$link_url = Url::fromRoute('entity.taxonomy_term.canonical', array('taxonomy_term' => $term->id()));
return \Drupal::l($term->label(), $link_url);
return SafeMarkup::checkPlain($term->label());
Finally, in the
buildTermListItem() method we either return the sanitized title of the term or a link to it depending on the formatter.
Again we see classes which should be injected but were used statically to save some space. With the risk of sounding like a broken record, keep in mind that you should inject these. For now, we must use them at the top:
use Drupal\Core\Url;
use Drupal\Component\Utility\SafeMarkup;
And there we have it, our very own DsField plugin in Drupal 8. Clearing the caches would now make this field available on all view modes of the Article content type.
Moreover, it can be configured to choose among multiple vocabularies, the terms of which it will then display. And finally, we can even specify a formatter to print these terms either linked or as plain text.
Meet the authorDaniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
This morning, during my usual scan of Feedly/Twitter/Reddit I read Secure the data of visitors on your Drupal website better. The post shows you how to use the Field encryption module.
The Field encryption module ensures that the values stored in the Drupal database are encrypted. When the database ends up in the wrong hands, then nobody can read the data since this module has encrypted it. This way, you are prepared for a worse case scenario.
It all seems straight forward enough, but I suspected it wouldn't be so simple and in fact doesn't look as secure as purported. My main concern was with the 3 options presented for storing the private key used to encrypt data. So I asked on Twitter:
How secure is this if Drupal needs to read the key? http://t.co/q8wzlLy2dM /cc @enygma @Awnage @ircmaxell
— Oscar Merida (@omerida) October 7, 2015
Now, this post isn't meant to denigrate the work of that project or to completely discount the advice on openlucius.com. Let's see why some of the ways to store the key are problematic.
@enygma @omerida @Awnage the key is stored in the db? o_O
— Anthony Ferrara (@ircmaxell) October 7, 2015
The first option is to store the key in the database, and at least the article recommends against selecting that. If your key is in the database, and a malicious attacker manages to steal your database or find some way to read it's contents via SQL Injection, they will have your key. With the key, nothing will stop them from unencrypting your data.
The second option is to specify it as a variable in
settings.php. The key is a little harder to get but is only a
variable_get('encrypt_drupal_variable_key') call away. Since
settings.php is in the public web root, misconfiguring PHP or having PHP files show the source code will leak your key too. Finally, if you're committing your settings files to git or SVN (hint: you shouldn't be), anyone with access to your repository will also have your key.
@omerida imho they should only offer the last option. @ircmaxell @Awnage
— Chris Cornutt (@enygma) October 7, 2015
The final option, to use a File should be the recommended way to specify the key. Ideally, the file is somewhere outside of your web root and, again, not in your code repository.
Only in option 1 is your key vulnerable to SQL injection attack. For the other 2 options, an attacker would have to gain access to your code to get your key. Given how Drupal 7 stores routes in the database, all that takes is and SQLi vulnerability in another module or core itself and someone could install a back door or shell on your site.
@omerida @enygma @Awnage depends on a lot of factors. If done correctly (haven't looked yet), could make SQLi virtually useless by itself.
— Anthony Ferrara (@ircmaxell) October 7, 2015
No matter how you store it, if you have the PHP module enabled, anyone who can build a view or execute PHP code from the Drupal UI can retrieve your key. There's also a temptation to share the key across development, testing, and production environments so that your database snapshots are portable between them all.
Others brought up issues on as well. The original module author added a comment highlighting that the Field Encryption module is still marked as Beta, which was released in 2013.Also, there are better key management solutions for Drupal.
@enygma @ircmaxell @omerida @Awnage the default options are bad, bad, and bad. Modules like townsec_key & key attempt to provide real KMS
— Cash Williams (@cashwilliams) October 7, 2015
Also, there are better algorithms for encryption than those in the module.
@ircmaxell @enygma @omerida @Awnage It's worse than that, the suggested plugin Encrypt, uses ECB & mcrypt with no authentication. *shrug*
— Ashley Pinner (@NeoThermic) October 7, 2015
Security is a Continual Process
This illustrates that security is a continual process, with a lot of considerations to take into account. It's not as easy as installing a single module or ticking a box on a check list. If you're storing really sensitive user data, ask yourself if you really need it. If this data is credit card information—get to know what it takes to be PCI compliant. Then ask if you aren't better off using a payment processor instead. But please, don't be lulled into a false sense of security after adding a single component.
With Drupal, it's possible to build very intricate solutions by gluing the right combination of contrib modules together. Of course, there is a downside to relying only on contrib modules, particularly for one-off or highly custom tasks. Bringing in a contrib module means you assume responsibility for feeding and caring for it. If it's a mature module, like Views, that's one thing. But if you find yourself looking at a little-used module that hasn't seen a stable release yet—or worse hasn't been updated in months—you may be better off rolling your own solution.
Drupal 8 development has shown that PHP itself, and the wider PHP community, already provides ways to solve common tasks. In this post, I'll show you some core PHP functionality that you may not be aware of; pulling in packages and libraries via Composer is a topic for another day.
In this particular case, I need to export a CSV file showing all nodes added to a site after a specific date. This report may get created just a handful of times. True, I could easily build a View and use Views Data Export to download it as CSV but doing it purely in code has these benefits:
- No module dependencies. It's at least one less thing to update and maintain, especially if security issues are found. If data exports in multiple formats were a more import feature across this site, the Views Data Export module would definitely make sense.
- Easy to deploy. If I make it a view, I have to remember to export it and make sure it's deployed to all my environments. If someone edits the view on one of them and doesn't export it, I could lose improvements or fixes. Granted Features can really streamline the process, but in this case it's not critical, and as you'll see everything we do will be in code anyway.
- Integration with Drush. I created a Drush command to run this at the command line. This made it quicker for me to develop since it was easy to run without involving a browser. PHPStorm's built-in terminal was perfect for this task. Drush commands are also useful for automation and scheduling. If I need, this script can be cron'd to run daily and pipe the output somewhere.
- Efficient resource usage. Depending on how PHP is configured, if you're running a command line script you may not have to worry about
max_execution_timesettings. These can cause your script to terminate unexpectedly when you're processing a lot of data. In this case, we could be exporting hundreds or thousands of nodes depending on how far back in time we go.
A Minimal module.info File
For this example, I created a simple exporters module and prefixed it with my initials to prevent unexpected naming clashes. Below are the contents of my
om_exporters.info file, free of any other module dependencies. I also created an
om_exporters.module file just in case but it ended up empty.
name = OM Exporters
description = Exports newest content to CSV
core = 7.x
php = 5.5
version = 7.x-1.0
Creating a Drush Command
Creating a custom Drush command is straightforward. Drush will look for new commands in a file called
<module>.drush.inc. In that file, you should have a function named
<module>_drush_command(), which implements a
hook_Drush_command(). When you add or change commands, you'll need to clear caches for Drush to know about the changes.
In this case, the function is simple:
function om_exporter_drush_command() {
$items['export-newest'] = array(
description' => 'Export Newest Content to CSV',
'aliases' => ['ex-new'],
'callback' => 'om_export_newest',
'arguments' => [
'date' => '',
return $items;
As you can see, the key to
$items becomes our Drush command
export-newest. We can give it a friendly description, a shorter alias (
ex-new), specify the function to run in
callback, and list any arguments we require.
Now, when you run the Drush command as shown below, whatever function listed as the callback will be invoked and passed the arguments needed.
Drush export-newest 2015-08-01
Validating Dates
The first thing we'll do is validate the date argument using PHP 5's
DateTime class. If you're not familiar with it, this class makes it easy to work with Dates and Timezones without a lot of fuss in an object-oriented manner. It's easier to understand than using older functions like
date(), and
The code below takes the
$date_arg passed in from Drush and makes sure its parsable as a date. If validation fails, our function does not continue.
function om_export_newest($date_arg) {
// get the default timezone
$tz = variable_get('date_default_timezone', 'UTC');
$tz = new \DateTimeZone($tz);
// First validate date, we assume $date_arg is
// a string that can be parsed by \DateTime so
// you have the flexibility to pass
// in '-1 months' or '28 days ago'
try {
$since = new \DateTime($date_arg, $tz);
} catch (\Exception $ex) {
Could not parse date:' . $ex->getMessage(),
// ..
Output CSV with SPL's File Object
The Standard PHP Library is a collection of useful classes that's not as well known as it should be. It's intended to solve common problems. In this task, I used the
\SplFileObject class to work with files as objects. I find it a lot easier than remembering and looking up the different
file_* functions, since I get autocompletion in my IDE. For this, we create a file object that writes to STDOUT so that our command will output everything to the terminal or screen. First, we create our
// we will use SPL to send our data to STDOUT formatted as CSV
$fout = new \SplFileObject("php://stdout");
Scripts write to STDOUT by default anyway, but to use the built-in
fputcsv method, we need to specify it explicitly.
Typically the first line of a CSV file is the header describing the columns that follow. Now, here's where we use
fputcsv(). This method takes in an array and then writes it as a CSV file. The method automatically handles using commas to separate fields, enclosing text fields with quotes, and so on. You can even configure how all that is handled; for example, if you need to use ';' as the separator. See the online documentation for
fputcsv for details.
// write our headers
'nid', 'type', 'title', 'date_created', 'path_alias'
Finding Nodes with EntityFieldQuery
Drupal's native
EntityFieldQuery API is a powerful alternative to always relying on Views to create and filter some collection of nodes, users, taxonomy terms, etc. It's also object-oriented (I keep saying that) and provides a very readable interface for querying Drupal's databases. It abstracts away the underlying data store for you, so you don't need to know exactly what tables or fields everything is in. For that same reason, it's much safer to use than doing a direct
One thing that is tricky at first is wrapping your head around the terms it uses. Entities have properties that are common to all the entities of that kind. For nodes, these are things like the title, created date, the bundle (content type), the status, and more. If it's fieldable, it can have fields specific to a bundle. If you need to query on property values, you use
propertyCondition. For fields, use
fieldCondition. The same pattern holds if you need to sort them by one or the other. To dig deeper, see How to Use EntityFieldQuery.
The code below shows how get all the nodes with a
created timestamp greater than the one we pass to our Drush script.
// query for nodes newer than the specified date
$query = $query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'node')
->addMetaData('account', user_load(1)) // Run the query as user 1
->propertyCondition('created', $created->format('U'), '>')
->propertyOrderBy('created', 'ASC');
Iterating Entities with Generators
Generators were introduced in PHP 5.5. They're a special kind of function that uses the
yield keyword to return an item that's part of some collection. In practice, they're a simple way to build a function that iterates over a collection without having to build the whole collection in memory first.
In the code excerpt below you'll see how we loop through the nodes returned by our
EntityFieldQuery. We do this in a generator—notice the
yield keyword.
$result = $query->execute();
if (!empty($result)) {
// $result is an array with node ids
foreach ($result['node'] as $nid => $row) {
// TRUE will reset static cache to
// keep memory usage low
$node = node_load($row->nid, null, TRUE);
if ($count < 100000) {
yield $node;
The generator function is called in a
foreach loop which terminates once the generator stops yielding values.
// use a generator to loop through nodes
foreach (nodes_generator($since) as $node) {
url('node/' . $node->nid)
With the module complete and enabled, we have a new Drush command out our disposal. We can invoke it in a terminal shell with the command below:
drush export-newest 2015-06-01
That command will output our nodes as a stream of CSV lines. To save the output in a file, just redirect it as shown below. Once you download the file, you can send it to your client and/or project manager to peruse in Excel.
drush export-newest 2015-06-01 > newest-nodes.csv
There you have it, a straightforward independent Drush command that uses built-in PHP libraries to export a collection of nodes to CSV. Of course, we could have done it with Views and some other modules, but this module is easy to version and quick to deploy. It only depends on Drupal core, so it's super low-maintenance.
For the full script, check out the gist on github
cTools is one of those critical Drupal 7 modules many others depend on. It provides a lot of APIs and functionality that makes life easier when developing modules. Views and Panels are just two examples of such powerhouses that depend on it.
cTools makes available different kinds of functionality. Object caching, configuration exportability, form wizards, dialogs and plugins are but a few. A lot of the credit you would normally attribute to Views or Panels is actually owed to cTools.
In this article, we are going to take a look at cTools plugins, especially how we can create our very own. After a brief introduction, we will immediately go hands on with a custom module that will use the cTools plugins to make defining Drupal blocks nicer (more in tune to how we define them in Drupal 8).
cTools plugins in Drupal 7 (conceptually not so dissimilar to the plugin system in Drupal 8) are meant for easily defining reusable bits of functionality. That is to say, for the ability to define isolated business logic that is used in some context. The goal is to set up that context and plugin type once, and allow other modules to then define plugins that can be used in that context automatically.
If you've been developing Drupal sites for more than a year you've probably encountered cTools plugins in one shape or form. I think the first plugin type we usually deal with is the
content_type plugin which allows us to create our own custom panel panes that display dynamic content. And that is awesome. Some of the others you may have encountered in the same realm of Panels are probably
context and
access (visibility rules). Maybe even
relationships and
arguments. These are all provided by cTools. Panels adds to this list by introducing
layouts and
styles that we normally use for creating Panels layouts and individual pane styles. These are I think the more common ones.
However, all of the above are to a certain extent a black box to many. All we know is that we need to define a hook to specify a directory and then provide an include file with some definition and logic code and the rest happens by magic. Going forward, I would like us to look into how a plugin type is defined so that if the case arises, we can create our own plugins to represent some reusable bits of functionality. To demonstrate this, we will create a module that turns the pesky hook system of defining custom Drupal blocks into a plugin based approach similar to what Drupal 8 is using.
The final code (+ a bit more) can be found in this repository if you want to follow along. And I do expect you are familiar with the steps necessary for defining custom Drupal blocks.
The block_plugin module
As I mentioned, I would like to illustrate the power of cTools plugins with a custom plugin type that makes defining Drupal 7 blocks saner. Instead of implementing the 2 main hooks (
hook_block_info() and
hook_block_view()) necessary to define a block, we'll be able to have separate plugin files each responsible for all the logic related to their own block. No more switch cases and changing the hook implementation every time we need a new block. So how do we do this?
First, let's create our
block_plugin.info file to get started with our module:
name = Block Plugin
description = Using cTools plugins to define Drupal core blocks
core = 7.x
dependencies[] = ctools
Simple enough.
The plugin type
In order to define our news plugin type, inside the
block_plugin.module file we need to implement
hook_ctools_plugin_type() which is responsible for defining new plugin types cTools will recognize:
function block_plugin_ctools_plugin_type() {
return array(
'block' => array(
'label' => 'Block',
'use hooks' => FALSE,
'process' => 'block_plugin_process_plugin'
In this hook we need to return an associative array of all the plugin type definitions we need keyed by the machine name of the plugin type name. Today we are only creating one called
block. For more information on all the options available here, feel free to consult the
plugins-creating.html help file within the cTools module. No use repeating all that information here.
process key defines a function name that gets triggered every time cTools loads for us a plugin and is responsible for shaping or massaging the plugin data before we use it. It's sort of a helper function that prepares the plugin for us each time so we don't have to bother. So let's see what we can do inside that function:
function block_plugin_process_plugin(&$plugin, $info) {
// Add a block admin title
if (!isset($plugin['admin title'])) {
$exploded = explode('_', $plugin['name']);
$name = '';
foreach ($exploded as $part) {
$name .= ucfirst($part) . ' ';
$plugin['admin title'] = $name;
// By default we also show a block title but this can be overwritten
if (!isset($plugin['show title'])) {
$plugin['show title'] = TRUE;
// Add a block view function
if (!isset($plugin['view'])) {
$plugin['view'] = $plugin['module'] . '_' . $plugin['name'] . '_view';
// Add a block form function
if (!isset($plugin['configure'])) {
$plugin['configure'] = $plugin['module'] . '_' . $plugin['name'] . '_configure';
// Add a block save function
if (!isset($plugin['save'])) {
$plugin['save'] = $plugin['module'] . '_' . $plugin['name'] . '_save';
This callback receives the plugin array as a reference and some information about the plugin type. The task at hand is to either change or add data to the plugin dynamically. So what do we achieve above?
First, if the developer hasn't defined an admin title for the block plugin, we generate one automatically based on the machine name of the plugin. This is so that we always have an admin title in the Drupal block interface.
Second, we choose to always display the title of the block so we mark the
show title key of the plugin array as TRUE. When defining the block plugin, the developer has the option of setting this to FALSE in which case we won't show a block title (subject).
Third, fourth and fifth, we generate a callback function for the block view, save and configure actions (if they haven't already been set by the developer for a given plugin). These callbacks will be used when implementing
hook_block_configure() and
hook_block_save(), respectively. We won't be covering the latter two in this article but feel free to check out the repository to see what these can look like.
And that's pretty much all we need for defining our custom plugin type. We should, however, also implement
hook_ctools_plugin_directory() which, as you may know, is responsible for telling cTools where a plugin of a certain type can be found in the current module:
function block_plugin_ctools_plugin_directory($module, $plugin) {
if ($module == 'block_plugin' && in_array($plugin, array_keys(block_plugin_ctools_plugin_type())) ) {
return 'plugins/' . $plugin;
This will need to be implemented also by any other module that wants to define
block plugins.
Drupal blocks
Now that we have the plugin type, let's write the code which turns any defined
block plugin into a Drupal block. Will start with the
hook_block_info() implementation:
function block_plugin_block_info() {
$blocks = array();
$plugins = block_plugin_get_all_plugins();
foreach ($plugins as $plugin) {
$blocks[DELTA_PREFIX . $plugin['name']] = array(
'info' => $plugin['admin title'],
return $blocks;
Here we load all of the plugins using a helper function and define the minimum required information for the block. Here you can add also more information but we are keeping it simple for brevity.
We know each plugin will have a machine name (the name of the include file basically) and an
admin title because we generate one in the processing phase if one doesn't exist. The
DELTA_PREFIX is a simple constant in which we define the prefix we want for the block machine name because we need to reuse it and should be able to easily change it if we want to:
define('DELTA_PREFIX', 'block_plugin_');
Our helper function we saw earlier looks like this:
function block_plugin_get_all_plugins() {
return ctools_get_plugins('block_plugin', 'block');
It's a simple wrapper around the respective cTools function. And for that matter, we also have the following function responsible for loading a single plugin by its machine name:
function block_plugin_get_plugin($name) {
return ctools_get_plugins('block_plugin', 'block', $name);
This is very similar to the one before.
In order to make our Drupal block definitions complete, we need to implement
function block_plugin_block_view($delta = '') {
$plugin = block_plugin_plugin_from_delta($delta);
if (!$plugin) {
$block = array();
// Optional title
if (isset($plugin['title']) && $plugin['show title'] !== FALSE) {
$block['subject'] = $plugin['title'];
// Block content
$block['content'] = $plugin['view']($delta);
return $block;
So what's happening here?
First, we use another helper function to try to load a plugin based on the delta of the current block and do nothing if we are not dealing with a plugin block.
Second, we build the block. If the user specified a
title key on the plugin and the
show title key is not false, we set the subject of the block (its title basically) as the former's value. As for the actual block content, we simply call the
view callback defined in the plugin. And that's it.
Let us quickly see also the helper function responsible for loading a plugin based on a block delta:
function block_plugin_plugin_from_delta($delta) {
$prefix_length = strlen(DELTA_PREFIX);
$name = substr($delta, $prefix_length);
$plugin = block_plugin_get_plugin($name);
return $plugin ? $plugin : FALSE;
Nothing complicated going on here.
Defining block plugins
Since we told cTools that it can find
block plugins inside the
plugins/block folder of our module, let's go ahead and create that folder. In it, we can add our first block inside a file with the
.inc extension, for example
$plugin = array(
'title' => t('This is my block'),
* Returns a renderable array that represents the block content
function block_plugin_my_block_view($delta) {
return array(
'#type' => 'markup',
'#markup' => 'Yo block!'
Like we do with all other plugins (
context, etc), the plugin definition is in the form of an array inside a variable called
$plugin. And for our case all we need at this point is a
title (and not even that since without it the block simply won't show a title).
Below it, we defined our callback function to display the block. The naming of this function is important. It matches the pattern we used for it during the processing phase (
module_name_plugin_name_view). If we want to name it differently, all we have to do is reference the function name in the
view key of the
$plugin and it will use that one instead.
And that is basically it. We can now clear our caches and go to the Block administration screen where we can find our block and add it to a region. Showing that block on the page should trigger the
view callback for that block plugin and render the contents.
In this article we've talked a bit about cTools plugins and saw how we can define our own type of plugin. We used the latter for transforming the Drupal block system into a rudimentary plugin system. This can be extended further to allow also for the block related configuration hooks to be replaced by callbacks inside the plugin include file. Additionally, as mentioned earlier, you can also make sure all the data available in
hook_block_info() can be defined inside the plugin. I leave these tasks up to you.
Meet the authorDaniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
In the first installment of this series we started our journey towards creating some simple but powerful functionality. The goal we set was to have the possibility to load a form on each node page and to be able to choose which form type should be used on the different node bundles.
The first step is done. We created a custom plugin type called
ReusableForm already featured with a base plugin class that new plugins can extend. Additionally, we saw that each plugin will interact with a form class that is defined in their annotation. And like with the plugins, we also created a base class new forms can extend.
It follows to see how we can configure the core node types to use one of the plugins defined on the site and how to render the relevant form when viewing the node. But first, in order to have something to work with, let's create our first
ReusableForm plugin that uses a very simple form.
Our first plugin
Inside the
src/Form folder of our module, create a class called
BasicForm.php (or whatever you want to call it). Inside, we can have this simple form definition:
namespace Drupal\reusable_forms\Form;
use Drupal\Core\Form\FormStateInterface;
* Defines the BasicForm class.
class BasicForm extends ReusableFormBase {
* {@inheritdoc}.
public function getFormId() {
return 'basic_form';
* {@inheritdoc}.
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);
return $form;
* {@inheritdoc}
public function submitForm(array &$form, FormStateInterface $form_state) {
// Handle form submission.
This form class extends our form base and implements all the required methods. For more information on forms and how they work in Drupal 8, you can check out one of my previous articles on Sitepoint.com. But as you can see, we are calling the parent
buildForm() method so that the logic we defined in the base class takes place. The rest is implementation detail (and here we don't do much). But you can perform whatever logic you want and handle the submissions in any way you need.
This form can now be used with a plugin of our type and it will have 3 form elements inherited from the form base class.
Next, let's create our first plugin. Inside the
src/Plugin/ReusableForm folder of our module, we can have a file called
BasicForm.php with the following:
namespace Drupal\reusable_forms\Plugin\ReusableForm;
use Drupal\reusable_forms\ReusableFormPluginBase;
* Provides a basic form.
* @ReusableForm(
* id = "basic_form",
* name = @Translation("Basic Form"),
* form = "Drupal\reusable_forms\Form\BasicForm"
* )
class BasicForm extends ReusableFormPluginBase {}
As you can see, all we need for our purposes is in the annotation: the id, name and form class to be used with this plugin. You can add your own methods, override existing ones and handle way more complex logic if you want, though. But for us this is ok as the
ReusableFormPluginBase class handles the form building already.
And that's it. This is all it takes to now define new
ReusableForm plugins. We create a form class with all the fields and logic it needs and then reference it inside a simple plugin class.
Node type configuration
Now that we have a ReusableForm plugin, we can proceed with configuring the node type entities to make use of it. For this, we'll need to first turn our plugin manager into a service so that we can access it and load plugins.
Inside the
reusable_forms.services.yml file of our module we can have this:
class: Drupal\reusable_forms\ReusableFormsManager
parent: default_plugin_manager
Now we'll be able to access the plugin manager using the
plugin.manager.reusable_forms service id. And by using the
parent key in the definition, we specified that all its dependencies can be looked up from the parent. That's cool!
Next, let's turn to our
.module file and do a couple of things in there. First, we want to alter the node type edit form and add some extra information about our form plugins. This is so that when users edit a node bundle they can select which form plugin should be used with the nodes of that type:
* Implements hook_form_BASE_FORM_ID_alter().
function reusable_forms_form_node_type_form_alter(&$form, FormStateInterface $form_state) {
$form['reusable_forms'] = array(
'#type' => 'details',
'#title' => t('Reusable forms'),
'#group' => 'additional_settings',
// Load the current node type configuration entity.
$node_type = $form_state->getFormObject()->getEntity();
$form['reusable_forms']['reusable_forms_enabled'] = array(
'#type' => 'checkbox',
'#title' => t('Enable reusable forms'),
'#description' => t('Check this box if you would like a reusable form on this node type.'),
'#default_value' => $node_type->getThirdPartySetting('reusable_forms', 'enabled', 0),
$form_plugins = \Drupal::service('plugin.manager.reusable_forms')->getDefinitions();
$options = [];
foreach ($form_plugins as $name => $plugin) {
$options[$name] = $plugin['name'];
$form['reusable_forms']['reusable_forms_enabled'] = array(
'#type' => 'radios',
'#title' => t('Available forms'),
'#default_value' => $node_type->getThirdPartySetting('reusable_forms', 'plugin', 'basic_form'),
'#options' => $options,
'#description' => t('The available forms you can choose from for this node type.'),
'#states' => array(
'visible' => array(
':input[name="reusable_forms_enabled"]' => array('checked' => TRUE),
$form['#entity_builders'][] = 'reusable_forms_form_node_type_form_builder';
hook_form_BASE_FORM_ID_alter will do the trick perfectly for this. Though we mustn't forget to use the
FormStateInterface class at the top:
use Drupal\Core\Form\FormStateInterface;
So what happens here? We are creating a new fieldset to group two form fields relevant for our purpose: a checkbox to enable the reusable forms and a select list to choose from the existing plugins. As options to the latter we are building an array of all the plugin names after we load our plugin manager and request from it all the available definitions. And using the
#states magic we make sure that this latter field is only visible if the checkbox to enable the forms is checked.
Right at the end we are adding an extra callback function to the
#entity_builders group that will be triggered when the entity is saved and that has the purpose of mapping values to an entity. So let's see that function now:
* Entity form builder for the node type form to map some values to third party
* settings
function reusable_forms_form_node_type_form_builder($entity_type, NodeTypeInterface $type, &$form, FormStateInterface $form_state) {
if ($form_state->getValue('reusable_forms_enabled') === 1) {
$type->setThirdPartySetting('reusable_forms', 'enabled', 1);
$type->setThirdPartySetting('reusable_forms', 'plugin', $form_state->getValue('reusable_forms_enabled'));
$type->unsetThirdPartySetting('reusable_forms', 'enabled');
$type->unsetThirdPartySetting('reusable_forms', 'plugin');
And again, let's make use of the
NodeTypeInterface at the top:
use Drupal\node\NodeTypeInterface;
In this function we are doing something simple but awesome. If the admin has enabled the use of reusable forms on this bundle, we make use of the configuration entity's third party settings space to store our data. Otherwise we simply unset it if it exists.
I purposefully ignored this last point when talking about the form alter we implemented before. Now you can see how the form element default values are populated by making a request to the third party settings object on the node type configuration entity.
Configuration schema
Before moving on to actually displaying the forms, we need to also add the schema definition for the new configuration we are storing. Inside the
config/schema/reusable_forms.schema.yml file of our module we can add the following:
type: mapping
label: 'Reusable Forms'
type: boolean
label: 'Whether to enable the reusable forms on this node type'
type: sequence
label: 'Available forms'
type: string
label: 'Plugin name'
Node view
Now that we are storing the user preferences on the node type config entity, let's see how we can render our chosen form plugins on the node pages of the enabled types.
The first thing we're going to do is define a content entity pseudo field that will be configurable from the node Manage display interface. Still inside our
.module file we can have this:
* Implements hook_entity_extra_field_info().
function reusable_forms_entity_extra_field_info() {
$extra = array();
$bundles = NodeType::loadMultiple();
$bundles = array_filter($bundles, function($bundle){
return $bundle->getThirdPartySetting('reusable_forms', 'enabled') === 1;
foreach ($bundles as $bundle) {
$extra['node'][$bundle->Id()]['display']['reusable_form'] = array(
'label' => t('Reusable form'),
'description' => t('Reusable form'),
'weight' => 100,
'visible' => TRUE,
return $extra;
And again we have to use
NodeType at the top:
use Drupal\node\Entity\NodeType;
What happens here is simple. We load all the node bundles and filter out the ones for which the reusable forms are not enabled. Then for each one we define an extra
display component in a very self-explanatory way.
By clearing the cache will be able to already see the new pseudo field in place on the configuration page – though it won't yet do anything. Which brings us to the last piece of the puzzle, rendering the relevant form.
To make the pseudo field we just defined useful, we need to implement
hook_entity_view (or a variant of it) and render its content:
* Implements hook_ENTITY_TYPE_view().
function reusable_forms_node_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode) {
if ($display->getComponent('reusable_form')) {
$plugin_manager = \Drupal::service('plugin.manager.reusable_forms');
$node_type = NodeType::load($entity->bundle());
$plugin = $node_type->getThirdPartySetting('reusable_forms', 'plugin');
if (!$plugin) {
$build['reusable_form'] = $plugin_manager->createInstance($plugin)->buildForm($entity);
And let's not forget the use statements at the top:
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
First, we check if the
reusable_form component exists on this node's display (whether it is made visible in the UI). If it is, we add the
reusable_form key to the render array that is building this node view.
We start by loading the plugin manager and the node type configuration object of the current node entity. Then we use the former to instantiate a plugin with the ID defined in the third party settings of the latter. And in doing so, as you remember, we are passing as argument the current entity object so that the form being built can be aware of the entity it is showing up with (though we are not really taking advantage of this in our example).
And that's it. What's left is to install the module from scratch (since we added the config schema), edit a node type and select our default plugin (or another one if you created it). Then you can configure the display of this node type to show the reusable form which will then do so when viewing a node of that type. Neat.
As we've seen in this article series, Drupal 8 provides us with the tools to do some powerful stuff. With the plugin system we set up the basis for our reusable functionality. And then we hooked into various points of Drupal core to make use of this logic. We've seen how any configuration entity that implements the
ThirdPartySettingInterface can be extended to include new data. And lastly, we've displayed relevant data when viewing content entities with the help of pseudo fields.
But this is just the beginning. You can take what we did here and extend it to your needs. For instance, you can create a new content entity type that will model form submissions. The possibilities are endless.
Meet the authorDaniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.
Reusable Forms in Drupal 8
- Drupal 8 Custom Plugin Types
Drupal 8 comes with a great addition to the backend developer toolkit in the form of the plugin system. Completely new, specific to Drupal and evolved from serving only a few specific purposes, plugins have become the go-to system for reusable functionality in Drupal 8.
In this article series of two parts, we will use this system to build a feature that allows the use of custom forms together with node entities. After we're done, we'll be able to do the following:
- configure node bundles to use one of multiple form types to be displayed together with the node display
- easily define new form types by extending from a sensible base class
Because the topic is very well covered elsewhere, I will not go into the details of how plugins work. But do feel free to brush up on the theory before diving into the crux of it here. And if you want to take a look at the end result, the code we write in both articles can be found in this repository.
We will get started by creating our custom plugin type. To this end, we will have 2 interfaces and 6 classes. It sounds like much, but I assure you they are rather boilerplate and quick to set up. Then, in the next installment of this series, we will see how to use it for our reusable forms attached to nodes.
Plugin manager
Responsible for discovering and loading plugins, the most important part in any plugin type is the manager. However, it's very simple to create one because Drupal already provides us with a sensible default base to extend. So in our module's
/src folder we can have this class inside a
ReusableFormManager.php file (the de facto name of our plugin type becoming
namespace Drupal\reusable_forms;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
class ReusableFormsManager extends DefaultPluginManager {
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct('Plugin/ReusableForm', $namespaces, $module_handler, 'Drupal\reusable_forms\ReusableFormPluginInterface', 'Drupal\reusable_forms\Annotation\ReusableForm');
$this->setCacheBackend($cache_backend, 'reusable_forms');
As I mentioned, our manager extends the
DefaultPluginManager class and just overrides the constructor to call the parent one with some important information about our plugin type:
Plugin/ReusableForm– the subdirectory where plugins of this type will be found within any module
Drupal\reusable_forms\ReusableFormPluginInterface– the interface each of our plugins will need to implement
Drupal\reusable_forms\Annotation\ReusableForm– the annotation class that will define our plugin properties (such as ID, name, etc.)
Additionally, we create an alter hook which can be implemented by various modules to alter the plugin definitions and we set a key for our plugins in the cache backend. For more information about plugin managers, what they do and how they are set up, you should consult the documentation page available on Drupal.org.
Plugin interface
Next, let's create that interface the manager expects all our plugins to implement. Inside a file called
ReusableFormPluginInterface.php located in the
src/ folder of our module, we can have this:
namespace Drupal\reusable_forms;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Component\Plugin\PluginInspectionInterface;
interface ReusableFormPluginInterface extends PluginInspectionInterface, ContainerFactoryPluginInterface {
* Return the name of the reusable form plugin.
* @return string
public function getName();
* Builds the associated form.
* @param $entity EntityInterface.
* The entity this plugin is associated with.
* @return array().
* Render array of form that implements \Drupal\reusable_forms\Form\ReusableFormInterface
public function buildForm($entity);
This is a very simple interface that enforces only two methods:
getName() and
buildForm(). The first will return the name of the plugin while the latter is expected to be passed an entity object and to return a render array of a form definition that implements
\Drupal\reusable_forms\Form\ReusableFormInterface (the interface we will set up for our actual forms). You'll also notice that we are extending two other interfaces. Those provide us with some extra helpful methods and allow us to inject dependencies from the container.
Plugin annotation
As defined in the manager, let's also set up our annotation class inside
namespace Drupal\reusable_forms\Annotation;
use Drupal\Component\Annotation\Plugin;
* Defines a reusable form plugin annotation object.
* @Annotation
class ReusableForm extends Plugin {
* The plugin ID.
* @var string
public $id;
* The name of the form plugin.
* @var \Drupal\Core\Annotation\Translation
* @ingroup plugin_translatable
public $name;
* The form class associated with this plugin
* It must implement \Drupal\reusable_forms\Form\ReusableFormInterface.
* @var string
public $form;
Here we simply extend the default
Plugin annotation class and define three properties (id, name and form). These will be the three keys found in the annotation of our individual plugins (we'll see an example in part two of this series).
Plugin base
So far we have the core of what we need for our plugin type: a plugin manager that can discover new plugins using the annotation we defined and instantiate them with its default factory, i.e. the Container Factory.
Let us now lay the ground work for the plugins themselves by creating a base class all the plugins can/should/will extend. Inside the
src/ folder of our module we can create a new file called
ReusableFormPluginBase.php with the following abstract class inside:
namespace Drupal\reusable_forms;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Form\FormBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
abstract class ReusableFormPluginBase extends PluginBase implements ReusableFormPluginInterface {
* The form builder.
* @var \Drupal\Core\Form\FormBuilder.
protected $formBuilder;
* Constructs a ReusableFormPluginBase object.
* @param array $configuration
* @param string $plugin_id
* @param mixed $plugin_definition
* @param FormBuilder $form_builder
public function __construct(array $configuration, $plugin_id, $plugin_definition, FormBuilder $form_builder) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->formBuilder = $form_builder;
* {@inheritdoc}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
* {@inheritdoc}
public function getName() {
return $this->pluginDefinition['name'];
* {@inheritdoc}
public function buildForm($entity) {
return $this->formBuilder->getForm($this->pluginDefinition['form'], $entity);
There are a few things to note here. First, we are extending from the plugin base class provided by Drupal so that we get some useful functionality (the methods of
PluginInspectionInterface are already implemented with sensible defaults). Second, we are implementing the interface we defined earlier. All of our plugins need to implement it so might as well take care of it here. Third, we are using dependency injection to load from the container the
form_builder service we will need to build our forms. This is possible because our interface extends from the
For more information about the service container and dependency injection in Drupal 8, check out one of my previous articles on Sitepoint.com.
As our interface dictates, we already take care of implementing the two methods right here in our base class. The
getName() method will simply return the name of the plugin as defined in the plugin annotation. The
buildForm() method, on the other hand, will use the form builder to build a form. For this it will use the class provided in the plugin annotation's
form key (which needs to be the fully qualified name of a class that implements our Form interface which we haven't defined yet). In doing so, we also pass the
$entity argument to the form (whatever that may be as long as it implements
EntityInterface). This is so that the form that is being rendered on the node page becomes aware of the node it is being rendered with.
Form Interface
Our plugin type is pretty much finished. We can now provide some sensible defaults to the forms that will be used by these plugins. Inside
src/Form/ReusableFormInterface.php we can have this simple interface:
namespace Drupal\reusable_forms\Form;
use Drupal\Core\Form\FormInterface;
interface ReusableFormInterface extends FormInterface {}
We are doing nothing here except for extending from the default Drupal
FormInterface. We have it so that we can add whatever methods we need our forms to implement (currently none) and are able to identify forms that implement this interface as compatible with our plugins.
More from this author
- From Request to Response: A Journey into Drupal 8 Internals
- Drupal 8 Theming Revamped - Updates and New Features
Form base
Now that we have a form interface to implement, let's also create a form base class that the rest of the forms can extend. Inside
src/Form/ReusableFormBase.php we can have the following abstract class:
namespace Drupal\reusable_forms\Form;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
* Defines the ReusableFormBase abstract class
abstract class ReusableFormBase extends FormBase implements ReusableFormInterface {
* @var EntityInterface.
protected $entity;
* {@inheritdoc}.
public function buildForm(array $form, FormStateInterface $form_state) {
$build_info = $form_state->getBuildInfo();
if ($build_info['args'] && $build_info['args'][0] instanceof EntityInterface) {
$this->entity = $build_info['args'][0];
$form['first_name'] = array(
'#type' => 'textfield',
'#title' => $this->t('First name'),
$form['last_name'] = array(
'#type' => 'textfield',
'#title' => $this->t('Last name'),
$form['email'] = array(
'#type' => 'email',
'#title' => $this->t('Email'),
$form['actions']['#type'] = 'actions';
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Submit'),
'#button_type' => 'primary',
return $form;
As you can see, we are implementing the interface but we are also extending from the default Drupal
FormBase class. And in this example we only have the
buildForm method that returns a simple form with three fields and a submit button. You can do whatever you want here in terms of what you consider a good base form. Additionally though, at the beginning of this method, we are checking if an
EntityInterface object has been passed as an argument when this form is built and setting it as a protected property. The classes extending this will be able to make use of this entity as long as they call the
parent::buildForm() method first.
In the first part of this article series we focused on setting up our custom plugin type and getting it ready for use. In doing so we quickly went through this process and saw how our plugins are expected to work with the actual form classes. In the next part we will work on making it possible to display them together with the nodes. This means adding extra configuration to the node type entities and displaying the forms using pseudo fields managed as part of the regular content view modes. Stay tuned!
In the first article on Drupal 8 module development we looked a bit at the routing aspect of this process. We've seen that creating pages with paths is now a matter of declaring routes that match up with controllers. The latter, as we've seen, can return a render array that gets interpreted into markup and displayed in the main content area of that page. However, did you know that under the hood, Drupal actually transforms that array into a Response object according to the dictates of Symfony's HTTPKernelInterface?
In this article, I would like us to go deeper into the internals of Drupal 8 (and Symfony2) and look at what actually happens (and can happen) from the moment a request is made by a user to the one in which they see something returned in response. The example I mentioned above is just one direction this process can go in, and today we are also going to see other possibilities. The goal is to understand the flexibility of the system which in turn can help us build awesome applications.
Before going into it, I strongly recommend you check out this diagram which does an amazing job at synthesizing what is often referred to as the render pipeline. Though in my opinion it represents more than the name implies because the render system is only part of what's depicted, albeit a big one.
Front controller (index.php)
It's no secret that Symfony2 is now an important part of Drupal. The latter uses many of Symfony's components, most importantly for this article the HTTPKernel and HTTPFoundation ones. Together they are responsible for encapsulating a user request, passing it to the application and then returning whatever comes back to the user in a consistent and OO way.
The HTTPKernelInterface (something you probably heard about also from other contexts) is what glues all of this together by taking in a Request object and always returning a Response one. A very simple but powerful concept.
This process is initiated inside the
index.php file which starts by generating said Request object and passing it to the
HTTPKernel::handle() method. The latter is then responsible for returning a Response object. At a high level, this is what happens both in a Drupal application as well as in a Symfony one (or any other that leverages the HTTPKernel component).
HTTPKernel and events
HTTPKernel is the heart of any Symfony based application. Its
handle() method, as we saw, has a great deal of responsibility in preparing a response and it does so in a workflow driven by events. This makes for a very flexible application where the heavy lifting is always delegated to listeners of these events.
If you look at the diagram from earlier, you can see this workflow depicted in the second column, and it essentially represents the glue between Symfony and the Drupal side of things.
It starts with the first event called
kernel.request. Subscribers to this event handle various tasks. But two very important ones in Drupal 8 are the format negotiation and routing. The first determines the type of response that needs to be returned (html, json, image, pdf, etc) while the second determines what the code responsible for handling this is (the
_controller key of a route definition inside the
routing.yml file). But like in most steps in this event workflow, if a listener returns a response object, the process skips most of the further steps (stops propagation) and goes straight to
The second event is
kernel.controller which is called after the application knows which controller is responsible for handling the request. At this point, listeners can still perform some overriding operations on it. Closely following this step, the Kernel is responsible for resolving the arguments that get passed to the controller. One such operation in Drupal is loading objects based on IDs found in the request (for example nodes) and directly providing the controller with them. Then finally the controller gets called with the respective parameters.
The controller is responsible for returning a response of some kind. If it returns a
Response object, the process skips to the
kernel.response event. Listeners to the latter can perform last minute modifications on the object such as modifying headers or the content itself. And after getting it from the
handle() method, the front controller uses the
send() method on the
Response object to send it back to the user and terminates the process.
Going deeper with render arrays
If the controller does not return a
Response object, the Kernel fires one last event:
kernel.view. Its subscribers are responsible for turning the result of the controller into an actual
Response object. So this means that you have the option of returning from your controller any kind of object as long as you couple it with a VIEW event subscriber that turns that into a proper
However, as we've seen in the example, most of the time controllers will return a render array. Usually this represents the page's main content (similar to page callbacks in Drupal 7).
To handle this, Drupal 8 has a MainContentViewSubscriber responsible for transforming this array into proper
Response objects. It does so by using a particular MainContentRenderer chosen during the format negotiation phase we've talked about before. And although there are a few such renderers already available, the default one used is the HtmlRenderer.
Since this is the most commonly used type of main content renderer, let's go in a bit deeper and see how this builds the page.
One of the cool things about this step in the process is the concept of page variants.
This means that
HTMLRenderer dispatches an event responsible for finding out which type of page is to be used to wrap the main content render array:
RenderEvents::SELECT_PAGE_DISPLAY_VARIANT. By default, the SimplePageVariant is used unless the Block module is enabled. In that case the BlockPageVariant kicks in and allows the placement of the blocks in the regions around the main content. If you want, you can subscribe to this event in your own module and provide your own variant.
All of this happens within the
prepare() method of the
HTMLRenderer which supplies the
renderResponse() method with a
#type => 'page' render array that wraps the main content one. The latter two get in turn wrapped into a
#type => 'html' render array which gets finally rendered using the Renderer class (the equivalent of
drupal_render() in Drupal 7). The resulting HTML string gets added to the
Response object and gets returned to the front controller.
Although this is a very high level overview of the process, this is basically what happens. Now we have a
Response object which means the Kernel can dispatch its
kernel.response event. And right after this, the front controller can send the
Response back to the user and terminate the process.
In this article we've taken a journey into Drupal 8 (and Symfony2) internals by following the pipeline from a user request to the response the server returns. We've seen how Drupal 8 leverages the HTTPKernel and HTTPFoundation Symfony2 components and how it basically lives on top of them. Additionally, we've seen how the glue between them is made up of the events the Kernel dispatches to which Drupal subscribes for all of its functionality. Finally, we've seen how HTML pages are built and returned to the user with the help of the render pipeline.
I believe that understanding what is going on under the hood in a Drupal 8 application will allow you to create awesome applications by knowing exactly which entry points you have into this flow. And I believe that if you take away only one thing from this article, it should be the word flexibility. Because the flexibility for building exactly what we need in Drupal 8 far surpasses anything in Drupal 7. It has truly become modern.
If you are a Drupal developer who has dabbled in theming older versions of Drupal (5, 6, 7) you understand why frustration is the trusty companion of any Drupal themer. Luckily, though, Drupal 8 promises so many improvements that even the Angry Themer is happy for a change. It is only natural we jump in and start looking at what these improvement are.
In this article, we will look at some of the more important changes to theming in Drupal 8. Although we will keep things simple and start from the basics, I do assume you have at least a bit of experience with theming in Drupal 7. And since theming is a big subject and this is just an introduction, you'll find all sorts of links to more information that can help you out.
Starting up
As with custom modules, a new theme always starts with a folder and the obligatory .info.yml file inside (as opposed to the old
.info file). These go in the root
themes folder of the Drupal codebase (as opposed to the old
sites/all/themes directory) and they should both have the same name (e.g.
my_theme.info.yml within the
my_theme folder).
Inside the
.info.yml file we provide a few required keys:
name: Theme name
description: Theme description
type: theme
core: 8.x
All the rest are optional and come as needed. With this in place, you can already navigate to
admin/appearance and enable the new theme or set it as default. You'll notice that from the good 'ol Bartik you now have a really naked theme in place. But the big difference from Drupal 7 is that you can easily start theming every aspect of your website.
A major improvement is that we now have an intermediary core theme called Classy which bridges the gap between the data output by the Drupal backend (usually the
system module) and the actual themes. So what all the other available core themes do (and what you can as well) is use Classy as their base theme and override its templates:
base theme: classy
Alternatively, you can also not do this but copy all of its template files into your theme and you'll end up at the same place. But you probably won't need all those files (some might not need changing) so in my opinion it's just better to use Classy as a base theme and just override what you need.
Although I won't go into details, region definition is also quite an important aspect of a theme's
info.yml file:
header: 'Header'
content: 'Content'
footer: 'Footer'
With this in place, inside your
page.html.twig template file you can print these regions out like so:
{{ page.header }}
This is the Twig templating syntax you should start getting familiar with if you haven't already.
By now I think everybody knows that Twig is the templating language used in Drupal 8. I won't go into it too much because there are many resources out there with plenty of information about how Twig syntax can make themers forget all about PHPTemplate in no time.
But one important thing to keep in mind is that there are no more theme functions in Drupal 8. This means that all themable output is run through an
html.twig file. We can still use hook_theme() to define reusable theme implementations, but they will now all use Twig files. And the cool thing is that these Twig templates are extensible. This means they can define only the necessary bits related to them and inherit the rest from their parent. Check out the Twig extends documentation for more information on what I mean.
I mentioned before how in Drupal 8 we are in full control over the markup of our site due to everything being neatly organised in template files within the Classy theme. So one of the next steps in building your theme would be overriding the
html.html.twig and/or
page.html.twig files to provide markup for your pages. Doing so, you get to play with semantic HTML5 markup because that is what Drupal 8 outputs by default.
All of these template files have documentation at the top with information about what variables you have available. Additionally, you can make use of various Twig filters and functions to manipulate this data straight from the template files. It is in fact recommended, for instance, to build translatable strings or urls straight in there rather than the preprocessor in order to maybe avoid unnecessary function calls (if these don't actually end up being printed).
And speaking of preprocess functions, they do still exist. However, there is no longer a
template.php file to put them in but rather a
.theme PHP file (
my_theme.theme) inside the root theme folder.
An interesting note about preprocessors is that in Drupal 8 we have to try to always prepare render arrays for the template file variables rather than final markup as we often do in Drupal 7. Of course, this is only if the data needs to be rendered and is not already just a simple string or something primitive like that. The point is to no longer call
drupal_render() within our preprocessor functions but let the Twig magic handle that for us.
There are many improvements made also to debugging of theme information. By turning on Twig debugging (
debug: true) inside the
sites/defaults/services.yml file, you get a bunch of helpful HTML comments printed in the page source.
These allow you to see which template file is responsible for a particular piece of markup, where it is located and what theme suggestions you can use to override it. No more spending hours trying to figure out what to override. This is a great win!
Additionally, Twig comes with the dump() function which allows you to print out a particular variable to the page from Twig. However, if that is not enough for you, the Devel module comes with the
kint() function that provides a better (traversable) variable inspection tool. This is the new Krumo.
Assets and libraries
The last thing we are going to cover here is the topic of assets (CSS and JavaScript files). A notable change is that Drupal 8 ships with jQuery 2.x alongside other modern frontend libraries like Modernizr, Backbone.js and Underscore.js. And if you ever had to work with jQuery in Drupal 7 you'll understand why this is no small gain. Plus this means that IE8 and lower are no longer supported!
Additionally, Drupal 8 has adopted a SMACSS based CSS file organization and we have some [good architecture and best practices] (https://www.drupal.org/node/1887918#best-practices) in place as well. No more excuses for messy CSS in our theme!
One thing that trips people up in the beginning is that, for performance reasons, assets are no longer added indiscriminately to every page. So if that Ajax functionality you're trying out doesn't work, make sure Drupal has loaded the necessary scripts for it. You can do so by declaring them as dependencies to your own.
What's great is that we now have a unified way of doing all of this across the entire development spectrum. We use libraries that contain both javascript and css files, which can have dependencies on other assets and which get
#attached to render arrays. If you want them loaded on all pages, you can also add them to your theme's
info.yml file or implement hook_page_attachments() and add them like that. However, it is recommended to always attach libraries to render arrays to make sure your assets don't get loaded unless they are really needed and that they are cached properly with the data they serve.
In this article we've looked at some of the more prominent changes to theming in Drupal 8. We've done so by taking a look at the starting point from which we create new themes and moved through some of the main topics related to this process. By no means, however, has this been a complete rundown of all the changes. I recommend keeping up to date with the documentation on Drupal.org (which is also constantly updating) and jump in the code yourself. It should be fun!
In this article, we are going to look at building a multistep form in Drupal 8. For brevity, the form will have only two steps in the shape of two completely separate forms. To persist values across these steps, we will use functionality provided by Drupal's core for storing temporary and private data across multiple requests.
In Drupal 7, a similar approach can be achieved using the cTools object cache. Alternatively, there is the option of persisting data through the
$form_state array as illustrated in this tutorial.
The code we write in this article can be found in this repository alongside much of the Drupal 8 work we've been doing so far. We will be dealing with forms quite a lot so I do recommend checking out one of the previous articles on Drupal 8 in which we talk about forms.
The plan
As I mentioned above, our multistep form will consist of two independent forms with two simple elements each. Users will be able to fill in the first one and move to the second form where they can either go back to the previous step or fill it in and press submit. While navigating between the different steps, the previously submitted values are stored and used to pre-populate the form fields. If the last form is submitted, however, the data gets processed (not covered in this article) and cleared from the temporary storage.
Technically, both of these forms will inherit common functionality from an abstract form class we will call
MultistepFormBase. This class will be in charge of injecting the necessary dependencies, scaffolding the form, processing the end result and anything else that is needed and is common to both.
We will group all the form classes together and place them inside a new folder called
Multistep located within the
Form plugin directory of our
demo module (next to the old
DemoForm). This is purely for having a clean structure and being able to quickly tell which forms are part of our multistep form process.
The code
We will start with the form base class. I will explain what is going on here after we see the code.
/** * @file * Contains \Drupal\demo\Form\Multistep\MultistepFormBase. */ namespace Drupal\demo\Form\Multistep; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\SessionManagerInterface; use Drupal\user\PrivateTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; abstract class MultistepFormBase extends FormBase { /** * @var \Drupal\user\PrivateTempStoreFactory */ protected $tempStoreFactory; /** * @var \Drupal\Core\Session\SessionManagerInterface */ private $sessionManager; /** * @var \Drupal\Core\Session\AccountInterface */ private $currentUser; /** * @var \Drupal\user\PrivateTempStore */ protected $store; /** * Constructs a \Drupal\demo\Form\Multistep\MultistepFormBase. * * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory * @param \Drupal\Core\Session\SessionManagerInterface $session_manager * @param \Drupal\Core\Session\AccountInterface $current_user */ public function __construct(PrivateTempStoreFactory $temp_store_factory, SessionManagerInterface $session_manager, AccountInterface $current_user) { $this->tempStoreFactory = $temp_store_factory; $this->sessionManager = $session_manager; $this->currentUser = $current_user; $this->store = $this->tempStoreFactory->get('multistep_data'); } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('user.private_tempstore'), $container->get('session_manager'), $container->get('current_user') ); } /** * {@inheritdoc}. */ public function buildForm(array $form, FormStateInterface $form_state) { // Start a manual session for anonymous users. if ($this->currentUser->isAnonymous() && !isset($_SESSION['multistep_form_holds_session'])) { $_SESSION['multistep_form_holds_session'] = true; $this->sessionManager->start(); } $form = array(); $form['actions']['#type'] = 'actions'; $form['actions']['submit'] = array( '#type' => 'submit', '#value' => $this->t('Submit'), '#button_type' => 'primary', '#weight' => 10, ); return $form; } /** * Saves the data from the multistep form. */ protected function saveData() { // Logic for saving data goes here... $this->deleteStore(); drupal_set_message($this->t('The form has been saved.')); } /** * Helper method that removes all the keys from the store collection used for * the multistep form. */ protected function deleteStore() { $keys = ['name', 'email', 'age', 'location']; foreach ($keys as $key) { $this->store->delete($key); } } }
Our abstract form class extends from the default Drupal
FormBase class so that we can use some of the functionality made available by it and the traits it uses. We are using dependency injection to inject some of the needed services:
PrivateTempStoreFactorygives us a temporary store that is private to the current user (
PrivateTempStore). We will keep all the submitted data from the form steps in this store. In the constructor, we are also immediately saving the
storeattribute which contains a reference to the
multistep_datakey/value collection we will use for this process. The
get()method on the factory either creates the store if it doesn't exist or retrieves it from the storage.
- The
SessionManagerallows us to start a session for anonymous users.
- The
CurrentUserallows us to check if the current user is anonymous.
Inside the
buildForm() method we do two main things. First, we start a session for anonymous users if one does't already exist. This is because without a session we cannot pass around temporary data across multiple requests. We use the session manager for this. Second, we create a base submit action button that will be present on all the implementing forms.
saveData() method is going to be called from one or more of the implementing forms and is responsible with persisting the data from the temporary storage once the multistep process is completed. We won't be going into the details of this implementation because it depends entirely on your use case (e.g. you can create a configuration entity from each submission). We do, however, handle the removal of all the items in the store once the data has been persisted. Keep in mind though that these types of logic checks should not be performed in the base class. You should defer to a dedicated service class as usual, or use a similar approach.
Now it's time for the actual forms that will represent steps in the process. We start with the first class inside a file called
/** * @file * Contains \Drupal\demo\Form\Multistep\MultistepOneForm. */ namespace Drupal\demo\Form\Multistep; use Drupal\Core\Form\FormStateInterface; class MultistepOneForm extends MultistepFormBase { /** * {@inheritdoc}. */ public function getFormId() { return 'multistep_form_one'; } /** * {@inheritdoc}. */ public function buildForm(array $form, FormStateInterface $form_state) { $form = parent::buildForm($form, $form_state); $form['name'] = array( '#type' => 'textfield', '#title' => $this->t('Your name'), '#default_value' => $this->store->get('name') ? $this->store->get('name') : '', ); $form['email'] = array( '#type' => 'email', '#title' => $this->t('Your email address'), '#default_value' => $this->store->get('email') ? $this->store->get('email') : '', ); $form['actions']['submit']['#value'] = $this->t('Next'); return $form; } /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { $this->store->set('email', $form_state->getValue('email')); $this->store->set('name', $form_state->getValue('name')); $form_state->setRedirect('demo.multistep_two'); } }
This form will look something like this:
In the
buildForm() method we are defining our two dummy form elements. Do notice that we are retrieving the existing form definition from the parent class first. The default values for these fields are set as the values found in the store for those keys (so that users can see the values they filled in at this step if they come back to it). Finally, we are changing the value of the action button to
Next (to indicate that this form is not the final one).
In the
submitForm() method we save the submitted values to the store and then redirect to the second form (which can be found at the route
demo.multistep_two). Keep in mind that we are not doing any sort of validation here to keep the code light. But most use cases will call for some input validation.
Since we've touched upon the issue of routes, let's update the route file in our
demo module and create two new routes for our forms:
demo.multistep_one: path: '/demo/multistep-one' defaults: _form: '\Drupal\demo\Form\Multistep\MultistepOneForm' _title: 'First form' requirements: _permission: 'access content' demo.multistep_two: path: '/demo/multistep-two' defaults: _form: '\Drupal\demo\Form\Multistep\MultistepTwoForm' _title: 'Second form' requirements: _permission: 'access content'
For more information about what is going on in this file you can read one of the previous Drupal 8 articles which explain routes as well.
Finally, we can create our second form (inside a file called
/** * @file * Contains \Drupal\demo\Form\Multistep\MultistepTwoForm. */ namespace Drupal\demo\Form\Multistep; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; class MultistepTwoForm extends MultistepFormBase { /** * {@inheritdoc}. */ public function getFormId() { return 'multistep_form_two'; } /** * {@inheritdoc}. */ public function buildForm(array $form, FormStateInterface $form_state) { $form = parent::buildForm($form, $form_state); $form['age'] = array( '#type' => 'textfield', '#title' => $this->t('Your age'), '#default_value' => $this->store->get('age') ? $this->store->get('age') : '', ); $form['location'] = array( '#type' => 'textfield', '#title' => $this->t('Your location'), '#default_value' => $this->store->get('location') ? $this->store->get('location') : '', ); $form['actions']['previous'] = array( '#type' => 'link', '#title' => $this->t('Previous'), '#attributes' => array( 'class' => array('button'), ), '#weight' => 0, '#url' => Url::fromRoute('demo.multistep_one'), ); return $form; } /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { $this->store->set('age', $form_state->getValue('age')); $this->store->set('location', $form_state->getValue('location')); // Save the data parent::saveData(); $form_state->setRedirect('some_route'); } }
This one will look like this, again very simple:
Again, we are extending from our base class like we did with the first form. This time, however, we have different form elements and we are adding a new action link next to the submit button. This will allow users to navigate back to the first step of the form process.
Inside the
submitForm() method we again save the values to the store and defer to the parent class to persist this data in any way it sees fit. We then redirect to whatever page we want (the route we use here is a dummy one).
And that is pretty much it. We should now have a working multistep form that uses the
PrivateTempStore to keep data available across multiple requests. If we need more steps, all we have to do is create some more forms, add them in between the existing ones and make a couple of adjustments. Of course you can make this much more flexible by not hardcoding the route names in the links and redirects, but I leave that part up to you.
In this article, we looked at a simple way to create a multistep form in Drupal 8. You can, of course, build on this approach and create highly complex and dynamic processes that involve not just forms but also other kinds of steps that leverage cross-request data. Thus, the purpose of this article has been as much about multistep forms as it has been about illustrating the power of the
PrivateTempStore. And if you, like me, think that the cTools object cache is very powerful in Drupal 7, you'll be very invested in its Drupal 8 counterpart.
One of the things that makes Drupal great is its flexible user permission system. The out of the box permissions grid we are all familiar with covers most uses cases of controlling what users can and cannot do. It is also very easy for module developers to create new permissions and roles that restrict the logic they implement.
Nevertheless, I have encountered a practical use case where the default configuration options are not enough. Namely, if you need to have multiple users with access to edit a particular node of a given type but without them necessarily having access to edit others of the same type. In other words, the next great article should be editable by Laura and Glenn but not by their colleagues. However, out of the box, users of a particular role can be masters either of their own content or of all content of a certain type. So this is not immediately possible.
In this article I am going to show you my solution to this problem in the form of a simple custom module called
editor_list. Article nodes will have a field where you can select users and only these users (or those who have full access) will be able to edit that particular node. You can find the module already in this git repository and you can install it on your site for a quick start. Do keep in mind that it has a dependency on the Entity Reference module as we will see in a minute.
I will keep the code comments to a minimum to save space but you can find them in the repository if you want. Basic knowledge of Drupal 7 is assumed in the remainder of this tutorial.
We first need the
editor_list.info file for our module to get us going:
name = Editor List description = Module illustrating a custom solution for having multiple editors on a node. core = 7.x dependencies[] = entityreference
Next, we need our
editor_list.module file where most of our business logic will be located. So go ahead and create it and we will populate it as we go on.
Finally, though not covered here, we can have an
editor_list.install file where we can implement
hook_install() and
hook_update hooks to create fields and/or deploy configuration. In the repository, you'll find that I provided an install hook that already creates an entity reference field called
field_editors and attaches it to the Article content type. If you are following along but not using the code in the repository, you should go ahead and create the field manually through the UI. It's a simple field that references User entities and allows for unlimited selections. Nothing major.
Node access
Going back to our
.module file, it's time to implement our access logic. First though, to make things as flexible and reusable as possible, let's have a simple function that returns an array of node types to which we apply our access logic:
function editor_list_node_types() { return array('article'); }
Since we are only targeting articles, this will suffice. But we will use this function in multiple places so in case we need to target other types as well, we just have to update this array.
Next, let's write another helpful function that returns all the user IDs set in the editors field of a given node. We will also use this in multiple places:
function editor_list_uids_from_list($node) { $users = field_get_items('node', $node, 'field_editors'); $allowed_uids = array(); if ($users) { $allowed_uids = array_map(function($user) { return $user['target_id']; }, $users); } return $allowed_uids; }
I believe the function is quite self explanatory so I won't go into details here. Instead, we can turn to our
hook_node_access() implementation that gets called by Drupal whenever a user tries to do something with a node (view, edit or delete):
/** * Implements hook_node_access(). */ function editor_list_node_access($node, $op, $account) { $node_types = editor_list_node_types(); if ( ! is_object($node) || ! in_array($node->type, $node_types) || $op !== 'update') { return NODE_ACCESS_IGNORE; } $allowed_uids = editor_list_uids_from_list($node); if (empty($allowed_uids)) { return NODE_ACCESS_IGNORE; } if (in_array($account->uid, $allowed_uids)) { return NODE_ACCESS_ALLOW; } }
So what's happening here?
First, we use our previously declared helper function to get the list of node types we want to target, and we basically ignore the situation and return if the node type of the currently accessed node is not within our list or if the operation the user is attempting is not of the type "update". Then we use our other helper function to check if there are any users in the editor list for this node and again ignore the situation if there aren't. However, if there are, and our accessing user is among them, we return the
NODE_ACCESS_ALLOW constant which basically gives the user access to perform the attempted operation. And that's it.
You can check out the documentation for more information about how this hook works.
Let's say you have admin users who can create and edit any type of content and regular authenticated users who cannot edit articles (apart from maybe the ones they created themselves). Adding one of these latter users to a node's editor list would give them access to that particular node. And another great thing is that since this is all nicely integrated, contextual filters and tabs also take these dynamic permissions into account.
Field access
We now have a working module that does what I initially set out for it to do. But let's say that your admin users are the only ones responsible for adding users to the editor lists. In other words, you are afraid that if your editors can edit their nodes and remove themselves from the list, they'll get locked out of the node they are supposed to work on.
To account for this situation, we need to implement a field access check and remove the possibility that editors tamper with that field. Implementing hook_field_access should do the trick nicely. And if you are wondering, this hook is similar to
hook_node_access() but is responsible for individual fields rather than the entire node (+ a couple of other small differences).
/** * Implements hook_field_access(). */ function editor_list_field_access($op, $field, $entity_type, $entity, $account) { $node_types = editor_list_node_types(); if ($entity_type === 'node' && is_object($entity) && in_array($entity->type, $node_types)) { return editor_list_control_field_access($op, $field, $entity_type, $entity, $account); } }
And here we have it. There are a few more parameters because this hook gets called for all entities, not just nodes. But again, we check if the currently accessed node is one of those we defined earlier (and that the entity is in fact a node) and this time delegate to another function to keep things tidier:
function editor_list_control_field_access($op, $field, $entity_type, $entity, $account) { if ($op !== 'edit') { return; } $uids = editor_list_uids_from_list($entity); if (!in_array($account->uid, $uids)) { return; } $deny = array('field_editors'); if (in_array($field['field_name'], $deny)) { return false; } }}
Since we only care if the user is trying to update a particular field, we return nothing if this is not the case. Keep in mind that the op string here is
edit and not
update as it was in the other hook. This is just one of those Drupal quirks of inconsistency we all came to love so much. And like before, we ignore the situation if the current user is not part of the editor list.
Then, we define an array of field names we want to deny access to (in our case only one but we can add to it depending on the use case). Finally, we return false if the currently accessed field is part of our
$deny array. Yet another difference here in that we have to return a boolean instead of a constant like we did before.
Now the editors in the list of a given node cannot remove themselves or add anybody else to the list. But then again, in some cases you may want this functionality and in others not. It's up to you.
Tidying up
The last thing I am going to show you here relates to organization and maybe a bit of user experience. With our current implementation, the editor list field on the Article nodes is present somewhere on the form (wherever you dragged-and-dropped it when editing the field settings). However, wouldn't it be nice if it were automatically part of the
Authoring information group at the bottom of the page? Something like this:
I think so. Let's see how we can do that.
First, we need to implement
hook_form_alter or one of its variations. I prefer the most targeted one to avoid unnecessary calls to it and a bunch of conditional checks:
/** * Implements hook_form_BASE_FORM_ID_alter(). */ function editor_list_form_article_node_form_alter(&$form, &$form_state, $form_id) { $form['#after_build'][] = 'editor_list_node_form_after_build'; }
We went with the
BASE_FORM_ID of the article nodes here so if we extend our application to other types we would do the same for those as well. Inside, we just define an
#after_build function to be triggered when the form has finished building. This is to ensure all the form alterations have been already done by contributed modules. All that is left to be done is to write the function responsible for making changes to the form:
function editor_list_node_form_after_build($form, &$form_state) { $field = field_info_field('field_editors'); if ( ! field_access('edit', $field, 'node', $form['#entity'])) { return $form; } if ($form['author']['#access'] === 0) { return $form; } $field_editors = $form['field_editors']; $field_editors['#weight'] = 0; $form['author']['additional_authors'] = $field_editors; $form['field_editors'] = array(); return $form; }
This looks complicated but it really isn't. We begin by loading the field definition of our editor list field. This is so that we can run the
field_access check on it and just return the form array unchanged if the current user doesn't have access to the field. Next, we do the same if the current user does not have access to the
author group on the form (this is the
Authoring information group we want to place the field into). And lastly, we make a copy of the field definition, change its weight and place it into the group, followed by unsetting the original definition to avoid having duplicates.
And that is pretty much it. Now the editors list field should be tucked in with the rest of the information related to authorship.
In this article, we created a solution to a content editing problem that Drupal 7 could not fix out of the box. However, it did provide us with the development tools necessary to make this an easy task inside of a custom module.
We now have an editor list field on the article node form by which we can specify exactly which users have access to that particular node. Though do keep in mind that in order for this to be of any use, the users you add to these lists must not have a role that allows them to edit all article nodes. Otherwise you won't see much of a difference.
In this article, I am going to show you a clean way of using the Drupal 8 Ajax API without writing one line of JavaScript code. To this end, we will go back to the first custom form we built for Drupal 8 in a previous article and Ajaxify some of its behaviour to make it more user friendly.
An updated version of this form can be found in this repository under the name
DemoForm (the
demo module). The code we write in this article can also be found there but in a separate branch called
ajax. I recommend you clone the repo and install the module in your development environment if you want to follow along.
Although poorly named, the
DemoForm was very helpful in illustrating the basics of writing a custom form in Drupal 8. It handles validation, configuration and exemplifies the use of the Form API in general. Of course, it focuses on the basics and has nothing spectacular going on.
If you remember, or check the code, you'll see that the form presents a single textfield responsible for collecting an email address to be saved as configuration. The form validation is in charge of making sure that the submitted email has a
.com ending (a poor attempt at that but enough to illustrate the principle of form validation). So when a user submits the form, they are saving a new email address to the configuration and get a confirmation message printed to the screen.
In this article, we will move the email validation logic to an Ajax callback so that after the user has finished typing the email address, the validation gets automagically triggered and a message printed without submitting the form. Again, there is nothing spectacular about this behaviour and you will see it quite often in forms in the wild (typically to validate usernames). But it's a good exercise for looking at Ajax in Drupal 8.
Ajax form
The first thing we need to do is move the email validation logic from the general
validateForm() to a method that handles only this aspect:
/** * Validates that the email field is correct. */ protected function validateEmail(array &$form, FormStateInterface $form_state) { if (substr($form_state->getValue('email'), -4) !== '.com') { return FALSE; } return TRUE; }
As you can notice, we've also changed the logic a bit to make sure the email address ends with a
Then, we can defer to this logic from the main validation method to make sure our existing behaviour still works:
/** * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state) { // Validate email. if (!$this->validateEmail($form, $form_state)) { $form_state->setErrorByName('email', $this->t('This is not a .com email address.')); } }
This way even if our form gets somehow submitted (programatically or otherwise), the validation will still be run.
Next, we need to turn to our form definition, specifically the email field, and make it trigger ajax requests based on a user interaction. This will be the act of a user changing the value of the field and removing focus from it:
$form['email'] = array( '#type' => 'email', '#title' => $this->t('Your .com email address.'), '#default_value' => $config->get('demo.email_address'), '#ajax' => [ 'callback' => array($this, 'validateEmailAjax'), 'event' => 'change', 'progress' => array( 'type' => 'throbber', 'message' => t('Verifying email...'), ), ], '#suffix' => '<span class="email-valid-message"></span>' );
What we did new here is add the
#ajax key to the array with some of the relevant keys. Additionally, we added a little markup after the form element as a wrapper for a short message regarding the validity of the email.
The callback inside the
#ajax array points to a method inside our form class (
validateEmailAjax()) while the
event adds a javascript binding to this form element for the jQuery change event. Alternatively, you can also specify a path key instead of a callback, but in our case it would mean having to also set up a route and a controller which seems redundant. And we don't want the
wrapper key because we do not intend to fill up an area with returned content but want to fine grain the actions that result from the callback. For that, we will use Ajax commands.
To learn more about all of this, I encourage you to consult the Ajax API page or the Form API entry for Ajax. There are a handful of other options you can use to further customize the Ajax behavior of your form elements.
Now it's time to write the callback method inside of our form class. This receives the
$form array and
$form_state object as arguments coming from the form that triggered the Ajax request:
/** * Ajax callback to validate the email field. */ public function validateEmailAjax(array &$form, FormStateInterface $form_state) { $valid = $this->validateEmail($form, $form_state); $response = new AjaxResponse(); if ($valid) { $css = ['border' => '1px solid green']; $message = $this->t('Email ok.'); } else { $css = ['border' => '1px solid red']; $message = $this->t('Email not valid.'); } $response->addCommand(new CssCommand('#edit-email', $css)); $response->addCommand(new HtmlCommand('.email-valid-message', $message)); return $response; }
Simply put, in this method, we perform the validation and return an Ajax response with multiple commands that differ depending on the validation result. With the CssCommand we apply some css directly to the email form element while with the HtmlCommand we replace the contents of the specified selector (remember the suffix from our form element?).
These commands pretty much map to jQuery functions so they are quite easy to grasp. You can find a list of all available commands on this page. And since we are using three new classes inside this method, we must remember to also use them at the top:
use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\CssCommand; use Drupal\Core\Ajax\HtmlCommand;
And that is pretty much it. If you clear the cache and reload your form, typing into the email field and removing focus will trigger the callback to validate the email address. You'll notice the little throbber icon there (which can be changed in the definition) and the short message we defined as well. A correct email address should highlight the field in green and print the OK message while on the contrary the color red is used with an opposite message.
If we had specified a
wrapper in the form element definition, we could have returned some content (or render array) which would have been placed inside that selector. So you have the option of choosing between returning content or Ajax commands but I recommend the latter for most cases because they offer a more flexible (and consistent) behavior.
In this article we've seen an example of using Ajax to improve our form and make it more friendly to end users. And we have written exactly zero lines of javascript to accomplish this.
In our case, it really is a matter of preference or fancification. But if you are dealing with a 20 field form which has validation on multiple fields similar to this, using Ajax really makes sense. It doesn't annoy users with having to submit the form only to realize their input is invalid.
Although forms are the main area where you'll see Ajax in Drupal 8, there are a couple of other ways you can leverage it without writing JavaScript.
Once nice way is to add the
use-ajax class on any link. This will have Drupal make an Ajax request to the URL in the
href attribute whenever the link is clicked. From the callback you can return Ajax commands and perform various actions as needed. But do keep in mind that jQuery and other core scripts are not loaded on all pages for anonymous users (hence Ajax will gracefully degrade to regular link behaviour). So make sure you include these scripts for anonymous users if you need this behavior.
In this article we are going to look at automated testing in Drupal 8. More specifically, we are going to write a few integration tests for some of the business logic we wrote in the previous Sitepoint articles on Drupal 8 module development. You can find the latest version of that code in this repository along with the tests we write today.
But before doing that, we will talk a bit about what kinds of tests we can write in Drupal 8 and how they actually work.
Simpletest (Testing)
Simpletest is the Drupal specific testing framework. For Drupal 6 it was a contributed module but since Drupal 7 it has been part of the core package. Simpletest is now an integral part of Drupal core development, allowing for safe API modifications due to an extensive codebase test coverage.
Right off the bat I will mention the authoritative documentation page for Drupal testing with Simpletest. There you can find a hub of information related to how Simpletest works, how you can write tests for it, what API methods you can use, etc.
By default, the Simpletest module that comes with Drupal core is not enabled so we will have to do that ourselves if we want to run tests. It can be found on the Extend page named as
Once that is done, we can head to
admin/config/development/testing and see all the tests currently available for the site. These include both core and contrib module tests. At the very bottom, there is also the
Clean environment button that we can use if any of our tests quit unexpectedly and there are some remaining test tables in your database.
How does Simpletest work?
When we run a test written for Simpletest, the latter uses the existing codebase and instructions found in the test to create a separate Drupal environment in which the test can run. This means adding additional tables to the database (prefixed by
simpletest_) and test data that are used to replicate the site instance.
Depending on the type of test we are running and what it contains, the nature of this replication can differ. In other words, the environment can have different data and core functionality depending on the test itself.
What kinds of tests are there in Drupal 8?
There are two main types of tests that we can write for Drupal 8: unit tests using PHPUnit (which is in core now) and functional tests (using Simpletest). However, the latter can also be split into two different kinds: web tests (which require web output) and kernel tests (which do not require web output). In this article we will practically cover only web tests because most of the functionality we wrote in the previous articles is manifested through output so that's how we need to test it as well.
Writing any type of test starts by implementing a specific class and placing it inside the
src/Tests folder of the module it tests. I also encourage you to read this documentation page that contains some more information on this topic as I do not want to duplicate it here.
Our tests
As I mentioned, in this article we will focus on providing test coverage for some of the business logic we created in the series on Drupal 8 module development. Although there is nothing complicated happening there, the
demo module we built offers a good example for starting out our testing process as well. So let's get started by first determining what we will test.
By looking at the demo module, we can delineate the following aspects we can test:
That's pretty much it. The custom menu link we defined inside the
demo.links.menu.yml could also be tested but that should already work out of the box so I prefer not to.
For the sake of brevity and the fact that we don't have too much we need to test, I will include all of our testing methods into one single class. However, you should probably group yours into multiple classes depending on what they are actually responsible for.
Inside a file called
DemoTest.php located in the
src/Tests/ folder, we can start by adding the following:
<?php namespace Drupal\demo\Tests; use Drupal\simpletest\WebTestBase; /** * Tests the Drupal 8 demo module functionality * * @group demo */ class DemoTest extends WebTestBase { /** * Modules to install. * * @var array */ public static $modules = array('demo', 'node', 'block'); /** * A simple user with 'access content' permission */ private $user; /** * Perform any initial set up tasks that run before every test method */ public function setUp() { parent::setUp(); $this->user = $this->drupalCreateUser(array('access content')); } }
Here we have a simple test class which for every test it runs, will enable the modules in the
$modules property and create a new user stored inside the
$user property (by virtue of running the
setUp() method).
For our purposes, we need to enable the
demo module because that is what we are testing, the
block module because we have a custom block plugin we need to test and the
node module because our logic uses the
access content permission defined by this module. Additionally, the user is created just so we can make sure this permission is respected.
For the three bullet points we identified above, we will now create three test methods. Keep in mind that each needs to start with the prefix
test in order for Simpletest to run them automatically.
Testing the page
We can start by testing the custom page callback:
/** * Tests that the 'demo/' path returns the right content */ public function testCustomPageExists() { $this->drupalLogin($this->user); $this->drupalGet('demo'); $this->assertResponse(200); $demo_service = \Drupal::service('demo.demo_service'); $this->assertText(sprintf('Hello %s!', $demo_service->getDemoValue()), 'Correct message is shown.'); }
And here is the code that does it.
First, we log in with the user we created in the
setUp() method and then navigate to the
demo path. Simpletest handles this navigation using its own internal browser. Next, we assert that the response of the last accessed page is 200. This validates that the page exists. However, this is not enough because we need to make sure the text rendered on the page is the one loaded from our service.
For this, we statically access the
\Drupal class and load our service. Then we assert that the page outputs the hello message composed of the hardcoded string and the return value of the service's
getDemoValue() method. It's probably a good idea to write a unit test for whatever logic happens inside the service but for our case this would be quite redundant.
And that's it with the page related logic. We can go to the testing page on our site, find the newly created
DemoTest and run it. If all is well, we should have all green and no fails.
Testing the form
For the form we have another method, albeit more meaty, that tests all the necessary logic:
/** * Tests the custom form */ public function testCustomFormWorks() { $this->drupalLogin($this->user); $this->drupalGet('demo/form'); $this->assertResponse(200); $config = $this->config('demo.settings'); $this->assertFieldByName('email', $config->get('demo.email_address'), 'The field was found with the correct value.'); $this->drupalPostForm(NULL, array( 'email' => '[email protected]' ), t('Save configuration')); $this->assertText('The configuration options have been saved.', 'The form was saved correctly.'); $this->drupalGet('demo/form'); $this->assertResponse(200); $this->assertFieldByName('email', '[email protected]', 'The field was found with the correct value.'); $this->drupalPostForm('demo/form', array( 'email' => '[email protected]' ), t('Save configuration')); $this->assertText('This is not a .com email address.', 'The form validation correctly failed.'); $this->drupalGet('demo/form'); $this->assertResponse(200); $this->assertNoFieldByName('email', '[email protected]', 'The field was found with the correct value.'); }
The first step is like before. We go to the form page and assert a successful response. Next, we want to test that the
assertFieldByName() assertion.
Another aspect we need to test is that saving the form with a correct email address does what it is supposed to: save the email to configuration. So we use the
drupalPostForm() method on the parent class to submit the form with a correct email and assert that a successful status message is printed on the page as a result. This proves that the form saved successfully but not necessarily that the new email was saved. So we redo the step we did earlier but this time assert that the default value of the email field is the new email address.
Finally, we need to also test that the form doesn't submit with an incorrect email address. We do so again in two steps: test a form validation failure when submitting the form and that loading the form again will not have the incorrect email as the default value of the email field.
Testing the block
/** * Tests the functionality of the Demo block */ public function testDemoBlock() { $user = $this->drupalCreateUser(array('access content', 'administer blocks')); $this->drupalLogin($user); $block = array(); $block['id'] = 'demo_block'; $block['settings[label]'] = $this->randomMachineName(8); $block['theme'] = $this->config('system.theme')->get('default'); $block['region'] = 'header'; $edit = array( 'settings[label]' => $block['settings[label]'], 'id' => $block['id'], 'region' => $block['region'] ); $this->drupalPostForm('admin/structure/block/add/' . $block['id'] . '/' . $block['theme'], $edit, t('Save block')); $this->assertText(t('The block configuration has been saved.'), 'Demo block created.'); $this->drupalGet(''); $this->assertText('Hello to no one', 'Default text is printed by the block.'); $edit = array('settings[demo_block_settings]' => 'Test name'); $this->drupalPostForm('admin/structure/block/manage/' . $block['id'], $edit, t('Save block')); $this->assertText(t('The block configuration has been saved.'), 'Demo block saved.'); $this->drupalGet(''); $this->assertText('Hello Test name!', 'Configured text is printed by the block.'); }
For this test we need another user that also has the permission to administer blocks. Then we create a new instance of our custom
demo_block with no value inside the
Who field and assert that a successful confirmation message is printed as a result. Next, we navigate to the front page and assert that our block shows up and displays the correct text:
Hello to no one.
Lastly, we edit the block and specify a
Test name inside the
Who field and assert that saving the block configuration resulted in the presence of a successful confirmation message. And we close off by navigating back to the home page to assert that the block renders the correct text.
In this article, we've seen how simple it is to write some basic integration tests for our Drupal 8 business logic. It involves creating one or multiple class files which simply make use of a large collection of API methods and assertions to test the correct behavior of our code. I strongly recommend you give this a try and start testing your custom code early as possible in order to make it more stable and less prone to being broken later on when changes are made.
Additionally, don't let yourself get discouraged by the slow process of writing tests. This is mostly only in the beginning until you are used to the APIs and you become as fluent as you are with the actual logic you are testing. I feel it's important to also mention that this article presented a very high level overview of the testing ecosystem in Drupal 8 as well as kept the tests quite simple. I recommend a more in depth look into the topic going forward.
About Drupal Sun
Drupal Sun is an Evolving Web project. It allows you to:
- Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
- Facet based on tags, author, or feed
- Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
- View the entire article text inline, or in the context of the site where it was created
See the blog post at Evolving Web

subset: commoncrawl