Problem/Motivation

Providing a UI for an entity type involves a lot of boilerplate code.

In particular, three YML files have to be provided:

- MODULE.links.menu.yml
- MODULE.links.task.yml
- MODULE.links.action.yml

These contain repetitive boilerplate code, where furthermore it's very easy to make a mistake and match up the route names incorrectly.

Proposed resolution

Add a new handler type for entities, 'link_provider'. This defines menu, task, and action links for an entity type.

Provide two link provider handlers in the Entity component that follow the standard admin UI pattern (for example, as used by Media module's entity types):

- DefaultContentEntityLinkProvider
-- defines a local task link for the collection under admin/content, alongside the Content admin listing
-- defines a menu link for the collection as well, so it appears in the admin toolbar
-- defines the 'add new entity' action link on the collection page
-- defines a set of local tasks link for view/edit/delete for a single entity

- DefaultConfigEntityLinkProvider
-- defines a menu link for the collection under admin/structure
-- defines the edit form local task link, which allows Field UI to attach its own tasks for the case that this config entity is the bundle for a content entity.

Both of these link provider handler classes expect to find the route names created by core's route_provider classes.

Entity types that customize their route provider to change or add routes would need to use their own link_provider handler (or keep using hardcoded link plugins of course).

Note that contextual links aren't provided by these, because they're part of Core-core, and contextual links are in a core module.

Remaining tasks

- Get a review from one of the managers of the plugin system. YAML plugins are not discoverable in core/lib (unlike annotation-based plugins), and so the current patch declares the plugin derivatives that are in core/lib/Core/Entity in system module's YAML files. This approach needs framework manager review.

Follow-ons

Convert core's entity types to use these handlers. This is left as as follow-up, because changing an entity type to use a link provider handlers will cause the plugin IDs of the link plugins to change. It's not clear whether the BC policy on plugin IDs applies to menu plugins, and there is no policy or defined way on how to handle YAML plugin ID changes -- see #2922451: [policy no patch] Make it possible to mark plugins as deprecated.

User interface changes

None.

CommentFileSizeAuthor
#89 2976861-nr-bot.txt17.9 KBneeds-review-queue-bot
#86 2976861-nr-bot.txt150 bytesneeds-review-queue-bot
#83 Screenshot 2022-10-10 at 3.02.02 PM.png143.86 KBbhanu951
#75 Screenshot 2022-08-11 at 2.06.24 PM.png89.26 KBbhanu951
#75 Screenshot 2022-08-11 at 2.06.37 PM.png76.75 KBbhanu951
#38 2976861-38.drupal.add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types.patch44.45 KBjoachim
#32 interdiff.2976861.30-32.txt1.47 KBjoachim
#32 2976861-32.drupal.add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types.patch41.82 KBjoachim
#30 interdiff.2976861.25-30.txt3.05 KBjoachim
#30 2976861-30.drupal.add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types.patch41.34 KBjoachim
#25 2976861-25.drupal.add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types.patch40.32 KBjoachim
#23 interdiff.2976861.18-22.txt12.42 KBjoachim
#22 2976861-22.drupal.add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types.patch40.31 KBjoachim
#18 2976861-18.core_.add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types.patch39.17 KBjoachim
#17 2976861-17.core_.add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types.patch38.45 KBjoachim
#15 2976861-15.drupal.add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types.patch34.72 KBjoachim
#12 2976861-12.drupal.add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types.patch34.69 KBjoachim
#7 2976861-7.drupal.add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types.patch23.72 KBjoachim
#5 2976861-5.drupal.add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types.patch23.55 KBjoachim

Issue fork drupal-2976861

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

joachim created an issue. See original summary.

bojanz’s picture

The Entity API contrib has code for this that can be borrowed (written by tstoeckler)

joachim’s picture

Assigned: Unassigned » joachim

Thanks for the info! I'll have a look.

Assigning to myself while I work on this.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

joachim’s picture

Issue summary: View changes
Status: Active » Needs review
StatusFileSize
new23.55 KB

Here's a patch.

A few notes:

- The declaration of the plugin derivers is in system module. That's because YAML plugin discovery doesn't look in core/lib/, unlike annotated class plugin discovery.
- I've not handled the collection tab for content entities, because that's due to change in #2862859: Create a top level, extendable, "Content" admin menu route that behaves like "Structure".

Status: Needs review » Needs work

The last submitted patch, 5: 2976861-5.drupal.add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

joachim’s picture

Fixed the codesniffer issues & the failing test.

joachim’s picture

Assigned: joachim » Unassigned
jhedstrom’s picture

This would be really great!

I think a test should be added, similar to the RouteProviderTest. One of the test entity types in the entity_test module should updated to use this link provider, and the new test can simply verify the menu links are created as expected.

joachim’s picture

> One of the test entity types in the entity_test module should updated to use this link provider, and the new test can simply verify the menu links are created as expected.

I've made a start on this at DrupalEurope. I'm making a new entity type within that module, so it doesn't affect existing tests.

sam152’s picture

Getting something like this into core would be awesome, the set of links in DefaultContentEntityLinksProvider are added to almost every entity type, so it would be a huge DX win.

I can't think of a better way to connect the dots between the routes and links, besides using the established naming conventions. The template method pattern used is consistent with the route providers, so +1 on consistency.

joachim’s picture

Thanks for the review!

Here is an updated patch which fixes a few minor bugs, and adds a test entity type to the entity_test module that's inside system module's tests.

However, I can't get this entity type to appear in the UI -- its route provider handler doesn't seem to fire. I've probably got some very minor thing not declared properly, but I can't see it.

Status: Needs review » Needs work
joachim’s picture

Status: Needs work » Needs review

> However, I can't get this entity type to appear in the UI -- its route provider handler doesn't seem to fire. I've probably got some very minor thing not declared properly, but I can't see it.

Entirely the fault of my local setup -- I had another entity_test module in my modules/custom folder!!!

joachim’s picture

Fixed the failing existing tests -- due to the router service not being typehinted as an interface.

joachim’s picture

> - I've not handled the collection tab for content entities, because that's due to change in #2862859: Create a top level, extendable, "Content" admin menu route that behaves like "Structure".

I've spoken to @ifrik at DrupalEurope about #2862859: Create a top level, extendable, "Content" admin menu route that behaves like "Structure", and how soon that might make it into core. The answer is not anytime soon, so this patch here should probably be changed to provide the collection tab for content entities.

joachim’s picture

Added kernel tests.

The functional test fails, but the machine I am on is too slow to keep running them to figure them out. Would be great if someone else could move these forward.

So, still to do:

- decide whether having system module declare the plugin deriver classes that are in Core\Entity is OK
- decide whether the awkward checking for existence of routes is OK
- add a collection tab for content entity types
- finish the functional test

joachim’s picture

> - add a collection tab for content entity types

Done.

sam152’s picture

This is seriously nice work and will be a huge DX win. Adding a progress review.

As for the other outstanding points, I'm not an expert on the system module, but the use of the deriver here seems reasonable and I can't think of a good alternative.

  1. +++ b/core/lib/Drupal/Core/Entity/EntityType.php
    @@ -540,6 +540,13 @@ public function hasRouteProviders() {
    +  /**
    +   * {@inheritdoc}
    +   */
    +  public function hasLinkProvider() {
    +    return !empty($this->handlers['link_provider']);
    +  }
    
    +++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
    @@ -274,6 +274,14 @@ public function hasRouteProviders();
    +  /**
    +   * Indicates if this entity type has a link provider.
    +   *
    +   * @return bool
    +   *   TRUE if the entity type has a link provider handler; FALSE otherwise.
    +   */
    +  public function hasLinkProvider();
    

    I think maybe the reason we have methods like hasFormClasses and hasRouteProviders is because those types of handlers have sub-elements, so a check to hasHandlerClass wouldn't be sufficient.

    Might be worth just leaning on hasHandlerClass to keep our interface tidy? This is more a preference though, feel free to disagree.

  2. +++ b/core/lib/Drupal/Core/Entity/Menu/BaseEntityLinksProvider.php
    @@ -0,0 +1,240 @@
    + * \Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider, and makes assumptions
    + * about the names of routes that class provides.
    

    Lets add the class as a @see as well.

    Maybe we could say instead "uses the established convention for route names" or something and maybe even go as far as to describe what those conventions are.

  3. +++ b/core/lib/Drupal/Core/Entity/Menu/BaseEntityLinksProvider.php
    @@ -0,0 +1,240 @@
    +   * This is a wrapper around the route provider's getRouteByName(), which
    +   * throws an exception for routes that are not found.
    

    I dug into this again after our conversation at DE, looks like a work-around to the exception based control flow would be to call getRoutesByNames([$route_name]) and check if it was empty or not. Not sure if this is the lesser of two evils.

  4. +++ b/core/lib/Drupal/Core/Entity/Menu/DefaultConfigEntityLinksProvider.php
    @@ -0,0 +1,50 @@
    +      $link['description'] = t('Create and manage fields, forms, and display settings for @plural-label.', [
    +        // TODO: need input from translation experts on whether this is right!
    +        '@plural-label' => $this->entityType->getPluralLabel(),
    +      ]);
    

    I assume this is correct, because the plural label will already be translated: label_plural = @Translation("content items"),

  5. +++ b/core/lib/Drupal/Core/Entity/Menu/DefaultConfigEntityLinksProvider.php
    @@ -0,0 +1,50 @@
    +    // Config entities have just one task here, but Field UI module hangs its
    +    // own tabs off it.
    

    Does field UI really do stuff with config entities?

  6. +++ b/core/modules/system/tests/modules/entity_test/src/EntityUITestListBuilder.php
    @@ -0,0 +1,35 @@
    +class EntityUITestListBuilder extends EntityListBuilder {
    
    +++ b/core/modules/system/tests/modules/entity_test/src/EntityUITestTypeListBuilder.php
    @@ -0,0 +1,31 @@
    +class EntityUITestTypeListBuilder extends EntityListBuilder {
    

    Maybe list builders are the next kind of boilerplate we need to address 😅

  7. +++ b/core/modules/system/tests/modules/entity_test/src/Form/EntityUITestTypeForm.php
    @@ -0,0 +1,57 @@
    +  /**
    +   * TODO: write function documentation.
    +   */
    

    {@inheritdoc}?

  8. +++ b/core/modules/system/tests/modules/entity_test/src/EntityUITestTypeListBuilder.php
    @@ -0,0 +1,31 @@
    +/**
    + * Provides the list builder handler for the Entity UI Test Type entity.
    + */
    +class EntityUITestTypeListBuilder extends EntityListBuilder {
    

    Maybe we can lean on \Drupal\entity_test\EntityTestListBuilder here?

joachim’s picture

Assigned: Unassigned » joachim

Thanks for the detailed review!

> I think maybe the reason we have methods like hasFormClasses and hasRouteProviders is because those types of handlers have sub-elements, so a check to hasHandlerClass wouldn't be sufficient.

There is hasListBuilderClass() and hasViewBuilderClass(), which don't have sub-elements.

> a work-around to the exception based control flow would be to call getRoutesByNames([$route_name])

I think that's definitely better. Thanks for the tip!

> Does field UI really do stuff with config entities?

Yes, because the node type is a config entity, and Field UI adds tabs for node fields to the node type form page. I'll expand that explanation.

> Maybe list builders are the next kind of boilerplate we need to address 😅

I think I maybe filed an issue very early on in the D8 cycle. I'll see if I can dig it up.

I'm working my way through the other suggested changed, new patch coming in a bit.

goodboy’s picture

  1. I see some copy-pasted code.
    For example,
    +  protected function getEditFormTaskLink($base_plugin_definition) {
    +    // Config entities have just one task here, but Field UI module hangs its
    +    // own tabs off it.
    +    if ($this->routeExists("entity.{$this->entityTypeID}.edit_form")) {
    +      $link = $base_plugin_definition;
    +
    +      $link['title'] = $this->t('Edit');
    +      $link['route_name'] = "entity.{$this->entityTypeID}.edit_form";
    +      $link['base_route'] = "entity.{$this->entityTypeID}.edit_form";
    +
    +      return $link;
    +    }
    +  }
    

    It may be replaced to

    +  protected function getEditFormTaskLink($base_plugin_definition) {
    +    // Config entities have just one task here, but Field UI module hangs its
    +    // own tabs off it.
    +    $edit_form_link = "entity.{$this->entityTypeID}.edit_form";
    +    if ($this->routeExists($edit_form_link)) {
    +      $link = $base_plugin_definition;
    +
    +      $link['title'] = $this->t('Edit');
    +      $link['route_name'] = $edit_form_link;
    +      $link['base_route'] = $edit_form_link;
    +
    +      return $link;
    +    }
    +  }
    

    Or even can make helper function like

    private function makeLink($link) {
      return "entity.{$this->entityTypeID}.$link";
    }
    

    and then use it as

    $edit_form_link = $this->makeLink('edit_form');
    
  2. Should we check $this->routeExists() for $link['base_route'] also?
    +  protected function getEditFormTaskLink($base_plugin_definition) {
    +    if ($this->routeExists("entity.{$this->entityTypeID}.edit_form") && $this->routeExists("entity.{$this->entityTypeID}.canonical")) {
    +      $link = $base_plugin_definition;
    +
    +      $link['title'] = $this->t('Edit');
    +      $link['route_name'] = "entity.{$this->entityTypeID}.edit_form";
    +      $link['base_route'] = "entity.{$this->entityTypeID}.canonical";
    +
    +      return $link;
    +    }
    +  }
    
joachim’s picture

Further changes from #19:

> Maybe we can lean on \Drupal\entity_test\EntityTestListBuilder here?

Hmm we could, but I generally tend to think that for tests, it's best not to get clever with code reuse, because that complicates the tests in ways that aren't helpful. If two totally separate test entity types that are for totally different tests share a handler, and in the future one set of tests needs to be changed, then the two sets of tests are entangled and it makes changing them harder.

Changes from #21:

> Or even can make helper function

Good idea! I really like this! It means that all the assumptions about how route names are formed for an entity type are encapsulated in a single method. So entity types that have non-standard route names can subclass the core link providers and override just the one method.

> Should we check $this->routeExists() for $link['base_route'] also?

Yes, good catch!

joachim’s picture

StatusFileSize
new12.42 KB

Here's the interdiff -- the upload button was being glitchy.

Thanks both for your reviews!

Status: Needs review » Needs work

The last submitted patch, 22: 2976861-22.drupal.add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

joachim’s picture

sam152’s picture

Bumping this again today after having to go through and painfully create a stock standard set of links from scratch.

One last question I had, does this meet the needs of any of the existing entity types in core? It would be great to include a real-world use of this and demonstrate how much fiddling it solves?

I'm not sure if this needs a framework manager review or not.

joachim’s picture

> One last question I had, does this meet the needs of any of the existing entity types in core? It would be great to include a real-world use of this and demonstrate how much fiddling it solves?

Good idea!

This default handlers produce a generic UI, such as Media provides, and other contrib modules that add an entity + entity type pair of entities.

I was planning on converting core entity types as a followup, but making a start now isn't a bad idea.

I made a quick start today, and found a problem with the patch: there's no documentation on what the IDs of the generated menu/task/action plugins are, which isn't good when you need to connect other parts of the UI to them, especially as getting those IDs out of the database is a PITA as they all live in the cache tables. So that's something!

Quick list of core entities and and, off the top of my head, what they'll need:

- node & node type: should mostly work OOTB, except for the admin/content menu and task which is different.
- comment & comment type: these don't use a route provider handler, so we can't work with them.
- block content type: might need a few tweaks, still figuring this one out
- media: should work OOTB
- terms & vocabs: might be a bit tricky, as these basically are mixed together in the same UI
- user: probably best leave it, as Profile contrib module and other modules will be depending on the plugin names

joachim’s picture

Ok, I've got block content working! Requires a BlockContentTypeLinksProvider with a few overrides:

- getCollectionMenuLink() needs to return NULL, as there's no menu link for the collection
- getCollectionTaskLink() needs to return a link, as the collection has a task link instead
- the add route is hardcoded, so I've added a helper getAddActionRouteName() to BaseEntityLinksProvider for this sort of case, so that specifying the different route name doesn't require repeating a load of code.

I think rather than either adding this follow-up work as patches here, or creating posting patches to follow-up issues where they'll fail tests and get out of sync with work here, I might create a repo on github as a sandbox for this. That way I can rebase commits so there's always a 2976861 branch & then a 'convert core entities' branch on the tip of it.

joachim’s picture

> I'm not sure if this needs a framework manager review or not.

I would say it needs review from one of the managers of the plugin system, on whether it's OK to declare the plugin derivatives in system module's YAML files, while the deriver class is in core/lib.

Furthermore, regarding the follow-up to use this for core entities, we potentially have a BC problem.

Converting an existing entity type to use a link handler means changing the IDs of menu link, task, and action plugins from the hardcoded ID in YAML files to the derived IDs. There's no way that a change of ID can be avoided, as we use a deriver, so even if we were to customise the link provider handler to match the old IDs, we'd still get a deriver prefix.

The BC policy at https://www.drupal.org/core/d8-bc-policy says:

> Particular plugin classes should not be considered part of the public API. References to plugin IDs and settings in default configuration can be relied upon however.

I think it could be argued either way whether the menu system plugins are included in that, as the 3 menu plugin types aren't especially plugin-ish.

The thing is, if we can't change this over and preserve BC in a D8 minor version, then the question is, *when* can we?

joachim’s picture

Updated patch with some fixes for things that got exposed by converting block_content_type:

- added docs on plugin IDs
- added getAddActionRouteName() helper method
- fixed label for add action

Patch and interdiff created by Dorgflow.

Status: Needs review » Needs work

The last submitted patch, 30: 2976861-30.drupal.add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

joachim’s picture

I'm eating my own dogfood and using this on a project that has a lot of custom entity types.

I'm liking that this gives me the ability to create my own link handlers, and then customize the UI for several entity types in one go!

I found one bug: content entities actually need to add a menu link to their collection under admin/content as well as the tab link.

Changes since the last patch:
- fixed missing collection menu link for content entities
Patch and interdiff created by Dorgflow.

No idea what the last test failure was about - doesn't seem related.

jhedstrom’s picture

I found one bug: content entities actually need to add a menu link to their collection under admin/content as well as the tab link.

I'm not sure I follow--does this patch require they do so or something breaks?

joachim’s picture

> I found one bug: content entities actually need to add a menu link to their collection under admin/content as well as the tab link.

> I'm not sure I follow--does this patch require they do so or something breaks?

Not at all.

Prior to patch #32, entity types using the handler weren't getting a child menu item under Content when using the contrib Admin Toolbar menu. I had assumed that was Admin Toolbar not handling things in a general manner but hardcoding for core entity types (which it is for other things -- see #3028213: generalize creation of 'add ENTITY' and 'add BUNDLE' links).

But in fact, it turns out that menu item is made in core -- Media has it for instance, as does Comment.

What Media module does with its links has pretty much been my template for the handler, since Media is the core entity type with the most generic UI. (Node has special cases because it's the primary entity type, block content is placed differently, comment has special cases.)

Therefore, I've added that menu link to the handler as well, to match what Media does.

It might be that not having it has an impact in core too, say, if you use the vertical version of the toolbar, but I've not checked.

jhedstrom’s picture

Ah, that makes sense. So is there anything left to do here other than a change record and an issue summary update?

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

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

joachim’s picture

Assigned: Unassigned » joachim

> So is there anything left to do here other than a change record and an issue summary update?

Functional tests, which I'm working on.

BTW, I've created some custom route & links handlers that cover two cases that core doesn't and shouldn't have to:

- single bundle entity type (like 'user')
- entity type that has bundles, but where the bundles do not come from a config entity

Code is at https://github.com/joachim-n/entity_admin_handlers, with more explanation in the README. I think these nicely show the potential of this patch :)

joachim’s picture

And here's the patch with functional tests.

Status: Needs review » Needs work

The last submitted patch, 38: 2976861-38.drupal.add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

joachim’s picture

Issue summary: View changes
Issue tags: -Needs issue summary update

Updated the IS.

joachim’s picture

Status: Needs work » Needs review

I'm going to say that a failure in /var/www/html/core/modules/views/tests/src/Kernel/Entity/RowEntityRenderersTest.php:148 is probably not related to this patch...

joachim’s picture

Assigned: joachim » Unassigned
bojanz’s picture

One interesting note: This code currently always generates a Delete local task.

However, core only provides a Delete local task for content entities. Delete local tasks for config entities were explicitly removed in #1834002: Configuration delete operations are all over the place. I am guessing that we don't want to revert that conclusion. So we should either define the Delete local task only for content entity types, or never.

I am also unsure what our convention is for defining a List local task for the collection. Core seems to do it for many config entity types.

joachim’s picture

Status: Needs review » Needs work

Thanks for the review!

> Delete local tasks for config entities were explicitly removed in #1834002: Configuration delete operations are all over the place. I am guessing that we don't want to revert that conclusion.

Good point. Will change that.

> I am also unsure what our convention is for defining a List local task for the collection. Core seems to do it for many config entity types.

I'll look into it.

bojanz’s picture

Also wanted to say that I find the current architecture confusing from a DX standpoint. I would have preferred the contrib Entity API approach of provider per link type, cause that gives me a clear mental mapping of 1 provider to 1 file. Reading through the current codebase required me to do several double-takes to figure out which link type I'm looking at.

joachim’s picture

The reason I put all 3 link types into the same handler is that all 3 types work together to create the UI. They're indivisible. You wouldn't want to switch out the code for just one of the 3 types because it would start to get really messy.

I've been consistent (I think!) in naming all the methods so they say which link type they are about, eg getMenuLinks(), getCollectionMenuLink(). Is there anything else that could make that clearer?

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.

manuel garcia’s picture

Status: Needs work » Needs review

I'm very excited about this one, let's do it!

I had a look at the latest patch, which btw applies cleanly against 8.9.x.

I had a go at the failing test, but was unable replicate the failure locally (tested using PHP 7.3, 7.2 and 7.1).

Let's see if the failure is actually still there with the test bot then, setting to needs review for now.

joachim’s picture

@Manuel Garcia that would be great, thanks!

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

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

joachim’s picture

Made an MR so it's easier to rebase in future, and rebased to 9.5.x

larowlan’s picture

larowlan’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

Left a review, this is a huge improvement over the current setup

I wonder if its worth converting an existing entity type, e.g. taxonomy perhaps so we also get the benefit of any existing test coverage

aaronmchale’s picture

Agree this is a great addition, also left some comments.

I wonder if its worth converting an existing entity type, e.g. taxonomy perhaps so we also get the benefit of any existing test coverage

+1 one to that, let's see how many we can do here, and open follow-ups for any that aren't a simple change.

joachim’s picture

> I wonder if its worth converting an existing entity type, e.g. taxonomy perhaps so we also get the benefit of any existing test coverage

The problem with doing that is that it will change the IDs of the menu link plugins in that module. There is no clear policy on how to deprecate or remove plugins. And with menu plugins, it's even more complicated, as we can't have the old plugin still function (it would cause a double menu item). We need some sort of way to leave the old plugin ID there as an alias if we want to provide BC for any code that expects that plugin ID.

larowlan’s picture

Should we also be generating contextual links?

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

joachim’s picture

> Should we also be generating contextual links?

Not sure, as contextual links come from a core module rather than non-module core.

Does anyone have any thoughts on how to handle the menu plugin ID change?

larowlan’s picture

Good point, that feels like reason enough to forego contextual links

ravi.shankar made their first commit to this issue’s fork.

aaronmchale’s picture

Added comments to MR.

joachim’s picture

> Yeah maybe entity annotation is overreaching, the class property is probably fine for now, maybe we could discuss it more in a follow-up issue?

Follow-up sounds good to me.

Fixed the incorrect parent menu ID, and rebased on 9.5.x.

This is still blocked on the problem of how to handle menu link IDs which will change.

berdir’s picture

> This is still blocked on the problem of how to handle menu link IDs which will change.

I would not change existing entity types here, verify on test entity types and conversion can be a follow up

joachim’s picture

Status: Needs work » Needs review

I like that plan!

In which case, I think this is ready for review.

bhanu951’s picture

Hi Joachim thanks for the work you done on this issue.

I tried to test the patch locally, but seems it need work. when I applied this patch seems the link for entity type collection is missing. It also misses the local task links for revisions and translation.


drush gen entity:content

Welcome to content-entity generator!
––––––––––––––––––––––––––––––––––––––

Module machine name [web]:
➤ entity_test

Entity type label [Entity test]:

Entity type ID [entity_test]:

Entity base path [/entity-test]:

Make the entity type fieldable? [Yes]:

Make the entity type revisionable? [No]:
➤ Yes

Make the entity type translatable? [No]:
➤ Yes

The entity type has bundle? [No]:
➤ Yes

Create canonical page? [Yes]:
➤ Yes

Create entity template? [Yes]:
➤ Yes

Create CRUD permissions? [No]:
➤ Yes

Add "label" base field? [Yes]:
➤ Yes

Add "status" base field? [Yes]:
➤ Yes

Add "created" base field? [Yes]:
➤ Yes

Add "changed" base field? [Yes]:
➤ Yes

Add "author" base field? [Yes]:
➤ Yes

Add "description" base field? [Yes]:

Create REST configuration for the entity? [No]:
➤ No

The following directories and files have been created or updated:
–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
• /app/web/modules/custom/entity_test/entity_test.links.action.yml
• /app/web/modules/custom/entity_test/entity_test.links.contextual.yml
• /app/web/modules/custom/entity_test/entity_test.links.menu.yml
• /app/web/modules/custom/entity_test/entity_test.links.task.yml
• /app/web/modules/custom/entity_test/entity_test.module
• /app/web/modules/custom/entity_test/entity_test.permissions.yml
• /app/web/modules/custom/entity_test/config/schema/entity_test.entity_type.schema.yml
• /app/web/modules/custom/entity_test/src/EntityTestAccessControlHandler.php
• /app/web/modules/custom/entity_test/src/EntityTestInterface.php
• /app/web/modules/custom/entity_test/src/EntityTestListBuilder.php
• /app/web/modules/custom/entity_test/src/EntityTestTypeListBuilder.php
• /app/web/modules/custom/entity_test/src/Entity/EntityTest.php
• /app/web/modules/custom/entity_test/src/Entity/EntityTestType.php
• /app/web/modules/custom/entity_test/src/Form/EntityTestForm.php
• /app/web/modules/custom/entity_test/src/Form/EntityTestTypeForm.php
• /app/web/modules/custom/entity_test/templates/entity-test.html.twig

I used the above drush command and generated test entity. After that I deleted below four ymls and added the patch from #67 and added "link_provider" = "Drupal\Core\Entity\Menu\DefaultContentEntityLinksProvider", in my entity annotation definition and cleared cache and check the entity type collection list , but it is missing.
• /app/web/modules/custom/entity_test/entity_test.links.action.yml
• /app/web/modules/custom/entity_test/entity_test.links.contextual.yml
• /app/web/modules/custom/entity_test/entity_test.links.menu.yml
• /app/web/modules/custom/entity_test/entity_test.links.task.yml

Basically menu link for entity.entity_test_type.collection route is not available with the patch.

It is also good to add Derivative for contextual links and permissions as well.

bhanu951’s picture

Status: Needs review » Needs work
joachim’s picture

> It is also good to add Derivative for contextual links and permissions as well.

We can't add contextual links as these come from a module -- see #62.

Permissions are a totally separate thing from menu links -- there is an issue for those too: #2809177: Introduce entity permission providers.

joachim’s picture

Status: Needs work » Needs review

> I used the above drush command and generated test entity. After that I deleted below four ymls and added the patch from #67 and added "link_provider" = "Drupal\Core\Entity\Menu\DefaultContentEntityLinksProvider", in my entity annotation definition and cleared cache and check the entity type collection list , but it is missing.

You deleted too many .yml files.

I generated a content entity using Module Builder, then deleted the *two* *.links.*.yml files, added the handler line to the entity annotation, resulting in this:

 *   handlers = {
 *     "route_provider" = {
 *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
 *     },
 *     "form" = {
 *       "default" = "Drupal\test_2976861\Form\Test2976861Form",
 *       "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm",
 *     },
 *     "list_builder" = "Drupal\test_2976861\Entity\Handler\Test2976861ListBuilder",
 *     "link_provider" = "Drupal\Core\Entity\Menu\DefaultContentEntityLinksProvider",
 *   },

Did a `drush cr` and my entity UI is working fine:

- menu item in admin menu
- collection at admin/content/test_2976861, as a tab alongside nodes/files/media/comments
- action link to add a new entity
- entity has view/edit/delete tabs

bhanu951’s picture

@joachim thanks for the quick response.

Were you able to see entity_test_type link under structure? Which list bundles and manage fields?

Thats what missing after applying this patch.

joachim’s picture

Sorry, I misread which collection was missing.

I went back to Module Builder, added a bundle config entity, re-generated, removed the links yml files again.

Did you add a links provider to the type entity too? Your EntityTestType.php needs:

 *     "link_provider" = "Drupal\Core\Entity\Menu\DefaultConfigEntityLinksProvider",
bhanu951’s picture

@joachim that was missing in my code. Thanks After adding that menu link is visible under structure, but it still misses bundle list and add new bundle option in second level menu.

Pls refer attached images for difference between node and custom entity. You can see that second level menu links are not visible while admin toolbar module is enabled.

joachim’s picture

> Thanks After adding that menu link is visible under structure, but it still misses bundle list and add new bundle option in second level menu.

That's because those 2nd level links are all hardcoded in Admin Toolbar module -- see the file /admin_toolbar_tools/src/Plugin/Derivative/ExtraLinks.php. There's an issue to fix this: #3028213: generalize creation of 'add ENTITY' and 'add BUNDLE' links

bhanu951’s picture

Got it thanks @joachim

joachim’s picture

I've been working in this space for a while! :)

Thanks for your attention to detail!

nuez’s picture

I've tested the MR and as far as I can see it's working well.

Contextual links

I was expecting the contextual links to be part of this proposal, until I found in the issue comments the reasons why this wasn't included. Just saying from a 'consumer' point of view'.

Parent base route

I think the DefaultContentEnityLinksProvider might hardly ever be used 'out of the box' for the lack to control the weight, base route and control over the UI as too many tabs and menu items might be added to the top admin/content menu item.

It's easy enough to extend this class, however could we consider adding a key to the entity type definition that can be used, such as 'link_provider_base_route' and 'link_provider_weight'?

joachim’s picture

Issue summary: View changes

Thanks for reviewing!

> I was expecting the contextual links to be part of this proposal, until I found in the issue comments the reasons why this wasn't included

I've added a note about that to the IS to help future reviewers.

> It's easy enough to extend this class, however could we consider adding a key to the entity type definition that can be used, such as 'link_provider_base_route' and 'link_provider_weight'?

I'm torn between on the one hand not wanting to make this MR too complex and have it reaching into inventing new entity type properties, and the DX of not needing to subclass it.

I'll think about it :)

If the weight's not specified in the menu links defined by the LinksProvider, then they'll just be in alphabetical order won't they?

aaronmchale’s picture

In regards to the weights, I just want to highlight this issue #3306388: Delete local task should be the last tab for content entities, the proposal is awaiting sign off at the Usability Group (hopefully I'll get a chance to present it today), but it's a strong case for providing sensible default weights, at least for the Delete local task.

aaronmchale’s picture

The proposed resolution in #3306388: Delete local task should be the last tab for content entities was accepted at #3307567: Drupal Usability Meeting 2022-09-09, so that issue is good to proceed.

Would anyone here also be willing to work on that issue?

This issue should also take account of the solution in that issue, as it will directly impact the solution here.

bhanu951’s picture

StatusFileSize
new143.86 KB

Hi @joachim ,

Seems I observed one more issue. After applying this patch and patch from #3028213-23 I am seeing two menu links for custom entity bundle collection. Please look into the attached images.

I have just generated entities as discussed in #69 and added link_provider in both entity and entity type annotation.

joachim’s picture

I suspect it's because the patch in Admin Toolbar module is creating the menu items that core doesn't create currently.

If this patch gets in, Admin Toolbar would have to be changed. Or the patch in #3028213: generalize creation of 'add ENTITY' and 'add BUNDLE' links should add defensive guards in anticipation of this issue getting fixed.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new150 bytes

The Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

joachim’s picture

Status: Needs work » Needs review

Added a 10.1 branch and MR.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new17.9 KB

The Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

joachim’s picture

Status: Needs work » Needs review

The branch is up to date AFAICT.

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: -Needs tests +Needs Review Queue Initiative, +Needs framework manager review

Looking into MR 3441 I see tests were added so removing that tag.

Remaining tasks mention

Get a review from one of the managers of the plugin system. YAML plugins are not discoverable in core/lib (unlike annotation-based plugins), and so the current patch declares the plugin derivatives that are in core/lib/Core/Entity in system module's YAML files. This approach needs framework manager review.

So tagging for that.

The 1 failure in the MR seems valid.

joachim’s picture

Rebased branch 2976861-add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types for 9.5 and 2976861-10.1-add-an-entity-Links-handler-to-provide-menu-linkstasksactions-for-entity-types for 10.1.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

pasqualle’s picture

I have a question about entity collection listing: Is there a guideline, policy or d.o issue about where the entity list should go in the admin menu?
something like:
- if you extend the ContentEntity class, then your entity listing should be under admin/content/my-entity-plural
- if you extend the ConfigEntity class, then your entity listing should be under admin/structure/my-entity-plural

Some of the examples where this "guideline" does not apply:
Shortcuts (ContentEntity from core) listing is under /admin/config/user-interface/shortcut
Taxonomy terms (ContentEntity from core) listing is under /admin/structure/taxonomy/manage/%vocabulary%/overview
Image styles (ConfigEntity from core) listing is under admin/config/media/image-styles
Workflows (ConfigEntity from core) listing is under admin/config/workflow/workflows
Languages (ConfigEntity from core) listing is under admin/config/regional/language
Entity Browsers (ConfigEntity from entity_browser module) listing is under admin/config/content/entity_browser
Newsletters (ConfigEntity from simplenews module) listing is under /admin/config/services/simplenews
QRCodes (ContentEntity from qrcode module) listing is under /admin/config/services/qrcode

joachim’s picture

There isn't guidance AFAICT.

There's a common pattern for content entity type/content entity type bundle pairs of admin/content and admin/structure.

Beyond that, as you say, there's a lot of variance. But the base classes here are meant to cover the common case and can be extended for other cases.

aaronmchale’s picture

I have a question about entity collection listing: Is there a guideline, policy or d.o issue about where the entity list should go in the admin menu?

While we were working on the effort to reorganise blocks in the admin UI, we were guided by structured content principles, and we applied these to the Drupal admin menu as follows:

  • Content: this is where you create individual items of content (whether that be nodes, blocks, media, etc).
  • Structure: this is where you build the structures that your content uses (aka bundles and fields), along with different representations of your structure (thinking Views here).
  • Appearance: this is where you decide what the look and feel of your site as a whole (aka selecting a theme, and hopefully in 10.2 the global block layout).

There's a nice organisation there, from individual items up to the whole site.

This isn't formally documented anywhere that I'm aware of (maybe it should be), but we will likely use this to help inform any future reorganisation efforts of the admin UI.

In terms of how that relates to the question on this issue, it allows us to think about this a little more holistically. For collections of content entities, in general those probably fit best under Content; If it's a collection of config entities, those could live under Structure if they facilitate structured content (aka bundles and fields), but those could also live under Configuration if they are not related to structured content.

joachim’s picture

> For collections of content entities, in general those probably fit best under Content; If it's a collection of config entities, those could live under Structure if they facilitate structured content (aka bundles and fields), but those could also live under Configuration if they are not related to structured content.

Are you suggesting we split DefaultConfigEntityLinksProvider into two classes,

- one for config entities for structured content, under Admin > Structure
- one for config entities that live under Admin > Configuration

In which case, for Configuration config entities, we'd need an entity property to specify the base route.

joachim’s picture

I've decided not to wait for figuring out how to deprecate plugins.

Instead, we can use alter hooks to ensure that plugin IDs are maintained for node and node_type links. We can remove those in due course. In fact, there is already this exact same handling in node module currently!

A separate problem is that core/modules/views/tests/src/Kernel/TestViewsTest.php is failing on a recursive router rebuild, because of Views. The chain goes like this:

- views data needs to know about routes
- the router is built
- MenuRouterRebuildSubscriber tells MenuLinkManager to rebuild() links
- that calls our new EntityMenuLinkDeriver
- BaseEntityLinksProvider checks routes for things

I'm not sure why this circularity is only coming up in Views, and only in that kernel test. After all, I don't get an error when I go `drush cr` with this MR.

joachim’s picture

Ok it looks like it IS Views that's somehow the problem, as if I add this kernel test on this branch, it passes:

  public function testRouteBuilderRebuild(): void {
    $this->container->get('router.builder')->rebuild();

    $this->assertTrue(TRUE);
  }

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.