Skip to main content

Drupal 10 upgrade: Custom code upgrades

Drupal 10 upgrade: www.computerminds.co.uk
Photo by Mitchell Luo on Unsplash
An article from ComputerMinds - Building with Drupal in the UK since 2005
7th Mar 2023

Steven Jones

Senior Developer
Hey, you seem to look at this article a lot! Why not Bookmark this article so you can find it easily in the future?

We're going to work through a full Drupal 9 to Drupal 10 upgrade of this very website: www.computerminds.co.uk. Follow along to see all the things we have to change to make that all happen.

Read more

This one is entirely on us, we wrote the custom code, which makes us responsible for maintaining it.

The upgrade status module gives us a nice report of each custom project and what changes it thinks are required to bring the code up to date to work with Drupal 10.

We really don't have a lot of custom module code, and the code that we do have is very simple, standard Drupal 8/9/10 stuff, so there are a couple of calls to taxonomy_term_load_multiple_by_name to remove, and an accessCheck to add to an entity query, but other than that our custom module code looked fine.

Our custom theme however, is a different story: we have some usages of jQuery once to remove, and we have a lot of custom Twig PHP classes that are extending deprecated classes. Nothing show-stopping, but a few things to do worth covering in more detail.

jQuery once

The jQuery once plugin has gone from core in Drupal 10, so we need to upgrade our javascript that is using it to use the vanilla javascript alternative that ships in Drupal 9: the once plugin. This is simple enough, we can rewrite code like this:

(function($) {
  Drupal.behaviors.smoothScroll = {
    attach: function (context, settings) {
      $('a[href*="#"]:not([href="#"])').once('smoothscroll').click(function() {
        if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
          var target = $(this.hash);
          target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
          if (target.length) {
            var offset_height = target.offset().top;
            $('html, body').stop().animate({
              scrollTop: offset_height ,
            }, 1000);
            return false;
          }
        }
      });
    }
  };
})(jQuery);

Into code like this:


(function($, Drupal, once) {
  Drupal.behaviors.smoothScroll = {
    attach: function (context, settings) {
      var elements = once('smoothscroll', 'a[href*="#"]:not([href="#"])', context);
      $(elements).click(function() {
        if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
          var target = $(this.hash);
          target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
          if (target.length) {
            var offset_height = target.offset().top;
            $('html, body').stop().animate({
              scrollTop: offset_height ,
            }, 1000);
            return false;
          }
        }
      });
    }
  };
})(jQuery, Drupal, once);

And then we need to tweak the definition of our library in the corresponding .libraries.yml file to remove the dependency on core/jquery.once and add one on core/once.

Twig changes

As mentioned previously the site was originally built in Drupal 8, which meant using Twig version 1, so there are plenty of deprecations to take care of to get the codebase ready for Drupal 10.

This is largely because we have a slightly strange setup in that we're using an early version of Emulsify to build our theme. Most importantly for this article it means that our theme contains quite a few twig functions like this:


<?php
/**
 * @file
 * Add "getUniqueId" function for Pattern Lab.
 *
 * Brings the useful Drupal Html::getUniqueId function in.
 */

use Drupal\Component\Utility\Html;

/**
 * Create the function we want to be able to call.
 *
 * Our function will be passed $context and then any other values provided.
 */
$function = new Twig_SimpleFunction('getUniqueId', function ($context, $string) {
  if (is_string($string)) {
    // Must cover the Drupal context AND the PatternLab context.
    if (class_exists('Drupal')) {
      return Html::getUniqueId($string);
    }
    else {
      return $string;
    }
  }
  else {
    return $string;
  }
}, ['needs_context' => TRUE, 'is_safe' => ['html']]);

This can trivially become (changing the base class that is extended):


<?php
/**
 * @file
 * Add "getUniqueId" function for Pattern Lab.
 *
 * Brings the useful Drupal Html::getUniqueId function in.
 */

use Drupal\Component\Utility\Html;

/**
 * Create the function we want to be able to call.
 *
 * Our function will be passed $context and then any other values provided.
 */
$function = new \Twig\TwigFunction('getUniqueId', function ($context, $string) {
  if (is_string($string)) {
    // Must cover the Drupal context AND the PatternLab context.
    if (class_exists('Drupal')) {
      return Html::getUniqueId($string);
    }
    else {
      return $string;
    }
  }
  else {
    return $string;
  }
}, ['needs_context' => TRUE, 'is_safe' => ['html']]);

The codebase has around 20 usages of deprecated Twig classes, however the task looks simple enough and there's nothing particularly tricky to do here, simply tweak the classes used per the deprecation messages in the older classes. There are automated tools that'll make these changes for you, but to be honest, there aren't that many changes to make and I'm keen to get eyes on the codebase more generally and get a feel for what we have in it!

All done

So that's it for Custom code and theme. We had actually been pretty good at:

  • Not writing custom code unless we had to.
  • If we did write custom code, not using already deprecated code.
  • Going back periodically and remediating new deprecations.

Which then makes these changes pretty straightforward. 

Aside: I'll just note that we have a lot jQuery style JavaScript in our codebase....mostly because we've been around for a while and learned JavaScript with jQuery, so it's just too easy for us to still write with jQuery everywhere. However, we know that we need to get to a place where we don't need jQuery and we'll get there, but we'll need to do some internal training on that and then go back through our code and re-write it without jQuery. Maybe I'll write an article about that too one day!

Hi, thanks for reading

ComputerMinds are the UK’s Drupal specialists with offices in Bristol and Coventry. We offer a range of Drupal services including Consultancy, Development, Training and Support. Whatever your Drupal problem, we can help.