Problem/Motivation

Drupal entities typically provide their routes through a route_provider handler. These route_provider classes provider boilerplate routing, so these can be removed from .routing.yml files

Steps to reproduce

See for example https://git.drupalcode.org/project/domain/-/blob/4.x/domain/domain.routi... and https://git.drupalcode.org/project/drupal/-/blob/main/core/lib/Drupal/Co...

Proposed resolution

Provide entity routes through EntityRouteProviderInterface

Remaining tasks

  1. Write a merge request
  2. Review
  3. Commit

User interface changes

The edit and delete pages are titled Edit %label and Delete %label, because their titles now come from EntityController::editTitle() and deleteTitle() instead of the static ones the routing files carried. Tab titles are unchanged, since those come from the local task definitions. The domain alias edit and delete routes gain _admin_route, which domain's already had, so those two pages use the admin theme and load the parent domain without configuration overrides.

API changes

This is a backwards-compatibility break. Two of the three items below fail silently, so they are worth a change record and a release note.

  • DomainAliasController::addAlias() is REMOVED. The controller class survives with listing() and listingTitle(). Its prepopulation of the parent domain moves to DomainAliasForm::getEntityFromRouteMatch(), which is the seam core calls. Anything calling that method, or pointing a route of its own at it, breaks outright.
  • The add form's form ID changes from domain_edit_form to domain_form. The entity type declares no add form handler, so the generated add route runs the default operation where the old route named domain.edit explicitly. An implementation of hook_form_domain_edit_form_alter() written to catch the add form silently stops firing on it. An alter written against hook_form_domain_form_alter() is unaffected, because that is the base form ID hook and it already fires for both operations. The domain alias add form keeps the ID domain_alias_form it already had, since the removed controller also ran the default operation.
  • Five form routes move out of the routing files and are generated from the link templates: entity.domain.add_form, entity.domain.edit_form, entity.domain.delete_form, entity.domain_alias.edit_form and entity.domain_alias.delete_form. The names core derives match the names those routes already had, and the two historical names survive as aliases, entity.domain.add of entity.domain.add_form and domain_alias.add of entity.domain_alias.add_form, so Url::fromRoute() keeps working for both. Both entity types now declare a route_provider handler, and domain_alias declares a DomainAliasHtmlRouteProvider that keeps the add route's own access requirements rather than core's _entity_create_access.

No contributed project that integrates with Domain was found to reference DomainAliasController::addAlias(), to alter domain_edit_form, or to name entity.domain.add or domain_alias.add, so the exposed cases are custom code and any project not checked here.

Data model changes

None

AI-Generated: Yes (Claude Code was used to help draft this issue summary and to write the code and tests on the merge request. I reviewed and ran the work myself before posting it.)

Issue fork domain-3615915

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

idebr created an issue. See original summary.

mably made their first commit to this issue’s fork.

mably’s picture

Status: Active » Needs review
mably’s picture

Issue summary: View changes
idebr’s picture

  1. The 'add-form' link template can be added here or in a follow-up, so some more boilerplate can be removed from the routing.yml. This is actionable for the domain_alias.add and be done here or in a follow-up issue
  2. Routes should have a primary name, preferably the entity.[entity_type].* version, and deprecate aliases. See for reference https://www.drupal.org/node/3317784. This is actionable for the domain.admin and domain_alias.admin and be done here or in a follow-up issue
mably’s picture

Both points are addressed, the first one here and the second one in a follow-up.

1. The add-form link template is now declared on the domain alias entity type, so domain_alias.add leaves the routing file and the route comes from the route provider as entity.domain_alias.add_form, with the old name kept as an alias. DomainAliasController::addAlias() is deleted: the parent domain record now reaches the form through DomainAliasForm::getEntityFromRouteMatch().

Two things the generated route cannot express, so the alias entity type gets a small DomainAliasHtmlRouteProvider that overrides getAddFormRoute():

  • DefaultHtmlRouteProvider::getAddFormRoute() sets _entity_create_access: domain_alias, which reaches DomainAliasAccessControlHandler::checkCreateAccess(). That method is inherited from the domain record handler and tests administer domains and create domains, so it would grant and deny the wrong people. The route keeps _entity_access: domain.update and _permission: create domain aliases, which is what it required before.
  • Nothing upcasts the {domain} parameter any more. EntityResolverManager only adds a parameter for the entity type the form itself builds, and the old route was upcast solely because the deleted controller type hinted DomainInterface $domain. The provider now sets that parameter conversion explicitly.

The page title is unchanged: EntityController::addTitle() renders "Add domain alias" from the entity type label. The route gains _admin_route, like the edit and delete routes already in this issue. New kernel coverage asserts the route, the alias, and that the form takes the parent domain record from the path; the functional test now asserts the saved alias still belongs to the domain record it was added from.

2. The route naming is filed as #3616784: Use entity.domain.collection as the primary name of the domain listing route rather than done here, because deprecating domain.admin is more than a rename. An alias is resolved by name and is never matched against a path, and nothing in core resolves an alias for a consumer keyed on a route name: local tasks (base_route), local actions (appears_on), the menu active trail and hook_help() all compare the matched route name as a plain string, and getRouteAliases() has no caller in core. Six contributed projects declare base_route: domain.admin for their tabs on the Domains page, so the rename needs a compatibility layer that rewrites the deprecated names for the length of the deprecation. The follow-up has the details.

The branch is also rebased on the current 4.x.

AI-Generated: Yes (Claude Code was used to help draft this comment and to write the code and the tests on the merge request. The new tests were confirmed to fail against the previous code and to pass with the change.)

mably’s picture

Issue summary: View changes