Problem/Motivation

The Drush command patternkit:libUpdate updates existing patterns when their schema and/or template changes. We need to be able to run this updater outside of Drush (e.g., in hook_update_N()).

Proposed resolution

Split the main logic of Patternkit's patternkit:libUpdate Drush command into a Drupal service, so that other systems can call them.

Release notes snippet

patternkit:devUpdate Drush Command Removed

The existing drush patternkit:devUpdate command has been removed since it is no longer relevant to active updates of the module and should no longer be in use.

patternkit:libUpdate Drush Command Filtering Now Available

The functionality of the existing drush patternkit:libUpdate command has been split into a pair of new services for improved accessibility to other use within a Drupal site. The commands options have also been revised to offer improved filtering options. Previously, the command excepted a library name intended to filter what patterns might be updated, but unfortunately this value was ignored. Now the command optionally accepts and uses a library name for filtering which patterns should be updated, but it also accepts a new --filter option allowing for limitation of what entity types, bundles, and display modes should be iterated through and updated.

❯ drush help patternkit:libUpdate
Update all patterns in a library.

Examples:
 drush pklu @patternkit           Update all @patternkit library patterns in all blocks and layouts.  
 drush pklu --filter=node.article Update all patterns on the Node Article content type and overrides. 

Arguments:
 [library_name] (optional) The name of a library to limit updates to. 

Options:
 --filter=FILTER A string concatenation of entity type, bundle, and display mode for updates to be limited to.     
                 Example: 'node.article.default'. Fewer items may also be provided to filter. For example, only an 
                 entity and a bundle may be provided ('node.article') or just an entity type ('node').             

Aliases: pklu, patternkit-lib-update

New Service: patternkit.helper.update

A new UpdateHelper service has been added encapsulating much of the functionality that was baked into the previous iteration of the patternkit:libUpdate command. The functionality of the previous iteration of the command has been broken down and implemented on this service as a handful of public methods intended to be versatile and helpful for working with and updating Patternkit components. This service also serves as the entryway to automating the update process for pattern components throughout a site from an update hook, for example.

/**
 * Update all the patterns.
 */
function mymodule_update_9001(&$sandbox) {
  /** @var \Drupal\patternkit\UpdateHelper $update_helper */
  $update_helper = \Drupal::service('patternkit.helper.update');

  [$blocks, $layouts] = $update_helper->updateAllPatternComponents();

  return t('Updated @blocks blocks and @layouts layouts', ['@blocks' => $blocks, '@layouts' => $layouts]);
}

It's also worth noting, the updateAllPatternComponents() method accepts a $filter argument allowing programmatic usage of all the same filter options exposed by the Drush command. An example update hook to only update patternkit patterns on the Article content type might look like this:

/**
 * Update @patternkit patterns on Article Nodes.
 */
function mymodule_update_9001(&$sandbox) {
  /** @var \Drupal\patternkit\UpdateHelper $update_helper */
  $update_helper = \Drupal::service('patternkit.helper.update');

  $filter = [
    'library' => 'patternkit',
    'entity_type' => 'node',
    'bundle' => 'article',
  ];
  [$blocks, $layouts] = $update_helper->updateAllPatternComponents($filter);

  return t('Updated @blocks blocks and @layouts layouts', ['@blocks' => $blocks, '@layouts' => $layouts]);
}

New Service: patternkit.helper.layout

A new LayoutHelper service has also been added to assist with management of Patternkit components placed within layout configurations. Much like the methods on the UpdateHelper service, the methods on the LayoutHelper service are intended to be flexible for usage by other code.

By default, the updateAllPatternComponents() method on the UpdateHelper service will automatically use the LayoutHelper service to also update Layout Builder components if Layout Builder is enabled, so no additional usage of the service is required for that scenario. If, however, one needs to retrieve all of the Patternkit components within a layout or apply a callback function to all the components within a layout, there are helpful methods available on the service to do those tasks.

Issue fork patternkit-3284744

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

krisahil created an issue. See original summary.

krisahil’s picture

Status: Active » Closed (works as designed)

Closing this issue, because we can already call the Drush command from an update hook. See this example from patternkit_update_8001():

  // Upgrades existing patterns.
  $command = new PatternkitCommands();
  $command->setLogger(\Drupal::logger('patternkit'));
  $command->devUpdate();
slucero’s picture

Assigned: krisahil » Unassigned
Status: Closed (works as designed) » Active

Let's reopen this issue for follow-up rather than planning to run commands from a Drush context in update hooks.

krisahil’s picture

Here's a patch that refactors the patternkit:libUpdate command into a service. I think next step would be moving the patternkit:devUpdate command into a service (or removing it if it's no longer necessary).

slucero’s picture

slucero’s picture

I just uploaded a new branch with some work I've done locally for breaking up the Drush operations into a pair of new services: 3284744-refactor-drush-update-commands. The aim here is to make these operations accessible outside of just Drush, and ideally provide access to some reusable methods that might prove helpful for different scenarios.

This still needs further testing and automated tests written for it, but I'm getting it off of my local so others can see it and offer feedback as well.

krisahil’s picture

@slucero, I left a few code-level comments on the MR, but looks good overall. Also, reading this code helped me understand the pattern updater Drush command a lot more than the previous code.

I functionally tested by first running drush pklu, which ran as expected.
Second, I added an update hook to manually run the update helper service, which also worked as expected (BTW, is this the expected way to run this code?) :

/**
 * Test 3284744.
 */
function mymodule_update_9001(&$sandbox) {
  /** @var \Drupal\patternkit\UpdateHelper $update_helper */
  $update_helper = \Drupal::service('patternkit.helper.update');

  list($blocks, $layouts) = $update_helper->updateAllPatternComponents();

  return t('Updated @blocks blocks and @layouts layouts', ['@blocks' => $blocks, '@layouts' => $layouts]);
}

Thanks for your work on this!

slucero’s picture

@krisahil, thank you for the testing and feedback!

I left a few code-level comments on the MR, but looks good overall.

Did these get saved? I'm not seeing them on the MR at all.

Also, reading this code helped me understand the pattern updater Drush command a lot more than the previous code.

Thanks for letting me know! I too had a lot of trouble following what the original code was doing, so hearing that it's actually easier to understand now is great validation and much appreciated!

Second, I added an update hook to manually run the update helper service, which also worked as expected (BTW, is this the expected way to run this code?)

Your example update hook looks good to me and will be good to include in documentation for the update. The only other element not represented there would be the use of optional filters if only patterns in a specific library should be updated or only layouts on a specific entity type/bundle/display should be targeted.

slucero’s picture

Thanks for getting those comments posted, @krisahil. I've gone through and added some replies to each.

For when I continue work on this issue, I had another question for the issue overall:

* Is the `drush patternkit:devUpdate` command still needed at all?

slucero’s picture

Issue summary: View changes
Status: Active » Needs review

I've posted some additional changes to add unit tests covering the new services and removing the drush patternkit:devUdpate command since it seems to be outdated and unlikely to still be used. The alternative to removing it would be to refactor it into the same pattern as the libUpdate command has been changed into, but some of the changes (like the example below) seem outdated and no longer relevant for maintenance.

          // Old plugin ids were in the format:
          // 'patternkit_block:library.name_path_to_pattern'.
          // New plugin ids are:
          // 'patternkit_block:library__name_path_to_pattern'.
          if (strpos($plugin_id, '.')) {
            $config['id'] = 'patternkit_block:' . str_replace('.', '_', str_replace('_', '__', substr($plugin_id, strlen('patternkit_block:'))));
          }

With the test coverage added, I've opened up the MR for review and testing. I'm also updating the issue description to capture the changes more clearly.

jordanpagewhite’s picture

Previously, the command excepted a library name intended to filter what patterns might be updated, but unfortunately this value was ignored

I believe that "excepted" should be "accepted" here, right?

slucero credited BLadwin.

slucero’s picture

Status: Needs review » Reviewed & tested by the community

  • slucero committed 0083fee on 9.1.x authored by krisahil
    Issue #3284744 by slucero, krisahil, jordanpagewhite, BLadwin: Need to...
slucero’s picture

Status: Reviewed & tested by the community » Fixed

Merged for release in beta 6.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.