Updated: Comment #33

This issue is to solicit feedback and have discussion on the proposed architecture and the overall implementation proposal (including the interfaces, and code organization). Patches posted here are for that and checking tests, not final review.

Once this the architecture is sufficiently reviewed and discussed, let's mark this fixed and polish the implementation in the following issue: #2256521: [META] New plan, Phase 2: Implement menu links as plugins, including static admin links and views, and custom links with menu_link_content entity, all managed via menu_ui module
see: #2256497: [meta] Menu Links - New Plan for the Homestretch for the big picture

Problem/Motivation

We want both the best site builder experience and best DX.

The menu links in the administrative section and the hierarchy pages built from them serve a different role than most of the user-defined menu link.

We want the admin section to be localized (using t()) so that the administrative section is localized and the localization values can be updated as the .po files are improved on localize.drupal.org

There is a mandate to define the admin links with YAML in parallel with local tasks & local actions, would be consistent DX, and would allow them to be localized.

The menu links created by users should be based on content entities so that we can add fields and use field translation.

The user-created links can be mixed in with the administrative links, so we need a common framework that can manage an optimized hierarchy storage that include both of these. In addition we expect Views and other modules will define additional menu links, so the framework needs to cover those use cases as well.

Proposed resolution

Use a plugin type as the common framework that glues together links defined in YAML, links with an associated content entity, link defined by Views, etc.

Once we have a faux plugin manager from issue #2 giving you the menu hierarchy, convert the links to actual plugins and get all the hierarchy code out of the entity code.

The plugin ID will be the machine name of each link, so all links will now have a machine name.

Need to figure out how to have the custom menu link plugins delegate the edit form, etc to a entity form and then extract out the values needed to hand to the manager to maintain the hierarchy.

This patch is to review the basic machinery of that plugin system. It excludes the changes from phase 2 to existing forms and function to use these plugins, but they are working already with a mix of static plugins, views-backed plugins, and content entity backed plugins.

Remaining tasks

Patch is currently being developed in a sandbox - look there if you want to see the very latest or try out the whole system as per phase 2.
http://drupalcode.org/sandbox/dereine/2031809.git/tree/refs/heads/2227441
git clone --branch 2227441 http://git.drupal.org/sandbox/dereine/2031809.git menutree

User interface changes

Link titles and path will no longer be editable for static menu links.

API changes

Major shift from all content entities to a framework based on Plugins. Custom menu links would represent the combination of a content entity and a plugin instance, in analogy to the custom block module.

Since the static menu link titles and description are define in YAML and not editable, they can be discovered just like local task titles and localized using t() to achieve OOTB localization of the admin interface.

The content entity backing each custom link can be translated using entity translation, so they would have full multilingual support.

Related

#2227179: Step 2: Move the menu tree storage to a separate service.

Follow-up from #2227179: Step 2: Move the menu tree storage to a separate service..

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

pwolanin’s picture

Issue summary: View changes
jessebeach’s picture

Issue summary: View changes
xjm’s picture

Issue tags: +beta target
amateescu’s picture

Crell’s picture

Wait... what's the justification for this? Didn't we just put a crapton of work into making links entities? Can we please stop confusing plugins and entities? :-)

dawehner’s picture

pwolanin’s picture

Issue summary: View changes
Gábor Hojtsy’s picture

So would the suggested YML files be hardcoded like local tasks, routes, etc? Ie. not in the config system but as their own YML files?

Also, what would people do if they have a view that has an admin path that they want to ship with? Is it shipping the view with the module? Shipping the view and the YML file manually created somehow? In other words: how do you turn a UI created admin view to an exported view shipped with a module?

Gábor Hojtsy’s picture

As per @pwolanin the response is the following:

- yes, admin menu items would be static one-off plugin YML like local tasks, routing, etc.
- yes, UI created views would be shippable in modules because Views would have a derivative plugin handler that would expose the views configured admin items, so these would be translatable as part of the View with config translation

These work for translation.

pwolanin’s picture

For views, the data for creating the link will be in the views config entity

Gábor Hojtsy’s picture

That sounds good to me. @pwolanin and I talked about this yesterday. I think this model matches how blocks are implemented and would be fine for translatability as well as far as I understand. It may not be the nicest solution but it applies a pattern established elsewhere even if that pattern is not the work of perfection.

pwolanin’s picture

Issue summary: View changes
pwolanin’s picture

Status: Postponed » Active

a PoC patch for the 1st part of this is currently being developed in a sandbox:
http://drupalcode.org/sandbox/dereine/2031809.git/tree/refs/heads/2227441
git clone --branch 2227441 http://git.drupal.org/sandbox/dereine/2031809.git menutree

I started on this with dawehner, since it seemed like step #2 would be a really huge effort - equal in size to doing this PoC implementation. At that point, since I'm convinced we want to end up here, it seemed much more effective (and fun) to just start on this step rather than step #2 code that might be thrown away in the end.

pwolanin’s picture

Title: Step 3: Implement menu links as actual plugins » New plan, Phase 1: (prior Step 3): Implement menu links as actual plugins
Parent issue: #2178723: [meta-2] Finalize module-facing API of declaring menu links » #2256497: [meta] Menu Links - New Plan for the Homestretch
Related issues: +#2178723: [meta-2] Finalize module-facing API of declaring menu links
xjm’s picture

Issue tags: -beta target +beta blocker

If this includes changes to the storage (and I believe it does) then it'd be beta-blocking.

effulgentsia’s picture

Status: Active » Needs review
FileSize
412.38 KB

Just a patch representation of the current sandbox for people who like that sort of thing. CNR for bot, not humans.

Status: Needs review » Needs work

The last submitted patch, 16: menutree.patch, failed testing.

Gábor Hojtsy’s picture

pwolanin asked me to try out their current sandbox user experience. I DID NOT review the code or the approach. Comments:

- That you cannto edit the built-in item title/path is confusing, it shows "Path: Administration" linking to the right place though; adding "This is a module provided link. Label and path cannot be changed." or something along those lines would be good and fixing so the display of it makes more sense.
- Custom menu links have the path field way too down in the form, should be up like in D7, that and title are the primary 2 things about the menu item.
- If you translate a built-in menu item the toolbar regressed that it looses the icon of the item. That is not the case in D8.
- Menu items don't yet have a #language_select element on their form; so although you can configure new entity defaults and language selector visibility for them in language module, it does not actually appear; see admin/config/regional/content-language with language module enabled and other entity types for #language_select use
- Menu link fields title and description appear for translation settings (revisit admin/config/regional/content-language with content translation also enabled) BUT the translation settings cannot be saved on them; other entity types, eg. basic page works
- Then I wanted to go and see how does the menu item show, on trying to manage my menu (ie. not even edit the menu item just displaying the list of menu items on the admin page). I got Fatal error: Call to a member function isTranslatable() on a non-object in /Users/gabor/Web/2031809/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationManager.php on line 39
- Since now I cannot edit any menu, I went and disabled content translation again. Then I tried moving around the translated menu item to a different place and then different tree position and it still all worked. And it was still translated.

That concluded my quick review. Again I DID NOT review the code or the approach.

Gábor Hojtsy’s picture

The fatal can be reproduced by just enabling content translation module and then visiting any admin/structure/menu/manage/* no special configuration needed.

pwolanin’s picture

The fatal was due to the use of the '#type' => 'language_configuration' element in the overview form. This is a no-op in HEAD, and not relevant to the patch, so it's removed now in the sandbox which fixes the fatal.

I am having a weird issue on /admin/config/regional/content-language where I can check the checkboxes to enable the menu link translation of title and description, but the setting is not saved. Something funny about the form structure for default fields?

effulgentsia’s picture

Status: Needs work » Needs review
FileSize
441.29 KB

Just another patch snapshot for visibility + simplytest.me.

Status: Needs review » Needs work

The last submitted patch, 21: menutree-21.patch, failed testing.

effulgentsia’s picture

Status: Needs work » Needs review
FileSize
441.48 KB
1.51 KB

Trivial merge fix just to get bot to run.

Status: Needs review » Needs work

The last submitted patch, 23: menutree-23.patch, failed testing.

pwolanin’s picture

Status: Needs work » Needs review
FileSize
480.38 KB

Here's another patch to see how the tests are doing.

Status: Needs review » Needs work

The last submitted patch, 25: 2227441-25.patch, failed testing.

pwolanin’s picture

Status: Needs work » Needs review
FileSize
480.41 KB

oops, that had setUp() 2x in one changed test.

Status: Needs review » Needs work

The last submitted patch, 27: 2227441-27.patch, failed testing.

xjm’s picture

Priority: Critical » Major
Issue tags: -beta blocker
pwolanin’s picture

Priority: Major » Critical

This is still critical. In any case, I need to update the summary. We actually far along and will just skip this step unless the committers want to add the new API pieces to core without using them in an initial patch.

pwolanin’s picture

Status: Needs work » Needs review
FileSize
493.14 KB

Another snapshot of progress to see test results.

Status: Needs review » Needs work

The last submitted patch, 31: 2227441-30.patch, failed testing.

pwolanin’s picture

Title: New plan, Phase 1: (prior Step 3): Implement menu links as actual plugins » New plan, Phase 1:Review the implementation of menu links as actual plugins
Issue summary: View changes
Status: Needs work » Needs review
FileSize
149.37 KB

Here's what actually needs review. This is a patch limited to the added classes for the menu link plugin, manager, and storage. Of particular note is the clean separation of concerns between the plugin manager and the tree storage. The latter hides all the details of the hierarchy implementation such as the pN columns or the fact that it's using SQL at all.

The test coverage in the patch is limited to 2 new test since it omits all the exiting tests that are in the process of being converted for phase 2.

Such patch *could* be committed after review, since it doesn't actually change core behavior, however it would as well make sense to commit with all of phase 2.

Here's the diff stat of the patch:

 core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php                     |  171 +++++++++++++
 core/lib/Drupal/Core/Menu/Form/MenuLinkFormInterface.php                   |   71 ++++++
 core/lib/Drupal/Core/Menu/MenuLinkBase.php                                 |  195 +++++++++++++++
 core/lib/Drupal/Core/Menu/MenuLinkDefault.php                              |   91 +++++++
 core/lib/Drupal/Core/Menu/MenuLinkInterface.php                            |  177 ++++++++++++++
 core/lib/Drupal/Core/Menu/MenuLinkTree.php                                 |  992 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php                        |  330 +++++++++++++++++++++++++
 core/lib/Drupal/Core/Menu/MenuLinkTreeStorage.php                          | 1177 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 core/lib/Drupal/Core/Menu/MenuLinkTreeStorageInterface.php                 |  199 ++++++++++++++++
 core/lib/Drupal/Core/Menu/StaticMenuLinkOverrides.php                      |  117 +++++++++
 core/lib/Drupal/Core/Menu/StaticMenuLinkOverridesInterface.php             |   78 ++++++
 core/lib/Drupal/Core/ParamConverter/MenuLinkPluginConverter.php            |   58 +++++
 core/modules/system/config/install/menu_link.static.overrides.yml          |    1 +
 core/modules/system/config/schema/system.schema.yml                        |   27 +++
 core/modules/system/lib/Drupal/system/Tests/Menu/MenuTreeStorageTest.php   |  298 +++++++++++++++++++++++
 core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsMenuLink.php    |   51 ++++
 core/modules/views/lib/Drupal/views/Plugin/Menu/Form/ViewsMenuLinkForm.php |   34 +++
 core/modules/views/lib/Drupal/views/Plugin/Menu/ViewsMenuLink.php          |  156 ++++++++++++
 core/tests/Drupal/Tests/Core/Menu/MenuLinkTreeTest.php                     |  272 +++++++++++++++++++++
 19 files changed, 4495 insertions(+)
jibran’s picture

Status: Needs review » Needs work

Here is little help to move it fwd.

  1. +++ b/core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php
    @@ -0,0 +1,171 @@
    +   * @var \Drupal\Core\Menu\MenuLinkInterface
    

    desc line missing.

  2. +++ b/core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php
    @@ -0,0 +1,171 @@
    +    $this->menuTree = $menu_tree;
    

    $this->menuTree is not defined in this class.

  3. +++ b/core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php
    @@ -0,0 +1,171 @@
    +      $this->menuLink->build(),
    

    Are we missing something here?

  4. +++ b/core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php
    @@ -0,0 +1,171 @@
    +      '#title' => $this->t('Enable menu link'),
    

    Isn't it "Enable" currently in core instead of "Enable menu link"?

  5. +++ b/core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php
    @@ -0,0 +1,171 @@
    +//      '#title_display' => 'invisible',
    

    not needed

  6. +++ b/core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php
    @@ -0,0 +1,171 @@
    +       '#description' => $this->t('If selected and this menu link has children, the menu will always appear expanded.'),
    

    wrong indentation

  7. +++ b/core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php
    @@ -0,0 +1,171 @@
    +    // @TODO Should we expose expanded?
    

    is it still pending?

  8. +++ b/core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php
    @@ -0,0 +1,171 @@
    +      $this->translationManager = \Drupal::translation();
    

    Why are we not using injection here?

  9. +++ b/core/lib/Drupal/Core/Menu/MenuLinkDefault.php
    @@ -0,0 +1,91 @@
    +   * @var array
    ...
    +   * @var \Drupal\Core\Menu\StaticMenuLinkOverridesInterface
    

    desc line missing.

  10. +++ b/core/lib/Drupal/Core/Menu/MenuLinkInterface.php
    @@ -0,0 +1,177 @@
    +   * Update and save values for a menu link.
    

    Updates and saves

  11. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTree.php
    @@ -0,0 +1,992 @@
    +use Drupal\Component\Utility\String;
    +use Drupal\Component\Utility\Unicode;
    +use Drupal\Component\Utility\UrlHelper;
    +use Drupal\Core\Cache\Cache;
    +use Drupal\Component\Utility\NestedArray;
    +use Drupal\Core\Entity\EntityManagerInterface;
    +use Symfony\Cmf\Component\Routing\RouteObjectInterface;
    +use Drupal\Component\Plugin\Exception\PluginException;
    +use Drupal\Core\Access\AccessManager;
    +use Drupal\Core\Cache\CacheBackendInterface;
    +use Drupal\Core\Extension\ModuleHandlerInterface;
    +use Drupal\Core\Language\LanguageManagerInterface;
    +use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
    +use Drupal\Core\Plugin\Discovery\YamlDiscovery;
    +use Drupal\Core\Plugin\Factory\ContainerFactory;
    +use Drupal\Core\Routing\RouteProviderInterface;
    +use Drupal\Core\Session\AccountInterface;
    +use Symfony\Component\HttpFoundation\RequestStack;
    

    Can we just reorder them? vendor last and etc.

  12. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTree.php
    @@ -0,0 +1,992 @@
    +class MenuLinkTree implements MenuLinkTreeInterface {
    

    There are still some missing public functions in this class not defined in interface.

  13. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTree.php
    @@ -0,0 +1,992 @@
    +  protected $factory;
    

    Can we use some better name here?

  14. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTree.php
    @@ -0,0 +1,992 @@
    +  protected $preferredLinks = array();
    +  protected $buildAllDataParameters = array();
    

    doc block missing.

  15. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTree.php
    @@ -0,0 +1,992 @@
    +    $this->factory = new ContainerFactory($this);
    

    Can we move it to the end or start of the function? and Maybe add a comment.

  16. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTree.php
    @@ -0,0 +1,992 @@
    +  public function getInstance(array $options) {
    

    I can't find it on interface.

  17. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTree.php
    @@ -0,0 +1,992 @@
    +    $cid = 'links:' . $menu_name . ':page:' . $system_path . ':' . $language_interface->id . ':' . $page_is_403 . ':' . (int) $max_depth;
    ...
    +      $cid .= ':trail';
    

    Can we make it array and then implode?

  18. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTree.php
    @@ -0,0 +1,992 @@
    +   * @TODO should this accept a menu link instance or just the ID?
    

    @todo.

  19. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTree.php
    @@ -0,0 +1,992 @@
    +  public function getParentDepthLimit($id) {
    ...
    +  protected function parentSelectOptionsTreeWalk(array $tree, $menu_name, $indent, &$options, $exclude, $depth_limit) {
    

    doc blocks missing.

  20. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php
    @@ -0,0 +1,330 @@
    +   * This should only be called when the link data has already been removed from
    +   * any external storage.  This method will not attempt to persist the deletion
    

    more then 80 chars.

  21. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php
    @@ -0,0 +1,330 @@
    +   * @param string $route_name
    +   * @param array $route_parameters
    +   * @param bool $include_hidden
    

    desc missing.

  22. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php
    @@ -0,0 +1,330 @@
    +   *     condition key/value pairs; see _menu_build_tree() for the actual query.
    

    more then 80 chars.

  23. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php
    @@ -0,0 +1,330 @@
    +   * Fetches a menu link which matches the route name, parameters and menu name.
    +   * @param string $route_name
    

    Space missing.

  24. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php
    @@ -0,0 +1,330 @@
    +   * @param array $route_parameters
    +   * @param null $selected_menu
    +   * @return mixed
    

    desc missing and space missing.

  25. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php
    @@ -0,0 +1,330 @@
    +   * @return \Drupal\Core\Menu\MenuLinkInterface
    

    I think it also returns noting if id in not there.

  26. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php
    @@ -0,0 +1,330 @@
    +   * Get a form instance for editing a menu link plugin.
    ...
    +   * Get the options for a select element to choose and menu and parent.
    ...
    +   * Get a list of menu names for use as options.
    

    Gets

  27. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php
    @@ -0,0 +1,330 @@
    +  public function menuNameExists($menu_name);
    ...
    +  public function getParentDepthLimit($id);
    

    doc block missing.

  28. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorage.php
    @@ -0,0 +1,1177 @@
    +class MenuLinkTreeStorage implements MenuLinkTreeStorageInterface {
    

    class doc missing.

  29. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorage.php
    @@ -0,0 +1,1177 @@
    +   * Execute a select query while making sure the database table exists.
    

    Executes

  30. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorage.php
    @@ -0,0 +1,1177 @@
    +   * Using the link definition, but up all the fields needed for database save.
    

    @param block missing.

  31. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorage.php
    @@ -0,0 +1,1177 @@
    +    // If no corresponding parent link was found, move the link to the top-level.
    

    more then 80 chars.

  32. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorage.php
    @@ -0,0 +1,1177 @@
    +      //  will be 0 even if there are children if those are hidden. has_children
    

    more then 80 chars.

  33. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorage.php
    @@ -0,0 +1,1177 @@
    +   * Find the relative depth of this link's deepest child.
    ...
    +   * Set the materialized path field values based on the parent.
    ...
    +   * Using the query field values and original values, move the link's children.
    

    @param block missing.

  34. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorage.php
    @@ -0,0 +1,1177 @@
    +   * Load the parent definition if it exists.
    

    Loads and @param block missing.

  35. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorage.php
    @@ -0,0 +1,1177 @@
    +   * Set the has_children flag for the link's parent if it has visible children.
    

    Sets

  36. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorage.php
    @@ -0,0 +1,1177 @@
    +    // @todo - does this make more sense than using the system path?
    

    have we finalized yet?

  37. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorage.php
    @@ -0,0 +1,1177 @@
    +   * Load all table fields, not just those that are in the plugin definition.
    

    loads and @param block missing.

  38. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorage.php
    @@ -0,0 +1,1177 @@
    +  protected  function loadFullMultiple(array $ids) {
    

    doc block missing.

  39. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorage.php
    @@ -0,0 +1,1177 @@
    +   * Prepares the data for calling $this->treeDataRecursive().
    

    @param block missing.

  40. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorage.php
    @@ -0,0 +1,1177 @@
    +   *   An array of the menu link ID values that are in the path from the current
    ...
    +      // We need to determine if we're on the path to root so we can later build
    

    more then 80 chars.

  41. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorage.php
    @@ -0,0 +1,1177 @@
    +   * Check if the tree table exists and create it if not.
    

    Checks

  42. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorageInterface.php
    @@ -0,0 +1,199 @@
    +  public function rebuild(array $definitions);
    

    desc line missing.

  43. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorageInterface.php
    @@ -0,0 +1,199 @@
    +   * Load a plugin definition from the storage.
    ...
    +   * Load multiple plugin definitions from the storage.
    ...
    +   * Load multiple plugin definitions from the storage based on properties.
    ...
    +   * Load multiple plugin definitions from the storage based on route.
    

    Loads

  44. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorageInterface.php
    @@ -0,0 +1,199 @@
    +   *   Array of menu Link definitions
    ...
    +   *   Array of menu link definitions
    ...
    +   *  Array of menu link definitions keyed by ID.
    

    An array.

  45. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorageInterface.php
    @@ -0,0 +1,199 @@
    +   * @param string $route_name
    +   * @param array $route_parameters
    +   * @param bool $include_hidden
    

    desc missing.

  46. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorageInterface.php
    @@ -0,0 +1,199 @@
    +   * Save a plugin definition to the storage.
    

    saves

  47. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorageInterface.php
    @@ -0,0 +1,199 @@
    +   * Load all the visible links that are below the given ID.
    ...
    +   * Load all the IDs for links that are below the given ID.
    ...
    +   * Load a subtree rooted by the given ID.
    

    Loads

  48. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorageInterface.php
    @@ -0,0 +1,199 @@
    +   * @param $id
    ...
    +   * @param $id
    ...
    +   * @param string $menu_name
    +   * @param array $parents
    ...
    +  public function getExpanded($menu_name, array $parents);
    

    desc line missing.

  49. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeStorageInterface.php
    @@ -0,0 +1,199 @@
    +  public function findChildrenRelativeDepth($id);
    ...
    +  public function menuNameExists($menu_name);
    ...
    +  public function getMenuNames();
    

    doc block missing.

  50. +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuTreeStorageTest.php
    @@ -0,0 +1,298 @@
    +  public function testBasicMethods() {
    ...
    +  public function doTestEmptyStorage() {
    ...
    +  public function doTestTable() {
    ...
    +  public function testSimpleHierarchy() {
    ...
    +  public function testMenuLinkMoving() {
    ...
    +  public function testMenuHiddenChildLinks() {
    

    doc block missing.

  51. +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuTreeStorageTest.php
    @@ -0,0 +1,298 @@
    +   * Add a link with the given ID and supply defaults.
    ...
    +   * Move the link with the given ID so it's under a new parent.
    ...
    +   * Test that a link's stored representation matches the expected values.
    

    Adds, moves, tests

  52. +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuTreeStorageTest.php
    @@ -0,0 +1,298 @@
    +   * @param $id
    

    typehint missng.

  53. +++ b/core/modules/views/lib/Drupal/views/Plugin/Menu/ViewsMenuLink.php
    @@ -0,0 +1,156 @@
    +   * @var array
    ...
    +   * @var \Drupal\Core\Entity\EntityManagerInterface
    ...
    +   * @var \Drupal\views\ViewExecutableFactory
    

    desc line missing.

  54. +++ b/core/modules/views/lib/Drupal/views/Plugin/Menu/ViewsMenuLink.php
    @@ -0,0 +1,156 @@
    +  protected $view;
    

    doc block missing.

pwolanin’s picture

Thanks for the review - I know there is still doc cleanup needed and appreciate having a punch list.

re: making $cid and array and doing implode - this $cid as direct sting concatenation seems to be the norm in core, e.g. in ContentEntityStorageBase

$cid = "field:{$this->entityTypeId}:$id";

Plus that $cid is basically carried forward from existing HEAD, so I think it's less error-prone to leave it as-is.

victor-shelepen’s picture

FileSize
3.77 KB

Take my patch to add a link "translate" on the page - admin/structure/menu/manage/main
Sceenshot

victor-shelepen’s picture

pwolanin’s picture

@likin - thanks, looks good on a quick review. I guess you talked to dawehner about this, since he an I discussed this approached yesterday?

Adding that patch to the sandbox too.

dawehner’s picture

Can we use some better name here?

Nope, this is the standard for plugin managers.

I can't find it on interface.

PluginManagerInterface => MapperInterface

Can we make it array and then implode?

We just do it like HEAD does it.

more then 80 chars.

Hope, exactly 80

I think it also returns noting if id in not there.

Sorry but the review does not offer enough context. Sadly I can't find which one you need.

Pushed a non-small doc cleanup.

jibran’s picture

Thank you very much for the fixes @dawehner.

Sorry but the review does not offer enough context. Sadly I can't find which one you need.

I blame dreditor for this. :)

pwolanin’s picture

Status: Needs work » Needs review
FileSize
1.23 KB

Here's another patch of just the APi additions for review

pwolanin’s picture

FileSize
541.77 KB

Here's a snapshot patch of all the work in the branch - let's see how the tests are doing.

Status: Needs review » Needs work

The last submitted patch, 42: 2227441-snapshot-42.patch, failed testing.

pwolanin’s picture

Status: Needs work » Needs review
FileSize
541.84 KB

oops - a mismatched type hint added during the last bit of cleanup.

Status: Needs review » Needs work

The last submitted patch, 44: 2227441-snapshot-44.patch, failed testing.

pwolanin’s picture

note, an earlier version of this fix is included in the patch: #2267753: ContentEntityDatabaseStorage::mapToStorageRecord hard-codes $entity->$name->value

One of several other bugs we've turned up and worked on and fixed. So at least that bit will come out as soon as that patch is committed.

pwolanin’s picture

Status: Needs work » Needs review
FileSize
543.45 KB

And after some cleanup for recent minor refactorings that broke a couple tests.

Status: Needs review » Needs work

The last submitted patch, 47: 2227441-snapshot-47.patch, failed testing.

victor-shelepen’s picture

It should fix some tests:

Drupal\shortcut\Tests\ShortcutLinksTest
Drupal\simpletest\Tests\DrupalUnitTestBaseTest
Drupal\toolbar\Tests\ToolbarAdminMenuTest

victor-shelepen’s picture

Status: Needs work » Needs review

The last submitted patch, 36: 2227441-api-review-36.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 49: 2227441-sandbox-49.patch, failed testing.

The last submitted patch, 49: 2227441-snapshot-49.patch, failed testing.

pwolanin’s picture

@likin - I don't think the changes to MenuLinkDefault are correct, but the rest looks good and I'll add to the branch.

victor-shelepen’s picture

@pwolanin The main idea MenuLinkDefaut is that we need notify anothers when link is updated. At this case we need notify the module toolbar to pass the test AdminToolbarMenuTest. It needs invokeAll. I tried To implement it according to DIC. So it looks weird.

dawehner’s picture

Status: Needs work » Needs review
FileSize
550.07 KB

@jibran
Fixed some of yours

@likin
I think we should do that in a generic place.
Also merged in some of your fixes.

Status: Needs review » Needs work

The last submitted patch, 56: menu-2227441-56.patch, failed testing.

victor-shelepen’s picture

@dawehner Could you give a git access for the repository?

dawehner’s picture

@likin
Granted ...

pwolanin’s picture

Status: Needs work » Needs review
FileSize
554.94 KB

Another snapshot for tests

Renamed MenuLinkTreeStorage to MenuTreeStorage since it's not specific to links (we could possibly re-factor book manager to use it and kill a bunch of duplicate code)

Still need to add back the language selector to the Menu entity form and figure out why that was causing an error when content_translation was enabled.

In discussion with Wim, it seems we have no test coverage of the $only_active_trail parameter for buildPagedata, so it would be good to add at least a basic check for that.

The get/set active menu names functionality should be moved into the MenuLinkTree class since right now it's calling out to a function in menu.inc.

Status: Needs review » Needs work

The last submitted patch, 60: 2227441-snapshot-60.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
558.02 KB

This time it should be green.

Status: Needs review » Needs work

The last submitted patch, 62: 2227441.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
558.82 KB

So.

Status: Needs review » Needs work

The last submitted patch, 64: 2227441.patch, failed testing.

pwolanin’s picture

Status: Needs work » Needs review
FileSize
559.66 KB

ok, clean-up for the work-around for Shortcut while we wait for this to be committed: #2267753: ContentEntityDatabaseStorage::mapToStorageRecord hard-codes $entity->$name->value

catch’s picture

That's in now.

pwolanin’s picture

Actually no - rolled back due conflict with #2190313: Add $EntityType::load() and loadMultiple() to simplify loading entities, but I'm hoping catch will committ again and we'll have to account for that change too

pwolanin’s picture

FileSize
555.15 KB

Ok, rebased against the fixes and changes.

dawehner’s picture

Okay, we need to come up with 40 kb more documentation in order to get a sane size.

@everyone_else
Talk to us so we can help you to help us to review the patch.

victor-shelepen’s picture

@dawehner Describe what help is realy needed.

pwolanin’s picture

@likin - we ned to go through the new interfaces and classes and make sure all the doxyen is complete and correct, and check e.g. system.api.php to see if there are outdated examples that need to be changed or removed.

pwolanin’s picture

FileSize
558.19 KB

Here's another snapshot after accounting for changes to Discoveryinterface and ConfirmFormBase I ran some tests, but let's see if I got them all.

bforchhammer’s picture

I have started looking at the patch. Below are a few things I noticed, and questions of someone who hasn't spent too much time with D8 core yet.

Note, that I have only managed to get through the first 30-40% of the patch; I haven't looked at stuff related to the menu content entity nor any of the tests.

  1. +++ b/core/lib/Drupal/Core/Menu/MenuLinkInterface.php
    @@ -0,0 +1,205 @@
    +/**
    + * Default object used for LocalTaskPlugins.
    + */
    +interface MenuLinkInterface extends PluginInspectionInterface, DerivativeInspectionInterface {
    

    docblock description doesn't seem to match the Interface?

  2. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTree.php
    @@ -0,0 +1,1182 @@
    +  function getChildIds($id) {
    

    Missing scope ("public").

  3. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php
    @@ -0,0 +1,386 @@
    +/**
    + * Provides an object which returns the available menu links.
    + */
    +interface MenuLinkTreeInterface extends PluginManagerInterface {
    

    This interface/service seems to cover quite a few different functionalities:

    • discovery of menu link plugins
    • create/update/delete/reset operations on plugins
    • reading/listing plugins
    • retrieving link trees (i.e. reading multiple plugins?), as well as retrieving subtrees, parents, children, ...
    • other utility functions for accessing tree storage features (e.g. maxdepth)
    • functionality related to rendering a menu tree
    • functionality related to determining active menus and the active trail
    • functionality related to forms for menu items (?)

    I'm wondering if some of these could be separated into more interfaces/classes, to make it easier to understand. Not sure about multiple services, but maybe at least separate, single-purpose interfaces (+use of inheritance)?

  4. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php
    @@ -0,0 +1,386 @@
    +  /**
    +   * Trigger discover save, and cleanup of static links
    +   *
    +   * @todo find a better name?
    +   */
    +  public function rebuild();
    

    Seems incomplete. Also missing a dot at the end.

    Maybe "rediscover" instead of "rebuild"? In my opinion renaming is not necessary, as long as the docblock is descriptive enough. :)

  5. +++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php
    @@ -0,0 +1,386 @@
    +  public function menuNameExists($menu_name);
    

    Missing docblock.

  6. +++ b/core/lib/Drupal/Core/Menu/MenuTreeStorage.php
    @@ -0,0 +1,1233 @@
    +  /**
    +   * Defines the schema for the tree table.
    +   */
    +  protected static function schemaDefinition() {
    +    $schema = array(
    +      'description' => 'Contains the menu tree hierarchy.',
    +      'fields' => array(
    

    From what I understand, the purpose of the "MenuTreeStorage" is currently two-fold: a) to store information on discovered menu link plugins, and some of their parameters/configuration etc.; and b), to store the hierarchical organization of menu items and to make it easy to quickly query information from that hierarchy.

    Reading the class name "MenuTreeStorage", I primarily think of the second part, i.e. the storage of hierarchical info.

    I understand that these two "tasks" are currently implemented with one database table for performance reasons, but I think that DX could profit from splitting the interface (and class) into two parts for a) CRUD on "plugin definitions", and b) managing hierarchical information (getParentPath, getParent, getChildren, etc.).

    Maybe the CRUD part could be moved to an (abstract?) base class MenuLinkPluginStorage, which is extended by MenuTreeStorage (adding hierarchical things). This would make it easier to replace the materialized path implementation with something else.

  7. +++ b/core/lib/Drupal/Core/Menu/MenuTreeStorage.php
    @@ -0,0 +1,1233 @@
    +  /**
    +   * Loads the parent definition if it exists.
    +   *
    +   * @param $link
    +   * @param $original
    +   * @internal param $
    +   *
    +   * @return array
    +   */
    +  protected function findParent($link, $original) {
    

    Incomplete docblock.

  8. +++ b/core/lib/Drupal/Core/Menu/MenuTreeStorageInterface.php
    @@ -0,0 +1,243 @@
    +  /**
    +   * Loads multiple plugin definitions from the storage based on properties.
    +   *
    +   * @param array $properties
    +   *   The properties to filter by.
    +   *
    +   * @return array
    +   *   An array of menu link definitions.
    +   */
    +  public function loadByProperties(array $properties);
    

    I think this method could make things difficult for alternative tree storage implementations because it seems fairly generic; it's not clear which properties are valid and commonly used for querying via this function (to me anyway), so optimising an implementation will likely also be difficult. From what I saw in the patch, this method is only called from the outside with the 'menu_name' property, so maybe it can be removed (or made protected) with the addition of a new "loadByMenuName" method?

  9. +++ b/core/lib/Drupal/Core/Menu/MenuTreeStorageInterface.php
    @@ -0,0 +1,243 @@
    +   * This function may be used build the data for a menu tree only, for example
    +   * to further massage the data manually before further processing happens.
    +   * MenuLinkTree::checkAccess() needs to be invoked afterwards.
    

    There's a "to" missing in the first sentence.

  10. +++ b/core/lib/Drupal/Core/Menu/MenuTreeStorageInterface.php
    @@ -0,0 +1,243 @@
    +  /**
    +   * Returns all the internal materialized IDs.
    +   *
    +   * @param string $id
    +   *   The parent menu link ID.
    +   *
    +   * @return array
    +   *   An array of plugin IDs that represents the path from this plugin ID
    +   *   to the root of the tree.
    +   */
    +  public function getMaterializedPathIds($id);
    

    The keyword "internal" somewhat contradicts the fact that it's exposed on a public interface. ;-)

    Also, as far as I know the "materialized path" describes a very specific way of storing hierarchical information; so in order to decouple the interface from a specific storage implementation, I think this could be renamed to something like getParentPath(), which is more neutral imo.

  11. +++ b/core/lib/Drupal/Core/Menu/MenuTreeStorageInterface.php
    @@ -0,0 +1,243 @@
    +  /**
    +   * @param string $menu_name
    +   *   The menu name.
    +   * @param array $parents
    +   *
    +   * @return array
    +   *   The menu link ID that are flagged as expanded in this menu.
    +   */
    +  public function getExpanded($menu_name, array $parents);
    

    Incomplete docblock.

  12. +++ b/core/lib/Drupal/Core/Menu/MenuTreeStorageInterface.php
    @@ -0,0 +1,243 @@
    +  /**
    +   * Finds the relative depth of this link's deepest child.
    +   *
    +   * @param string $id
    +   *   The parent menu link ID.
    +   *
    +   * @return int
    +   *   Returns the relative depth.
    +   */
    +  public function findChildrenRelativeDepth($id);
    

    According to Wikipedia: The depth of a node is the length of the path to its root (i.e., its root path).

    Therefore, I think this could just be named getDepth($id).

    Using get* instead of find* might also be more consistent with other method names; on the other hand, there seems to be quite a few different patterns (load*, get*, find*, count*, and others), so not sure.

  13. +++ b/core/modules/menu_link_content/lib/Drupal/menu_link_content/MenuLinkContentAccessController.php
    @@ -0,0 +1,58 @@
    \ No newline at end of file
    

    newline.

pwolanin’s picture

@bforchhammer - thanks for the constructive feedback, especially in terms of architecture. There are almost certainly a pile of doxygen to be fixed I know.

So, yes, I'd agree that MenuLinkTreeInterface covers a lot, though splitting the storage out the tree storage actually a good step forward compared to past versions.

One argument for keeping the MenuLinkTree monolithic is that it allows the tree storage to be private to it. If we split it up the tree storage is positioned as more of a public service, which isn't really my intent. Also, much more importantly, there is a subtle optimization that makes splitting it up problematic. In function checkAccess the code does:

      $this->definitions[$definition['id']] = $definition;

That allows us to use each definition loaded with the tree when we later call

$this->createInstance($definition['id']);

The way the plugin factory works, we'd otherwise be making another database query to re-load the individual plugin definition to instantiate it. I suppose we could have this in-memory cache in the tree storage instead, though I was happy to be able to write is as a totally stateless service and leave all the caching to the manager


For the storage I think of it as "tree storage", not just "storage" so I expect it to store data in a way that can be retrieved as a tree. I can't really see the value in in a service that just stories the data and another that stores the tree. Yes, that might be one way to implement this internally, but why embed that in the interfaces? The separation between that and the other code in the manger that manipulates the tree is (I think) already a big step forward in that it's behind an interface, and tries not to leak the details of the tree storage algorithm by only taking in or returns the plugins definition.

Good point about loadByProperties() - at the very least it should be clearly documented to only accept properties that are part of the plugin definition (and enforce that internally). Maybe having specific methods would be better and make it easier to alter the tree storage implementation.

The materialized path value that's return is not implementation specific except, perhaps, in its naming. Happy for better naming suggestions. It's just the list of ancestor IDs leading to the root of the tree. What do you generically call the leaf to root path?

findChildrenRelativeDepth() Is not the actual depth, it's the difference in depth between the link of the given ID and its deepest child. Happy for naming suggestions to clarify that too.

pwolanin’s picture

Quick 1st pass at using xhprof for this patch as compared to HEAD (df710a893bef9b60c600b68925edcc49470635b8) both a clean install with drush 7.

Loading /admin/config with a warm cache

HEAD:

Overall Summary
Total Incl. Wall Time (microsec): 726,987 microsecs
Total Incl. CPU (microsecs): 661,783 microsecs
Total Incl. MemUse (bytes): 10,217,828 bytes
Total Incl. PeakMemUse (bytes): 10,284,236 bytes
Number of Function Calls: 62,333

patch:

Overall Summary
Total Incl. Wall Time (microsec): 661,677 microsecs
Total Incl. CPU (microsecs): 615,320 microsecs
Total Incl. MemUse (bytes): 10,126,568 bytes
Total Incl. PeakMemUse (bytes): 10,193,088 bytes
Number of Function Calls: 56,436

Is it useful to post the actual xhprof files?

bforchhammer’s picture

I definitely agree that the separation of storage and "other things" is already a big step forward! :)

One argument for keeping the MenuLinkTree monolithic is that it allows the tree storage to be private to it. If we split it up the tree storage is positioned as more of a public service, which isn't really my intent. Also, much more importantly, there is a subtle optimization that makes splitting it up problematic. In function checkAccess the code does: [...]

Can you elaborate on why it should not be made public? What's the benefit of keeping it private?

(Personally, I wish we had a more generic concept for storing hierarchical content; I do understand that that's out of scope for this issue, but wouldn't making the tree storage public help us go more into that direction?)

Also, I have to admit I don't completely understand how that checkAccess optimization works at the moment; I do understand that it helps prevent database queries, but could that not be formalized somehow, i.e. extracted into a separate method? it does not seem to have much to do with "checking access", nor is it mentioned as a side-effect in the docblock at the moment.

The materialized path value that's return is not implementation specific except, perhaps, in its naming. Happy for better naming suggestions. It's just the list of ancestor IDs leading to the root of the tree. What do you generically call the leaf to root path?

I think that would be called the "root path" of a given node/leaf.

findChildrenRelativeDepth() Is not the actual depth, it's the difference in depth between the link of the given ID and its deepest child. Happy for naming suggestions to clarify that too.

Sorry, I must have misread the description. I think what you're describing is actually called the "height" of a (sub-)tree.
Wikipedia: "The height of a node is the length of the longest downward path to a leaf from that node. The height of the root is the height of the tree."

---------------

I have read the patch partially from the perspective of a contrib developer, i.e. trying to figure out how to override certain functionality or pieces of code with custom versions. One example of something which contrib developers have wanted to adjust in D7, is the code surrounding the active trail/preferred menu item; I'm wondering how I would override the logic of getActiveTrailIds() and menuLinkGetPreferred() with the patch in D8?

pwolanin’s picture

So, since getActiveTrailIds() is just the path to root of the preferred link, there is nothing to override there. Rather, menuLinkGetPreferred() is a possible place to override.

Note, however, that compared to D7, none of this affects the breadcrumb, which was a major motivation before for overriding the active trail.

Bojhan’s picture

This sounds like a good idea, we already have similar concepts in core - and this kind of stuff creates a lot of WTF's when you are translating. As long as its easy to overwrite in the code, when you do want to change it - it should all be fine.

Let me know when there is a UI issue, I'd like to make sure we provide a very understandable help text.

pwolanin’s picture

I was just discussing with Bojhan in IRC. For for context for others: In terms of UX, we want to preserve the ability to localize module-provided links

There can be many variants of link plugin present in the same menu: http://screencast.com/t/tBggPZkoe

In this patch, we have a mix of content and static-module provided ones (and ones provided by views, but those are rare)

for some links you can edit anything since they are content: http://screencast.com/t/Uxaj7il3o2

others are partially locked since they are statically defined with localized strings: http://screencast.com/t/AYftCPIy

Discussing with actual site-builders and others, I think editing the module-provided admin links is a rare use case. You can replace it with a content link if needed.

Also, site builders are used to different edit forms having different fields (e.g. 2 different node types), so this is unsurprising.

Gábor Hojtsy’s picture

Yeah there are 3 ways to translate those menu items and in fact 3 UIs. I don't think that is a show-stopper, we also advocated introducing the basic functionality at least for translation and then contrib can ease the flow. Eg. blocks have the same problem, module provided blocks are translated as UI strings (eg. who is online block) while content blocks are translated as content entities BUT both of their titles are on the config entity and translated as configuration. So that is also a 3-way translation intersection for blocks. This is not really different for menus. That is where contribs like TMGMT will shine. If we'd consider this a showstopper, then even low level translation support would not be there which affects contribs very negatively because they will not assume translatable things and not interoperate with them.

I think we should probably a look a bit at how views provided items will be translated. I should in fact try that. :) Because you can translate the menu title as config translation on the view but does that end up in the menu display?

Gábor Hojtsy’s picture

This translation test turned out to be a bit different in result than what I was expecting.

1. So the default main nav menu has a module provided Home link that is not editable. That is translatable with the locale UI. That works.
2. I created a custom menu item (content entity). Configured it to be translatable and translated the title. It shows up translated in the admin summary translation table of the entity. NOT in the menu though.
3. I created a menu item via the views UI with a page display. Then enabled config translation and translated the menu item there. This worked flawlessly showing the translated menu item as I expected.

When I switched back to my original English, it worked fine also. So the content translation translated menu item does not end up in the rendered menu:

It is of course confusing to translate these, only the content one has a direct translate tab in the menu UI. I don't think this is anywhere more complex than blocks which even combine different methods of translation in one object's different properties. Here it is "just" different object per object. So I don't think this is a desired UI overall but it is how the technology works and we accepted similar confusion for blocks and pushed that to contrib to improve the UI. Would be similar here. Solving the translation itself in core is crucial for contrib collaboration though.

pwolanin’s picture

@Gabor. Thanks for the review.

I'm pretty sure the same code is running in the admin overview and for the link rendering in the menu. We are asking the loaded entity for the label. Any idea where this could be going wrong?

@bforchammer- when I say I want the tree storage to be private I only mean this instance. If another module wanted to use it, it should define a different service that stored data in a different table. Probably the default for the table same should be removed.

effulgentsia’s picture

I read (some parts less thoroughly than others) through the patch, and overall, like it a lot, and think it's a huge improvement in terms of architecture and code relative to HEAD. While per #74.3, MenuLinkTree is ridiculously monolithic, I don't think it's any more monolithic than HEAD's MenuTree and menu.inc functions that it's replacing.

My biggest concern with the patch is this:

+++ b/core/modules/menu_link_content/lib/Drupal/menu_link_content/Plugin/Menu/MenuLinkContent.php
@@ -0,0 +1,196 @@
+  public function getTitle() {
+    return $this->getEntity()->getTitle();
+  }

Not just 'title', but the plugin instance to entity proxy in general. Can we get xhprof and db query comparison of patch vs HEAD for rendering a menu with many content links?

dawehner’s picture

@effulgentsia
Previously $menu_link['title'] caused a __get call so I can't really imagine how the new code could be much slower than the old one. It is one call to this function per rendered menu link.

Gábor Hojtsy’s picture

@pwolanin: ok, well, so a fundamental difference between content entities and config entities is config entities are always loaded with overrides in the page's language and you need to work to NOT load them translated. For content entities, it is the other way around. For some reason. If you would know the language you would use getTranslation($langcode) on the entity. But since you don't know and want to use the language negotiation/fallback system, you would use the entity manager, so $this->entityManager->getTranslationFromContext($entity); (I see you already have the entity manager injected). More docs on Drupal 8 entity translation API at https://drupal.org/node/2040721

effulgentsia’s picture

Re #85: but at what point is the entity loaded? Does it benefit from a loadMultiple()?

pwolanin’s picture

It does not try to multiload now. We were more focused on the first step of maki nine none of

Hue links on admin pages use an entity.

The menu link content entity is loaded when you first need the title or something else from the actual entity.

dawehner’s picture

FileSize
1.44 KB

@effulgentsia
Really good point. Previousyl MenuTree::doBuildTree() had that multi oading. I do agree 100% that this would be a critical regression,
much like #2250315: Regression: Bring back node access optimization to menu trees is at the point, but noone reviewed it yet.

I wonder how to best provide this functionality. Technically this certainly does not belong into the storage but in the rendering kind of code. We could throw some event
so other modules can work on the tree. An alternative could be to collect the plugin IDs per plugin class and call some static method. Any oppinions?

@gabor
Pushed a new version, see the interdiff. Is it just me or should cache_context.language be added to pretty much everything? Output most always have either a t() or some entity translation involved.

Gábor Hojtsy’s picture

@dawehner: yeah in your case the menu item is possibly translated with one of these methods: (1) content entity translation for custom menu items (2) t() for static module shipped items (3) config translation for views provided items. So all items may be language dependent somehow.

dawehner’s picture

@Gábor
Well, I talk about all possible blocks. Basically all of them should have some sort of translation involved in some step, when they generate the content.

Gábor Hojtsy’s picture

@dawehner #91: yup!

pwolanin’s picture

@dawehner - since core won't be shipping any entity-backed links, I guess we could debate how critical a problem it would be for the initial patch.

In particular, it might mean re- organizing the code that does the access check, since I think we get the title there to make a new array key so the links are ordered by the localized or translated title.

I'd prefer to do that in a follow-up, but probably depends how catch feels about it.

pwolanin’s picture

Discussed multi-load of MenuLinkContent entities with catch and alexpott in IRC today. Both agreed we could do that in a follow-up since it should not involve any API changes, just internal reorganization of the code flow in the manager potentially.

quoted with permission from IRC:

[09:52am] pwolanin: catch:  at this point we are not multi-locading the MenuLinkContent entities
[09:52am] pwolanin: catch:  since they are not created by core, I'm not sure if we need to address that in the 1st patch
[09:52am] pwolanin: vs figuring it out in a follow-up
[09:53am] catch: pwolanin: you mean during rendering?
[09:53am] pwolanin: catch:  yes
...
[09:58am] catch: pwolanin: hmm, it is probably OK as a follow-up - especially if it's a performance improvement otherwise.
[09:58am] catch: pwolanin: I can see it getting pretty bad on some sites if we don't fix it though.
[09:58am] pwolanin: catch:  yes, certainly.  it needs to be done before release
[09:59am] catch: pwolanin: so critical follow-up?
[09:59am] catch: But no API change or anything.
[09:59am] pwolanin: catch:  yes
[09:59am] catch: pwolanin: yeah I'm not sure. alexpott do you have an opinion?
[10:00am] alexpott: catch, pwolanin: reading backscroll - makes sense to me - critical followup
pwolanin’s picture

Title: New plan, Phase 1:Review the implementation of menu links as actual plugins » New plan, Phase 1:Review the architecture and overall implementation proposal for menu links as plugins
Issue summary: View changes
Related issues: +#2256521: [META] New plan, Phase 2: Implement menu links as plugins, including static admin links and views, and custom links with menu_link_content entity, all managed via menu_ui module
effulgentsia’s picture

Priority: Critical » Major
Status: Needs review » Fixed

I'll be bold here and mark this issue fixed, and suggest that the next patch should be posted to #2256521: [META] New plan, Phase 2: Implement menu links as plugins, including static admin links and views, and custom links with menu_link_content entity, all managed via menu_ui module, where detailed reviews can proceed. If anyone has remaining concerns about the architecture in general, please reopen this issue so we can discuss them, but IMO, we're ready to move on to detailed reviews in that issue.

Also, resetting priority to match the parent issue.

pwolanin’s picture

FileSize
557.93 KB

There were some minor conflicts in HEAD, here's a re-rolled snapshot patch including dawehner's change to make the links properly translated.

Moving any further patches to the #2 issue

Status: Fixed » Closed (fixed)

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