Problem/Motivation

As discussed in #2336597: Convert path aliases to full featured entities, we should try to move all the code related to path aliases to a module, since the Path Alias API is not really required for routing to work.

Proposed resolution

Move all the Path Alias API classes and interfaces to a new path_alias module. The module will be required in Drupal 8, so that there is no way for sites to accidentally delete all their path aliases (or any other scenario which might result from uninstalling it). In 9.x or 10.x we can make it optional.

To keep BC:

  • the new interfaces and classes will extend the legacy ones;
  • the new services will be defined in core.services.yml and will use the legacy classes, which will be replaced with the new ones by PathAliasServiceProvider, once path_alias is installed;
  • the legacy alias manager is still available and shares its internal state with the new one to keep performance optimizations working.

Remaining tasks

  • Validate the proposed solution
  • Write a patch
  • Reviews

User interface changes

None

API changes

No public API change, however the path_subscriber and path_processor_alias services are replaced with the new path_alias.subscriber and path_alias.path_processor ones.

Additionally the path.alias_whitelist becomes a service alias of the new path_alias.whitelist service.

Data model changes

None

Release notes snippet

The Path Alias core subsystem has been moved to the path_alias module. This may require updates to service providers that altered or decorated the legacy services.

An upgrade path is provided from Drupal 8.7 for this change. The upgrade path has been updated so that it should also work for upgrading from 8.8.0-alpha; however, note that upgrade paths from alpha releases are not officially supported.

CommentFileSizeAuthor
#95 path-alias_module-3084983-95.d9.0.patch847 bytesplach
#95 path-alias_module-3084983-95.d8.8.patch847 bytesplach
#92 3084983-92.patch841 bytesKrzysztof Domański
#76 path-alias_module-3084983-76.interdiff.txt4.27 KBplach
#76 path-alias_module-3084983-76.d9.patch185.37 KBplach
#76 path-alias_module-3084983-76.d8.patch185.51 KBplach
#70 interdiff-70.txt6.99 KBamateescu
#70 path-alias_module-3084983-70.D9.patch182.64 KBamateescu
#70 path-alias_module-3084983-70.D8.patch182.8 KBamateescu
#67 path-alias_module-3084983-67.d9.patch179.86 KBplach
#67 path-alias_module-3084983-67.d8-d9-diff.txt1 KBplach
#67 path-alias_module-3084983-67.d8.patch180.02 KBplach
#66 path-alias_module-3084983-66.d9.patch178.34 KBplach
#66 path-alias_module-3084983-66.interdiff.txt629 bytesplach
#66 path-alias_module-3084983-66.patch180.01 KBplach
#64 path-alias_module-3084983-64.review.txt117.11 KBplach
#64 path-alias_module-3084983-64.interdiff.txt10.02 KBplach
#64 path-alias_module-3084983-64.patch180.02 KBplach
#56 path-alias_module-3084983-56.patch186.24 KBplach
#56 path-alias_module-3084983-56.interdiff.txt871 bytesplach
#54 path-alias_module-3084983-54.review.patch122.48 KBplach
#54 path-alias_module-3084983-54.patch185.39 KBplach
#54 path-alias_module-3084983-54.interdiff.txt25.11 KBplach
#52 path-alias_module-3084983-52.patch183.74 KBplach
#52 path-alias_module-3084983-52.interdiff.txt26.02 KBplach
#48 path-alias_module-3084983-48.patch180.53 KBplach
#48 path-alias_module-3084983-48.interdiff.txt62.91 KBplach
#46 path-alias_module-3084983-46.patch117.62 KBplach
#46 path-alias_module-3084983-46.interdiff.txt20.2 KBplach
#43 path-alias_module-3084983-43.patch118.32 KBplach
#43 path-alias_module-3084983-43.interdiff.txt7.97 KBplach
#41 path-alias_module-3084983-41.patch110.67 KBplach
#41 path-alias_module-3084983-41.interdiff.txt5.34 KBplach
#37 path-alias_module-3084983-37.patch106.23 KBplach
#37 path-alias_module-3084983-37.interdiff.txt32.53 KBplach
#35 path-alias_module-3084983-35.patch82.45 KBplach
#32 path-alias_module-3084983-32.patch83.36 KBplach
#29 path-alias_module-3084983-29.patch83.33 KBplach
#26 interdiff-26.txt10.52 KBamateescu
#26 3084983-26.patch69.83 KBamateescu
#25 interdiff-services-and-classes.txt4.07 KBamateescu
#25 3084983-25.patch70.22 KBamateescu
#23 interdiff-23.txt1.49 KBamateescu
#23 3084983-23.patch65.83 KBamateescu
#20 interdiff-20.txt662 bytesamateescu
#20 3084983-20.patch64.34 KBamateescu
#18 interdiff-18.txt18.01 KBamateescu
#18 3084983-18.patch64.4 KBamateescu
#12 path-alias_module-3084983-12.patch65.92 KBplach
#12 path-alias_module-3084983-12.interdiff.txt8.1 KBplach
#10 interdiff-10.txt3.72 KBamateescu
#10 3084983-10.patch58.9 KBamateescu
#9 interdiff-9.txt5.85 KBamateescu
#9 3084983-9.patch57.08 KBamateescu
#7 interdiff-7.txt3.5 KBamateescu
#7 3084983-7.patch51.93 KBamateescu
#6 interdiff.txt529 bytesplach
#6 3084983-6.patch49.06 KBplach
#2 3084983.patch48.45 KBamateescu
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

amateescu created an issue. See original summary.

amateescu’s picture

Status: Active » Needs review
FileSize
48.45 KB

Here's a start.

Status: Needs review » Needs work

The last submitted patch, 2: 3084983.patch, failed testing. View results

plach’s picture

Closed #3084874: Consider moving path aliases to a new path_alias module as a duplicate. @catch posted a few questions in the IS over there:

Do we move the aliases to path module (making it required), or to a new path alias module (which would also need to be required, at least to start)?

How do we deal with the change in provider?

plach’s picture

Of course the latter question is relevant only if we're not able to make this happen in 8.8.

plach’s picture

FileSize
49.06 KB
529 bytes

Minor fix

amateescu’s picture

Status: Needs work » Needs review
FileSize
51.93 KB
3.5 KB

A few more fixes.

Status: Needs review » Needs work

The last submitted patch, 7: 3084983-7.patch, failed testing. View results

amateescu’s picture

Status: Needs work » Needs review
FileSize
57.08 KB
5.85 KB

And some more :)

amateescu’s picture

Another quick round.

Most of the remaining failures have common causes, which should be covered by this to-do list:

\Drupal\system\Form\SiteInformationForm

We need to remove the alias bits from that form and alter it in the path module.

\Drupal\system\Plugin\Condition\RequestPath

We need to remove the alias bits from this plugin and override the plugin class in the path module to re-add the path alias functionality.

\Drupal\views\Plugin\views\argument_default\Raw

Not sure about this one, maybe the easiest option would be to add the path module as a dependency for the views module.

Status: Needs review » Needs work

The last submitted patch, 10: 3084983-10.patch, failed testing. View results

plach’s picture

Status: Needs review » Needs work

The last submitted patch, 12: path-alias_module-3084983-12.patch, failed testing. View results

catch’s picture

Don't we need to make path module required? If so then all the assumptions that the system is available shouldn't need to change (obviously they should eventually but not to make tests pass).

Ideally we want a patch here that doesn't require any changes outside Drupal\Core and path module since that will prove this is a non-disruptive change.

plach’s picture

Yes, I think we discussed making this required for D8 and optional in D9, right? So in the latter case we would still need some test changes, but that would be follow-up work, I think.

amateescu’s picture

I'm not sure we need to make the module required at all. Reading the patch again, most changes outside Drupal\Core\Path are in kernel tests, which don't install required modules automatically..

catch’s picture

We need to make it required in 8.x not because it has to be technically, but so that there is no way for sites to accidentally delete all their path aliases in 8.x (or any other scenario which might result from uninstalling it). In 9.x or 10.x we can make it optional, but it would need to be explicitly added to install profiles and similar.

amateescu’s picture

Status: Needs work » Needs review
FileSize
64.4 KB
18.01 KB

@catch, that makes perfect sense :) And also makes this patch a lot smaller and easier to finish!

Status: Needs review » Needs work

The last submitted patch, 18: 3084983-18.patch, failed testing. View results

amateescu’s picture

Status: Needs work » Needs review
FileSize
64.34 KB
662 bytes

Let's keep the existing service name to keep this patch as non-disruptive as possible.

Status: Needs review » Needs work

The last submitted patch, 20: 3084983-20.patch, failed testing. View results

Berdir’s picture

+++ b/core/modules/path/src/PathAliasInterface.php
@@ -1,6 +1,6 @@
diff --git a/core/lib/Drupal/Core/Path/PathAliasStorage.php b/core/modules/path/src/PathAliasStorage.php

diff --git a/core/lib/Drupal/Core/Path/PathAliasStorage.php b/core/modules/path/src/PathAliasStorage.php
similarity index 98%

similarity index 98%
rename from core/lib/Drupal/Core/Path/PathAliasStorage.php

rename from core/lib/Drupal/Core/Path/PathAliasStorage.php
rename to core/modules/path/src/PathAliasStorage.php

rename to core/modules/path/src/PathAliasStorage.php
index a9db234c45..65e630ffe1 100644

index a9db234c45..65e630ffe1 100644
--- a/core/lib/Drupal/Core/Path/PathAliasStorage.php

--- a/core/lib/Drupal/Core/Path/PathAliasStorage.php
+++ b/core/modules/path/src/PathAliasStorage.php

+++ b/core/modules/path/src/PathAliasStorage.php
+++ b/core/modules/path/src/PathAliasStorage.php
@@ -1,6 +1,6 @@

@@ -1,6 +1,6 @@
 <?php
 
-namespace Drupal\Core\Path;
+namespace Drupal\path;
 

Even if we keep the old service names (we could also have aliases that we deprecated I guess), we still need to have BC for the classes as well, probably by leaving the old classes there and adding a @deprected, for people we used/subclassed them somhow directly.

I think having them just be an empty OldName extends NewName would be problematic as we can't prevent them from being loaded before the module is enabled.

amateescu’s picture

@Berdir, how about using class_alias() instead of leaving duplicated code around? That should allow existing code to work just as before.

Fixed a couple more test fails. The remaining ones have an interesting cause: the current Path module adds the path base field to some entity type, but, since it's optional in HEAD, all the REST and JSON:API tests don't expect that field.

I guess this means we have to move all code to a new module (path_alias?) instead of the existing one..

Berdir’s picture

> @Berdir, how about using class_alias() instead of leaving duplicated code around? That should allow existing code to work just as before.

Defined inside path.module then? Would still fail if a module subclasses one of these classes, e.g. a custom replacement of one of these services through a ServiceProvider, we can't fix this before these are called. I think the safest option is to just leave them where they are now and add @deprecated.

Actually, isn't it the same with the services? Looks like there are no core modules that inject any of these path services directly in another service, only in on-demand create() methods? But I'm pretty sure trying to update a site with e.g. pathauto or redirect enabled will fail hard on a site that has an empty cache. So IMHO same here, we need to leave the old services in place, pointing to the deprecated classes.

FWIW, that was my main concern in the initial issue about moving this to a module. Moving code/services around isn't a big deal, sure, especially if we keep it as required, but BC is *hard* :)

amateescu’s picture

Status: Needs work » Needs review
FileSize
70.22 KB
4.07 KB

@Berdir, maybe we can define the class aliases in bootstrap.php directly. I'll have to try out a manual upgrade from 8.7.x though :)

Discussed a bit with @plach today and we realized that we need a separate module after all, so I moved everything over to a new path_alias module.

The interdiff contains only the proposal for how to handle the deprecated services and classes.

amateescu’s picture

Of course @Berdir is right, we have to keep the old services and classes and deprecate them :) Just added a bunch of @deprecated tags for now to check the testbot.

Status: Needs review » Needs work

The last submitted patch, 26: 3084983-26.patch, failed testing. View results

plach’s picture

Assigned: Unassigned » plach

Working a bit on this

plach’s picture

Assigned: plach » Unassigned
Status: Needs work » Needs review
FileSize
83.33 KB

After discussing with @Berdir and @catch, I'm trying an approach that should limit the amount of changes to the minimum. We still need some test adjustments, especially to kernel tests. To rule out trivial kernel test failures only caused by the missing path_alias schema this patch tweaks KernelTestBase to always install it, however the plan is remove that and fix all Kernel tests in the final version.

Unassigning for now if anyone wishes to take a look at the last failures, I will get back to it later

Status: Needs review » Needs work

The last submitted patch, 29: path-alias_module-3084983-29.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

plach’s picture

Assigned: Unassigned » plach

Back on this

plach’s picture

plach’s picture

Assigned: plach » Unassigned

No luck so far

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

plach’s picture

Assigned: Unassigned » plach
Status: Needs work » Needs review
FileSize
82.45 KB

Status: Needs review » Needs work

The last submitted patch, 35: path-alias_module-3084983-35.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

plach’s picture

Assigned: plach » Unassigned

Status: Needs review » Needs work

The last submitted patch, 37: path-alias_module-3084983-37.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

plach’s picture

+++ b/core/core.services.yml
@@ -1754,3 +1749,32 @@ services:
+    arguments: [path_alias_whitelist, '@cache.bootstrap', '@lock', '@state', 'path_alias.repository']

geez...

plach’s picture

This should have way less test failures.

Status: Needs review » Needs work

The last submitted patch, 41: path-alias_module-3084983-41.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

plach’s picture

Status: Needs review » Needs work

The last submitted patch, 43: path-alias_module-3084983-43.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

plach’s picture

Assigned: Unassigned » plach
plach’s picture

A few more fixes.

Status: Needs review » Needs work

The last submitted patch, 46: path-alias_module-3084983-46.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

plach’s picture

Assigned: plach » Unassigned
Status: Needs work » Needs review
FileSize
62.91 KB
180.53 KB

This should fix the last failures. Setting to needs review, but still needs work actually, I will summarize tomorrow.

plach’s picture

Status: Needs review » Needs work

Here's the reason why I think this still needs work: the current patch, to be able to raise deprecation errors when a legacy path alias service is retrieved, defines a new set of services, instead of aliasing them, since the ability to deprecate aliases was introduced only in Symfony 4.3.

However this turned out to be a bad choice, since this way we'd have two path alias subscriber instances and (most importantly) two path alias processor ones executed, which would mean that lookups are doubled, since the service state is not shared, which I wrongly assumed would be the case. This is not acceptable, I believe.

For this reason I think we should move back to aliases and rely on class-level deprecation messages to warn API consumers. Unfortunately this won't work for what I believe is the most common use case, that is just performing path alias lookups through the alias manager, because PathAliasServiceProvider will swap the legacy class with the new one, which will cause deprecation errors not to be raised.

Maybe a possible solution could be a hybrid one: duplicate only the alias manager service, deprecate the old one, don't swap its implementation so that both container-level and class-level deprecation errors are raised, and somehow ensure that both instances rely on the same internal state, maybe via static members or some shared reference.

The other services (path_subscriber, path_processor_alias, and path.alias_whitelist) could be considered "internal" from the API usage perspective, even if they are not formally marked as such, meaning we would alias them, which wouldn't trigger explicit deprecation messages when retrieving them.

I think this would provide the best compromise between performance and DX, given the current state. From 9.0.0 on, when needing to deprecate services, we will be able to rely on deprecated aliases and this process will be way easier and reliable.

plach’s picture

Title: Move all the code related to path aliases to the path module » Move all the code related to path aliases to a new (required) "path_alias" module
catch’s picture

The other services (path_subscriber, path_processor_alias, and path.alias_whitelist

So path_subscriber and path_processor_alias are event subscribers/tagged services respectively, I think these can be straightforwardly removed from a minor release (as we might remove a hook implementation). We'd leave the actual code in core and deprecated, but remove the service definition.

path.alias_whitelist is tagged as needs destruction but is injected into other services. Would "move back to aliases and rely on class-level deprecation messages to warn API consumers" work for that?

somehow ensure that both instances rely on the same internal state

Depending on what state needs to be shared, MemoryCache may help there.

plach’s picture

This implements #51. Unfortunately aliasing the whitelist means we don't get deprecation messages for it, because the actual implementation class is not deprecated.

plach’s picture

Issue summary: View changes
Issue tags: +Needs change record updates

Updated IS, added CR and follow-up to remove the legacy code.

Also, marked as requiring CR updates to adjust the alias repository CR once this is committed.

plach’s picture

This updates the previous patch with all the missing CR and @todo references and performs a final PHP doc cleanup. We should be ready for review now.

This provides also a "review" patch that doesn't include the fixture changes that are bloating the actual patch.

andypost’s picture

Maybe it makes sense to split path_alias and path_alias_ui

plach’s picture

This adds explicit test coverage for the DB update changes.

@andypost, #55:

There is no UI code involved here: the UI is entirely provided by the path module. I intentionally did not update any module name/description and marked the path_alias module hidden to avoid introducing any user-facing change.

catch’s picture

Issue summary: View changes
  1. +++ b/core/core.services.yml
    @@ -1222,11 +1221,6 @@ services:
         tags:
           - { name: event_subscriber }
    -  path_subscriber:
    -    class: Drupal\Core\EventSubscriber\PathSubscriber
    -    tags:
    -      - { name: event_subscriber }
    -    arguments: ['@path.alias_manager', '@path.current']
       route_access_response_subscriber:
         class: Drupal\Core\EventSubscriber\RouteAccessResponseSubscriber
         tags:
    @@ -1357,12 +1351,6 @@ services:
    
    @@ -1357,12 +1351,6 @@ services:
         arguments: ['@current_route_match']
         tags:
           - { name: route_processor_outbound, priority: 200 }
    -  path_processor_alias:
    -    class: Drupal\Core\PathProcessor\PathProcessorAlias
    -    tags:
    -      - { name: path_processor_inbound, priority: 100 }
    -      - { name: path_processor_outbound, priority: 300 }
    -    arguments: ['@path.alias_manager']
       route_processor_csrf:
    

    I think it's fine to remove these outright, because nothing should interact with them directly.

    The one thing that might happen is that someone has code altering the service definitions, which is equivalent to doing hook_module_implements_alter(). If this is the case, then it's reasonable for them to need to update their code for a minor. Nothing should be using the services directly in the same way you wouldn't call a hook implementation directly.

  2. +++ b/core/core.services.yml
    @@ -1754,3 +1742,32 @@ services:
    +
    +  # Path Alias services, defined here to be available even when "path_alias" is
    +  # not enabled yet. These will replace the legacy core services, which are now
    +  # deprecated and will be removed in Drupal 9.
    +  # See https://www.drupal.org/node/3092086
    

    Do these also need a @todo to remove in Drupal 9 - they should be OK to remove in the first release where we can guarantee path_alias module is enabled.

  3. +++ b/core/lib/Drupal/Core/Path/AliasManager.php
    @@ -115,12 +121,37 @@ class AliasManager implements AliasManagerInterface, CacheDecoratorInterface {
    +
    +      // Despite being two different services, hence two different class
    +      // instances, both the new and the legacy alias managers need to share the
    +      // same internal state to keep the path/alias lookup optimizations
    +      // working.
    +      try {
    +        $alias_manager = \Drupal::service('path_alias.manager');
    +        if ($alias_manager instanceof $new_class) {
    +          $synced_properties = ['cacheKey', 'langcodePreloaded', 'lookupMap', 'noAlias', 'noPath', 'preloadedPathLookups'];
    +          foreach ($synced_properties as $property) {
    +            $this->{$property} = &$alias_manager->{$property};
    +          }
    +        }
    +      }
    

    Oof. But can't think of another way to do it, and at least it's temporary.

  4. +++ b/core/modules/path/path.module
    @@ -48,14 +48,16 @@ function path_help($route_name, RouteMatchInterface $route_match) {
      */
     function path_entity_type_alter(array &$entity_types) {
       /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
    -  $entity_types['path_alias']->setFormClass('default', PathAliasForm::class);
    -  $entity_types['path_alias']->setFormClass('delete', ContentEntityDeleteForm::class);
    -  $entity_types['path_alias']->setHandlerClass('route_provider', ['html' => AdminHtmlRouteProvider::class]);
    -  $entity_types['path_alias']->setListBuilderClass(PathAliasListBuilder::class);
    -  $entity_types['path_alias']->setLinkTemplate('collection', '/admin/config/search/path');
    -  $entity_types['path_alias']->setLinkTemplate('add-form', '/admin/config/search/path/add');
    -  $entity_types['path_alias']->setLinkTemplate('edit-form', '/admin/config/search/path/edit/{path_alias}');
    -  $entity_types['path_alias']->setLinkTemplate('delete-form', '/admin/config/search/path/delete/{path_alias}');
    +  if (isset($entity_types['path_alias'])) {
    +    $entity_types['path_alias']->setFormClass('default', PathAliasForm::class);
    

    Path module should have a dependency on path_alias module, so is this only needed to avoid errors during the update? If so we should add a @todo to remove the conditional.

  5. +++ b/core/modules/path_alias/src/PathAliasServiceProvider.php
    @@ -0,0 +1,76 @@
    +
    +/**
    + * Path Alias service provider.
    + *
    + * Updates core path alias service definitions to use the new classes provided
    + * by "path_alias" and marks the old ones as deprecated. The "path_alias.*"
    + * services defined in "core.services.yml" will bridge the gap.
    + *
    + * @see https://www.drupal.org/node/3092086
    + */
    

    Probably also needs a @todo to remove in Drupal 9.

Added to the release notes snippet to note the lack of upgrade path from 8.8.0-alpha1.

amateescu’s picture

Very nice work, @plach! I could only find a couple of nitpicks and an important point that need to be addressed :)

  1. +++ b/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php
    @@ -11,6 +11,11 @@
    + *   @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0.
    + *   Use \Drupal\path_alias\EventSubscriber\PathAliasSubscriber.
    ...
    + *   @see https://www.drupal.org/node/3092086
    
    +++ b/core/lib/Drupal/Core/Path/AliasManager.php
    @@ -7,9 +7,15 @@
    + *   @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0.
    + *   Use \Drupal\path_alias\AliasManager.
    ...
    + *   @see https://www.drupal.org/node/3092086
    
    +++ b/core/lib/Drupal/Core/Path/AliasManagerInterface.php
    @@ -4,6 +4,11 @@
    + * Use \Drupal\path_alias\AliasManagerInterface.
    
    +++ b/core/lib/Drupal/Core/Path/AliasRepository.php
    @@ -9,6 +9,11 @@
    + *   @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0.
    + *   Use \Drupal\path_alias\AliasRepository.
    ...
    + *   @see https://www.drupal.org/node/3092086
    
    +++ b/core/lib/Drupal/Core/Path/AliasRepositoryInterface.php
    @@ -5,15 +5,10 @@
    + * Use \Drupal\path_alias\AliasRepositoryInterface.
    
    +++ b/core/lib/Drupal/Core/Path/AliasWhitelist.php
    @@ -10,6 +10,11 @@
    + *   @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0.
    + *   Use \Drupal\path_alias\AliasWhitelist.
    ...
    + *   @see https://www.drupal.org/node/3092086
    
    +++ b/core/lib/Drupal/Core/PathProcessor/PathProcessorAlias.php
    @@ -8,6 +8,11 @@
    + *   @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0.
    + *   Use \Drupal\path_alias\PathProcessor\AliasPathProcessor.
    ...
    + *   @see https://www.drupal.org/node/3092086
    

    The indentation is off in these places, and the "Use ... " line needs an "instead." suffix :)

  2. +++ b/core/lib/Drupal/Core/Update/UpdateServiceProvider.php
    @@ -41,11 +41,11 @@ public function alter(ContainerBuilder $container) {
    -    if ($container->hasDefinition('path_processor_alias')) {
    -      $container->getDefinition('path_processor_alias')
    -        ->clearTag('path_processor_inbound')
    -        ->clearTag('path_processor_outbound');
    +    if ($container->hasDefinition('path_alias.path_processor')) {
    +      $container->getDefinition('path_alias.path_processor')
    +          ->clearTag('path_processor_inbound')
    +          ->clearTag('path_processor_outbound');
    +      }
         }
    -  }
    

    The new indentation is a bit off here.

  3. +++ b/core/modules/system/system.install
    @@ -2395,6 +2395,11 @@ function system_update_8802() {
     function system_update_8803() {
    +  // Enable the Path module if needed.
    +  if (!\Drupal::moduleHandler()->moduleExists('path_alias')) {
    +    \Drupal::service('module_installer')->install(['path_alias'], FALSE);
    +  }
    

    I think this needs to be moved to a new update function since system_update_8803() is now part of a tagged release (8.8.0-alpha1).

xjm’s picture

+++ b/core/MAINTAINERS.txt
@@ -318,6 +318,9 @@ Page Cache
+Path Alias
+- ?
+

I'm not sure about adding a new module with no maintainer?

Who is responsible for the subsystem with the current architecture? I mean @catch's name is on "Path" so maybe this is effectively the same state as HEAD, just refactored?

catch’s picture

I'm still technically the path maintainer, but although I do try to review any major path system patches I don't actively maintain it obviously.

So the ? is more honest, but putting my name there wouldn't be less honest than the situation in HEAD.

amateescu’s picture

Regarding point 3 from my comment in #58 above: disregard that, we can't move it to a new update function because the new path_alias module needs to be enabled before installing the entity type. That will work fine for sites that are updating straight to 8.8.0-beta1, and we can provide an update helper script (e.g. that can be run with Drush) for sites that are already on alpha1.

catch’s picture

I think the update helper script is a lot more reliable in case there are sites that want to update from the alpha - it saves trying to account for two completely different database states in the 'official' update path, which adds a lot of complexity, but there's still a way for people with broken alpha installs to repair their site if they need to.

Berdir’s picture

  1. +++ b/core/modules/path_alias/tests/modules/path_alias_deprecated_test/path_alias_deprecated_test.module
    @@ -8,20 +8,20 @@
      */
    -function path_deprecated_test_path_insert($path) {
    +function path_alias_deprecated_test_path_insert($path) {
       // Nothing to do here.
     }
     
     /**
      * Implements hook_path_update().
    

    A bit confused about the rename of the test module, that was just there to test the old hooks, it's path hooks that are deprecated, not path_alias hooks? I see that it is moved from system/tests to pathauto/tests, but that too seems a bit strange, why should path_alias be responsible for testing what has been deprecated in \Drupal\Core\Path?

  2. +++ b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php
    @@ -38,7 +38,7 @@ class RouteProviderTest extends KernelTestBase {
       /**
        * Modules to enable.
        */
    -  public static $modules = ['url_alter_test', 'system', 'language'];
    +  public static $modules = ['url_alter_test', 'system', 'language', 'path_alias'];
     
       /**
    

    This change is going to break contrib kernel tests that interact with the alias storage again. A bit annoying for the projects that already updated their tests for D8.8 :-/

plach’s picture

Thanks for the reviews, the attached patch should address them. Including an updated review patch.


@amateescu, #58:

1: That was a trick to prevent PHPUnit from complaining about deprecated classes backing non-deprecated services. Unfortunately I was not able to find any other workaround besides "breaking" its parsing logic.

amateescu’s picture

Status: Needs review » Reviewed & tested by the community

I tested this patch manually following these steps:

- installed Drupal 8.6.0
- enabled the Language module, added a custom language
- added three aliases in all three possible languages: language neutral, English, and the custom language added above

Then I updated the code base to 8.8.x and applied this patch.

Navigating to the frontpage in a browser throws an exception that it can't find the path_alias table. That's expected since #2336597: Convert path aliases to full featured entities. It would be nice to have a followup issue to at least discuss whether we can fail more gracefully for this specific database exception (missing path_alias table), but it's not at all in the scope of this issue.

Navigating to update.php worked fine, as well as running all the updates. After that, the site was functional again, all the three aliases were still working, and the new path_alias module added by this patch was enabled (checked by looking at core.extension with Drush).

After that, I wiped the site's database, restored the dump with the 8.6.0 site, and checked how Drush copes with this patch:

- drush status: worked fine with no errors
- drush cr: same
- drush updb -y: applied all the updates successfully, and then I could perform all the manual checks again successfully.

The patch itself might look a bit ugly and even non-compliant with the deprecation coding standards, but it does handle BC perfectly in my opinion. And the upside is that we only have to live with this "ugliness" for a few months, until Drupal 9.0 :)

plach’s picture

plach’s picture

Please, disregard #66, patches were quite garbled. Here's a pair of new ones:

  • path-alias_module-3084983-67.d8.patch is a straight reroll of #64 on 8.9.x.
  • path-alias_module-3084983-67.d9.patch is a straight reroll of #64 on 9.0.x with the diff in path-alias_module-3084983-67.d8-d9-diff.txt applied.
catch’s picture

OK I'm not finding anything much to complain about in the patch - the backwards compatibility layers are not pretty, but they're temporary.

The main question is whether this can still get into the 8.8.x alpha.

The thing that makes this tricky is:

 function system_update_8803() {
+  // Enable the Path module if needed.
+  if (!\Drupal::moduleHandler()->moduleExists('path_alias')) {
+    \Drupal::service('module_installer')->install(['path_alias'], FALSE);
+  }
+
   $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
   if (!$entity_definition_update_manager->getEntityType('path_alias')) {

i.e. we have to enable the path_alias module prior to adding the path alias entity type, since both will happen in the same update when updating from Drupal 8.7.x, and the module has to be enabled for its entity type to be installed.

A site on 8.8.0-alpha1 will already have the path alias entity type installed, so the upgrade path from the alpha would be completely different: enabling the module then re-assigning the provider for the already-existing entity type.

This is what makes it difficult to provide an update from both 8.7 and 8.8 alpha - that there would be two completely different code paths to support, and it would add risk to the 8.7-8.8 upgrade path (the majority of updates) trying to support both.

We're pretty sure we can provide a one-off script to update from alpha to beta (enabling the module and switching the entity type provider) - which is a much more known quantity if you know you're dealing with that situation than dynamically switching the upgrade path for everyone, and the script would only be necessary for a handful of sites at most.

The further question is what happens if this doesn't land in 8.8.0-alpha1. We won't be able to do any new 9.x deprecations in 8.9.x, but we could potentially commit it (to either 8.9.x or 9.1.x) with deprecations for Drupal 10. One advantage of a Drupal 9 commit is that Symfony 4 supports deprecated aliases, which would remove some of the bc layer ugliness here, although obviously removing the deprecations altogether in Drupal 9 would be nice too.

For a post-8.8 commit the upgrade path looks like getting the module enabled and switching the provider - i.e. the same thing that would need to happen in an alpha-beta update script more or less.

One last thing, if this doesn't land in Drupal 8.8, there is one thing we can do to make things easier in Drupal 9.

The whole point of moving this code to a module, is to make the entire path alias subsystem optional in Drupal 9. However it's easier to make the module optional if we know that it's already required and enabled on every site. If we add the new module in 9.0.x then make it optional in 9.3.x, a site could update from 8.9.x to 9.3.x which might be tricky.

Therefore, as a fallback in 8.8.x, we could just add a path_alias stub module with .info.yml only, as a new required (and potentially hidden) module, then future updates are just moving code as opposed to trying to enable the new module at the same time.

Tagging with needs release manager review so xjm gets the final decision here - while I didn't work on the patch I'm a bit biased towards path alias system clean-up.

xjm’s picture

So all things considered I guess I do agree it's preferable to cluster these changes in the same minor release as #2336597: Convert path aliases to full featured entities and friends. That way we're only altering the API and data model once, rather than twice, and very few sites are probably using this.

That said, could we have a new update hook that's conditional on whether the module's enabled or not? So we retain the conditional change in system_update_8803() that enables the module, but then also add a separate update hook in 8805 that does the same thing (plus updates the entity definition's provider to be path_alias.module as @catch points out).

amateescu’s picture

jibran’s picture

+++ b/core/modules/system/system.install
@@ -2555,3 +2555,39 @@ function system_update_8804(&$sandbox = NULL) {
+  // system_update_8803() ran as part of Drupal 8.8.0-alpha1, in which case we

Do we want to add a dedicated DB dump of 8.8.0-alpha1 to test this?

catch’s picture

I don't think we should add test coverage for 8.8.0-alpha1, the upgrade path is 'best effort' since alphas aren't supported at all for production sites, similar to how we don't add test coverage for constructor bc layers. #70 looks good.

plach’s picture

Title: Move all the code related to path aliases to a new (required) "path_alias" module » [PP-1] Move all the code related to path aliases to a new (required) "path_alias" module
Status: Reviewed & tested by the community » Postponed

While working on the update path @amateescu found this issue: #3092855: Installing a module in an update function should not result in automatically installing entity type/field definitions.

@amateescu, @catch, and I briefly discussed it and decided that this should be postponed on it, otherwise it would be introducing a potential update path issue.

plach’s picture

plach’s picture

Title: [PP-1] Move all the code related to path aliases to a new (required) "path_alias" module » Move all the code related to path aliases to a new (required) "path_alias" module
Status: Postponed » Reviewed & tested by the community

(for reals now)

plach’s picture

@amateescu pointed out in Slack that we can remove the previous definition installations from system_update_8803(): sites having already run it won’t care and sites not having run it yet won’t need them.

This also removes the core keys after #3072702: Core extensions should not need to specify the new core_version_requirement in *.info.yml files so that 9.0.x can be installed landed, which makes the D9 version of the patch identical to D8, aside from context differences.

amateescu’s picture

The interdiff from #76 looks good to me, +1 for keeping it RTBC :)

xjm’s picture

Issue tags: +9.0.0 release notes

Is it worth repeating the manual testing from #65 for #76?

That said, this seems like a good approach for the upgrade path and OK to backport to 8.8.x still so that it's in the same minor as the other changes.

We probably need to mention this in the 9.0.0 release notes as well just as a reminder that sites or modules will need to add a dependency on or check for the new module for stuff to continue working as expected.

  • catch committed 2c08ab2 on 9.0.x
    Issue #3084983 by plach, amateescu, catch, Berdir, xjm: Move all the...

  • catch committed 0a7bb28 on 8.9.x
    Issue #3084983 by plach, amateescu, catch, Berdir, xjm: Move all the...

  • catch committed 4a0c73c on 8.8.x
    Issue #3084983 by plach, amateescu, catch, Berdir, xjm: Move all the...
catch’s picture

Version: 8.9.x-dev » 8.8.x-dev
Status: Reviewed & tested by the community » Fixed

#76 definitely won't make any difference for sites on the alpha, it would only affect sites upgrading from 8.7 but that's covered by the upgrade path tests.

Committed/pushed to 9.0.x, 8.9.x and 8.8.x

Probably the patch that actually makes path module optional in 9.x will be the one that ends up in the release notes, but let's leave this tagged anyway.

Tricky issue but very glad we were able to get it done, enables a lot more clean-up in 9.x

Wim Leers’s picture

Published the change record: https://www.drupal.org/node/3092086.

dww’s picture

This breaks all contrib Kernel tests that have anything to do with path aliases, since they now have to install path_alias separately.
See, for example #3089688: Make the build pass on Drupal 8.8
That's cool, the drop is always moving and all. ;)

However, the way we handle \Drupal::VERSION during alpha/beta makes it even harder for contrib to continue to support 8.7.x and 8.8.x-dev.
@see #3093130: Between alpha1 and X.Y.0 official \Drupal::VERSION should not be "8.8.0-dev" since it doesn't work for Semver::satisfies()

Any objections to adding some of that wisdom to the (now published) CR so other contrib maintainers can keep their Kernel tests working on the currently supported versions of core?

Thanks,
-Derek

dww’s picture

I added https://www.drupal.org/node/3092086/revisions/view/11636531/11636895 for now. Feel free to make further edits as desired.

Thanks,
-Derek

plach’s picture

@dww:

Very useful addition, Derek, thank you!

dww’s picture

@plach re: #86 you're welcome!

However, I'm happy to report that I just ripped it back out of the CR, since a much better solution is now in 8.8.x:

#3093257: Install path_alias by default in kernel tests to minimize the impact on contrib tests supporting 8.7.x

:) Yay!

Cheers,
-Derek

xjm’s picture

Issue summary: View changes

This is no longer incompatible with alpha1, so updating the release note.

jibran’s picture

This has broken subpathauto module see #3093377: Fix the failing HEAD.

Berdir’s picture

Also pathauto: #3093401: Compatibility with Drupal 8.8.0-beta1, although we'd have almost managed to make it compatible if we wouldn't have hardcoded the entity class name ;)

One thing I noticed there is that the BC layer is quite confusing, as two people including @jibran tried to "fix" my patch which uses the non-deprecated interface.

Makes me wonder if we could somehow invert that definition and define the real services in path_alias.services.yml and add a fallback in \Drupal\Core\CoreServiceProvider::alter() if they aren't there?

Berdir’s picture

Also, the reason this actually broke subpathauto is that it uses the path_processor_alias service directly, for which we didn't provide BC. It would be trivial for us to keep that service in place with a deprecation warning so that subpathauto doesn't need to either require 8.8 or add workarounds per my comment over there?

Krzysztof Domański’s picture

Status: Fixed » Needs work
FileSize
841 bytes

When we change the core/composer.json, we should also update the composer.lock.

See also #3089007: There is no information in the documentation about creating patches with the changes in composer.json and composer.lock files.

Berdir’s picture

> Also, the reason this actually broke subpathauto is that it uses the path_processor_alias service directly, for which we didn't provide BC. It would be trivial for us to keep that service in place with a deprecation warning so that subpathauto doesn't need to either require 8.8 or add workarounds per my comment over there?

Ah, might not be so trivial after all. It couldn't be a tagged service, or it would be called twice. So we could maybe keep the service without the tags, in case someone had injected it. Still not quite sure why subpathauto even did that, didn't check the code.

plach’s picture

Status: Needs work » Reviewed & tested by the community

#92 looks good, thanks!

plach’s picture

  • catch committed d1eec07 on 9.0.x
    Issue #3084983 by plach, amateescu, Krzysztof Domański, catch, Berdir,...

  • catch committed 15a2f4f on 8.9.x
    Issue #3084983 by plach, amateescu, Krzysztof Domański, catch, Berdir,...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed to 9.0.x, 8.9.x, 8.8.x, thanks!

  • catch committed 0c4d3d4 on 8.8.x
    Issue #3084983 by plach, amateescu, Krzysztof Domański, catch, Berdir,...
plach’s picture

Status: Fixed » Closed (fixed)

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

pfrenssen’s picture

The change to `system_update_8803()` is problematic. We should not enable modules during `hook_install()` since the database structure is in a broken state. This should be done in a post_update hook.

There is an issue filed for Organic Groups reporting that `system_update_8803()` breaks (ref. https://github.com/Gizra/og/issues/572). In this case the problem is that by installing a module the entire router is being rebuilt. This seems a very risky operation when the database is not in a known good state.

Berdir’s picture

A post update would be too late for this I think, as without the module, the whole site is going to be in a fragile state and alias lookups would fail.