Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough
Jan 04 2021
Jan 04

Note: This post is written with a Drupal context, but applies to any PHP project.

This is a test that I wrote recently, which uses the camel case method name that is recommended by the Drupal and PSR-2 coding standards:

public function testThatPathAliasesAreNotTransferredToTheNewLanguageWhenOneIsAdded(): void {
  // ...
}

It has a long method name that describes the test that is being run. However, it's quite hard to read. Generally, I prefer to write tests like this, using the @test annotation (so that I can remove the test prefix) and snake case method names:

/** @test */
public function path_aliases_are_not_transferred_to_the_new_language_when_one_is_added(): void {
  // ...
}

This to me is a lot easier to read, particularly for long and descriptive test method names, and is commonly used within parts of the PHP community.

This approach, however, can result in some errors from PHPCS:

  • The open comment tag must be the only content on the line
  • Public method name "DefinedLanguageNodeTest::path_aliases_are_not_transferred_to_the_new_language_when_one_is_added" is not in lowerCamel format

We can avoid the errors by excluding the files when running PHPCS, or modifying rules within phpcs.xml (or phpcs.xml.dist) file to change the severity value for the rules. These approaches would mean either ignoring all PHPCS sniffs within the test files or ignoring some checks within all files, neither of which is an ideal approach.

Ignoring whole or partial files

We can tell PHPCS to ignore whole or partial files by adding comments - there's an example of this at the top of default.settings.php file:

// @codingStandardsIgnoreFile

The @codingStandards syntax, however, is deprecated and will be removed in PHP_CodeSniffer version 4.0. The new syntax to do this is:

// phpcs:ignoreFile

As well as phpcs:ignoreFile which ignores all of the sniffs in an entire file, there are also commands to disable and re-enable PHPCS at different points within the same file:

// Stop PHPCS checking.
// phpcs:disable

// Start PHPCS checking.
// phpcs:enable

Disabling specific rules in a file

As well as excluding a section of code from checks, with phpcs:ignore you can also specify a list of sniffs to ignore. For example:

// phpcs:disable Drupal.Commenting.DocComment, Drupal.NamingConventions.ValidFunctionName

By adding this to the top of the test class, these specific sniffs will be ignored so no errors will be reported, and any other sniffs will continue to work as normal.

If you're unsure what the names of the sniffs are that you want to ignore, add -s to the PHPCS command to have it include the sniff names in its output.

For more information on ignoring files, folders, part of files, and limiting results, see the Advanced Usage page for the PHP CodeSniffer project on GitHub.

You can also see this being used in some of the tests for this website.

Sep 05 2020
Sep 05

This week I gave a new talk on upgrading to Drupal 9 for the Drupal NYC meetup. Whilst preparing for that, I decided to upgrade my Dransible example project that I use for my Ansible and Ansistrano talk to Drupal 9 and document the process.

Whilst the steps taken are in the slides for that talk, here is the full list of steps that I took including the Composer commands.

Updating from Drupal 8.8 to 8.9

To begin with, let's update to the latest version of Drupal 8 so that we can do some testing and see all of the latest deprecation notices before moving to Drupal 9.

  1. Remove Drush temporarily using composer remove drush/drush as it will cause us being stuck on Drupal 8.9.0-beta2 rather than a newer, stable 8.9 version.
  2. Update ^8.8 to ^8.9 in composer.json for drupal/core-recommended, drupal/core-dev and drupal/core-composer-scaffold, and run composer update drupal/core-* --with-dependencies to update core to 8.9.5.
  3. Re-add Drush so that it's present for the deployment by running composer require drush/drush:^9.

Preparing for Drupal 9

  1. Add the Upgrade Status module by running composer require drupal/upgrade_status.
  2. Upgrade to Drush 10 by running composer require drush/drush:^10.
  3. Remove the Config Installer module by running composer remove drupal/config_installer. This is no longer needed since Drupal 8.6, and there will be no Drupal 9 version.
  4. Update the Admin Toolbar module to 2.3, a Drupal 9 compatible version, by running composer update drupal/admin_toolbar.

As I'd previously updated the Simple Message custom module to be Drupal 9 compatible (adding the core_version_requirement key to the info.yml file, and removing usages of deprecated code), no changes needed to be made to that.

Upgrading to Drupal 9

  1. Update ^8.9 to ^9.0 for the core packages in composer.json, and run composer update drupal/core-* --with-dependencies to update to 9.0.5.
  2. Re-add Drush by running composer require drush/drush. This will install Drush 10 by default.

Post upgrade

Although everything seemed to have updated OK locally, there were some errors when running a deployment to the Vagrant virtual machine that needed to be addressed, as well as some post-upgrade housekeeping steps to perform.

  1. Fix the deployment error by adding the Symfony Configuration component as a dependency by running composer require symfony/config:^4.
  2. Alias Drupal\Core\Messenger\MessengerInterface to messenger in simple_message.services.yml to fix the autowiring error.
  3. Add settings["config_sync_directory"] to settings file variables (this will be added automatically in the next version of the Drupal settings Ansible role).
  4. Remove the Upgrade Status module by running composer remove drupal/upgrade_status, as it's no longer needed.

And that's it! The Dransible demo project is upgraded, and if you see my Ansible deployments talk in the future, the demo site will be running on Drupal 9.

If you want to see the original pull request, it's at https://github.com/opdavies/dransible/pull/7.

May 20 2020
May 20

I recently finished porting this website from a static site generator to Drupal 8, meaning that this site has now been powered by three different major versions of Drupal (6, 7 and 8) as well as by two static site generators since it was first launched in early 2010.

The majority of the content was imported using migrations from JSON feeds that I created. This included:

  • Blog tags
  • Blog posts
  • Talks
  • Redirects

In some follow-up posts, I'll be looking at each migration separately, describing any issues and look at how it was used to import its respective content.

I'll update this post with the links to the follow-up posts, and they are also available from the blog series' page.

Apr 22 2020
Apr 22

Some time ago, I announced that I was planning on writing a book on automated testing and test driven development with Drupal. I created a landing page and set up a mailing list, but I wasn't sure at that point what I was going to cover or create as part of the book.

I'm going to write a book on automated testing in Drupal. Join the mailing list for updates, and I'm happy to take suggestions on what to cover. https://t.co/YXNpe6f8Ft #drupal

— Oliver Davies (@opdavies) May 15, 2018

Being a meetup and DrupalCamp conference organiser, after some thought I decided to build a website for an example conference, and that some of this code would then be included in the book as example content. This seemed to cover most of what I originally wanted, through features like a call for papers for potential speakers to propose sessions, allowing organisers to administer and moderate those proposals, automatically sending notification emails to submitters and displaying the accepted sessions.

I've started building it with Drupal 8.8 and it is now available on GitStore to purchase access to, including all future updates as I continue building the application - adding new features and upgrading to Drupal 9 once it is released. There are some other interesting things there too, such as using feature flags to enable or disable functionality, and using GitHub Actions to run the tests automatically.

The book itself I've added a page for on Leanpub, and I'll be continuing to add content to it in parallel to building the example codebase. Once there is enough content, I will release the first draft for purchase.

Any purchases that are made via Gitstore or Leanpub, an amount will be donated to the Drupal Association and the #DrupalCares campaign to help sustain the Association during COVID-19.

Aug 21 2018
Aug 21

I’ve been experimenting with moving some code to Drupal 8, and I’m quite intrigued by a different way that I’ve tried to structure it - using event subscribers, building on some of the takeaways from Drupal Dev Days.

Here is how this module is currently structured:

Note that there is no opdavies_blog.module file, and rather than calling actions from within a hook like opdavies_blog_entity_update(), each action becomes it’s own event subscriber class.

This means that there are no long hook_entity_update functions, and instead there are descriptive, readable event subscriber class names, simpler action code that is responsibile only for performing one task, and you’re able to inject and autowire dependencies into the event subscriber classes as services - making it easier and cleaner to use dependency injection, and simpler write tests to mock dependencies when needed.

The additional events are provided by the Hook Event Dispatcher module.

Code

opdavies_blog.services.yml:

services:
  Drupal\opdavies_blog\EventSubscriber\PostToMedium:
    autowire: true
    tags:
      - { name: event_subscriber }

  Drupal\opdavies_blog\EventSubscriber\SendTweet:
    autowire: true
    tags:
      - { name: event_subscriber }

Adding autowire: true is not required for the event subscriber to work. I’m using it to automatically inject any dependencies into the class rather than specifying them separately as arguments.

src/EventSubscriber/SendTweet.php:

namespace Drupal\opdavies_blog\EventSubscriber;

use Drupal\hook_event_dispatcher\Event\Entity\EntityUpdateEvent;
use Drupal\hook_event_dispatcher\HookEventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class SendTweet implements EventSubscriberInterface {

  ...

  public static function getSubscribedEvents() {
    return [
      HookEventDispatcherInterface::ENTITY_UPDATE => 'sendTweet',
    ];
  }

  public function sendTweet(EntityUpdateEvent $event) {
    // Perform checks and send the tweet.
  }

}

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

Evolving Web