Drupal 10 upgrade: Custom code upgrades
Steven Jones
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!