5

The Drupal 8 menu system is much more logical than in previous versions, and breaking the many functions of hook_menu() into multiple YAML files makes perfect sense and simplifies the creation and management of most Drupal menu items.

However, because the YAML files are static elements, it's not clear to me how I can create a dynamic path and menu item in my module. This was fairly easy in hook_menu(), and was handled the same way as regular menu items.

In my module, I'd like to allow the admin to set the path, menu title and parent menu item for a form (the form will have several instances, each of which will have its own path and menu item, defined by the site admin).

Really appreciate if anyone can point me at an example, or give a simple run down of how to do this.

7

You can follow two example:

In Providing dynamic routes you can easy use simple code:

<?php
/**
 * @file
 * Contains \Drupal\example\Routing\ExampleRoutes.
 */

namespace Drupal\example\Routing;

use Symfony\Component\Routing\Route;

/**
 * Defines dynamic routes.
 */
class ExampleRoutes {

  /**
   * {@inheritdoc}
   */
  public function routes() {
    $routes = array();
    // Declares a single route under the name 'example.content'.
    // Returns an array of Route objects. 
    $routes['example.content'] = new Route(
      // Path to attach this route to:
      '/example',
      // Route defaults:
      array(
        '_controller' => '\Drupal\example\Controller\ExampleController::content',
        '_title' => 'Hello'
      ),
      // Route requirements:
      array(
        '_permission'  => 'access content',
      )
    );
    return $routes;
  }

}

Just need array store data and foreach in function routes. You can dynamic router.

After dynamic router you can create dynamic menu with Derivative plugin. Example class MenuLinkContentDeriver. In function getDerivativeDefinitions you need return array have data like this:

 $links = array(
      'route_name' => $router_name,//like example.content in define route
      'id' => $menu_link_id,
    );
 $links['title'] = $title;
 $links['description'] = $description;
 $links['parent'] = 'main:';
 $links['enabled'] = 1;
  • 1
    These are a great help, and looks like it gives me everything I need to solve the routing part of the problem. However, I would also like to dynamically add to a menu. I know the admin could get around this by manually creating a menu for the route, but it would be nice to have the module do it. Any ideas? – James Shields Sep 12 '16 at 15:58
  • 1
    Updated my answer. – MrD Sep 12 '16 at 16:16
  • 1
    That's very helpful thanks. In your routing example, the class doesn't need to be derived from any base class, does it? For the menu, do I create a subclass of MenuLinkContentDeriver or an instance? – James Shields Sep 12 '16 at 16:27
  • 1
    In routing _controller is class, you need define it. If routes exited just need class extends DeriverBase like MenuLinkContentDeriver example. – MrD Sep 12 '16 at 16:33
  • 1
    For the menu links, you can also just create menu link content entities as suggested by 4k4 in the other answer. That's also what the menu_ui module does for menu links created within nodes. – Berdir Sep 12 '16 at 19:08
5

To provide an alternative, you could work with a single route with an argument and just create URL Aliases for, similar to how all nodes have a system path at /node/ID but can have whatever visible path/alias that a user wants.

That's how contact forms work for example, every contact form is available ad contact/{contact_form_id}, if you don't want that you can create an alias for /foo to point to /contact/foo. Core doesn't actually provide that for contact forms in an integrated way but contact_storage adds exactly that functionality in the latest version.

The main advantage is that by having a single, static route, changing/creating/deleting a $thing doesn't require you to rebuild the router, which is a relatively slow, locking (no other requests can be served while that happens) process.

And as already mentioned in a comment, menu links could also be created as menu link content entities.

To summarize, you'd basically do what path.module and menu_ui.module do with nodes.

  • That's quite a good idea. The routes wouldn't change very often, but it's certainly a consideration. I might try implementing both approaches and see which works better. – James Shields Sep 14 '16 at 9:29
5

While D8 uses the symfony routing system and configures this with yml files and dynamic route callbacks as described by @MrD, menus are now a separate system.

The menu system offers two ways to define menu links. Static in mymodule.links.menu.yml and dynamic as content.

To do the latter add an item to a menu by creating a MenuLinkContent entity:

$item = \Drupal\menu_link_content\Entity\MenuLinkContent::create([
  'link' => ['uri' => 'internal:/<front>'],
  'title' => 'Front Page',
  'menu_name' => 'admin',
]);
$item->save();

A more complex example would be how drupal generates the menu link when it saves a node:

https://api.drupal.org/api/drupal/core!modules!menu_ui!menu_ui.module/function/_menu_ui_node_save/8.2.x

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.