Skip to main content

Ensuring smart_date works for all versions of Drupal 10 and 11

Published on

At MidCamp a few weeks ago, Martin Anderson-Clutz tapped me on the shoulder to check out a Smart Date issue for compatibility with Drupal 10.2. As of Drupal 10.2, ListItemBase::extractAllowedValues takes an array as its first argument versus a string. The method used to explode a newline separated string into an array for its callers. I took a look at the issue. The change affected the parseValues method in the SmartDateListItemBase class. The parseValues method takes the field's values and passes them to extractAllowedValues, the method with a changed signature in Drupal 11.

The original method contained the following:

  /**
   * {@inheritdoc}
   */
  public static function parseValues($values) {
    // Use the ListItemBase parsing function, but don't allow generated keys.
    $result = static::extractAllowedValues($values, 1);
    return $result;
  }

The fix was the following:

  /**
   * {@inheritdoc}
   */
  public static function parseValues($values) {
    // Use the ListItemBase parsing function, but don't allow generated keys.
    $list = explode("\n", $values);
    $list = array_map('trim', $list);
    $list = array_filter($list, 'strlen');
    $result = static::extractAllowedValues($list, 1);
    return $result;
  }

Everyone RTBC'd a patch that would break the module, and any further fixes for folks below 10.2 would force a new minor version of the module! That's not good. Luckily, Drupal now has utilities that make it easier than ever to write backward-compatible code.

I will walk through how I made the method work across multiple minor versions of Drupal core without triggering deprecation notices.

Keep Smart Date compatible with all versions of Drupal 10

The Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall method allows handling backward-compatible code branches. This utility was added to Drupal 10.1.3, and Smart Date supports those few sites that are still on Drupal. While they should upgrade to Drupal 10, we don't have to drop support for 10.0.

First, we check if the DeprecationHelper exists. If it doesn't, we know the code base is unaffected by the deprecation.

    if (!class_exists(DeprecationHelper::class)) {
      return static::extractAllowedValues($values, 1);
    }

Then, we use DeprecationHelper::backwardsCompatibleCall to support calling two different code branches, the deprecated and current version, using closures. The first arguments to backwardsCompatibleCall allow determining whether the current version of Drupal is before or after the deprecation. The first callable is used if we're running a version after the deprecation; otherwise, the second one is.

    return DeprecationHelper::backwardsCompatibleCall(
      \Drupal::VERSION,
      '10.2',
      static function () use ($values) {
        $list = explode("\n", $values);
        $list = array_map('trim', $list);
        $list = array_filter($list, 'strlen');
        return static::extractAllowedValues($list, 1);
      },
      static fn () => self::extractAllowedValues($values, 1)
    );

I took the proposed fix, which was correct, and placed it into the currentCallable argument and the existing code for the deprecatedCallable argument. Now, the maintainers of Smart Date can continue delivering bug fixes without leaving users behind, and users have a simple upgrade path without worrying about dropped minor version support of Drupal core.

You can find the full commit here: smart_date/-/commit/1dcd8f2beae45ceaa3e941c1819d6d1c0995dffb

 

I'm available for one-on-one consulting calls – click here to book a meeting with me 🗓️

#