Admin Menu Items
July 19, 2016 | Write by opensource-services
In the first article of this series, we described how to create a simple MVC/MVVM endpoint in Magento 2. Implicit, but unstated, was that we were setting up an endpoint for Magento’s frontend cart application. While the backend admin application uses the same MVC/MVVM/View system as the frontend, there’s additional features based on backend security and user interface conventions in Magento 2.
Today we’re going to start exploring the Menu Item system. Our end goal is to add a link to Magento’s left side admin application which, as you’ll learn, is the first step towards adding a backend page to Magento 2.
Adding a Menu to the Left Side
Menu Items are the links along the left side of the Magento admin console application. For example — if you navigate to
<code> Content -> Elements -> Pages </code>
the Pages hyperlink is a Menu Item. If you right click on this hyperlink and select Copy Link Address, you’ll see something like this
<code> http://magento.example.xom/admin/cms/page/index/key/ed2ddfe814ba40acb42b6fd4e95be717d32528860c3960d5e178b50e3691e0b0/ </code>
There’s a few things we’ll want to make note of with regards to the structure of an admin URL. First, admin URLs have a four segment structure.
<code> admin/cms/page/index </code>
The first segment,
admin, is the area. All URLs in the Magento admin start with this additional URL segment. The next three segments are the URL front name, controller name, and action name.
<code> Front Name: cms Controller Name: page Action Name: index </code>
Same as a standard frontend controller, Magento 2 combines all three of these segments to create a single controller class name. Magento 2 is a “One URL, One Controller” system. If you’re not sure what that means, don’t worry, future examples below will clear that up.
Finally, all Menu Items have an additional URL parameter named
key, with a corresponding value
<code> key/ed2ddfe814ba40acb42b6fd4e95be717d32528860c3960d5e178b50e3691e0b0/ </code>
This special code is required for all URLs, and is here to help prevent cross site script attacks. If you fail to include this key with your URL, Magento will reject the request as invalid.
This URL key is why we need to create a Menu Item in the first place — without Magento generating a URL key for us, there’s no simple safe way to access a standard admin controller.
Creating a Menu Item
We’re going to dive right in and create a new Menu Item. First, we’ll need to create a Magento module to hold our Menu Item. You can create the base module files using the pestle command line framework’s
<code> $ pestle.phar generate_module Pulsestorm MenuTutorial 0.0.1 </code>
and then enable the module in Magento by running the following two commands
<code> $ php bin/magento module:enable Pulsestorm_MenuTutorial $ php bin/magento setup:upgrade </code>
If you’re interested in creating a module by hand, or curious what the above pestle command is actually doing, take a look at our Introduction to Magento 2 — No More MVC article.
Next, we’ll want to add a
menu.xml file to our module. Create the following file with the following contents.
With the above in place, clear your Magento cache and load a backend page. You should see a new, top level Menu Item at the bottom of the admin navigation
Congratulations! You’ve just created your first Menu Item.
At its simplest, a
menu.xml file is a collection of
<add/> nodes. Each of these nodes adds a Menu Item to Magento’s backend.
The id attribute defines a unique identifier for this node. By convention, (but not required) this should the the name of the module (
Pulsestorm_MenuTutorial), followed by two colons (
::), followed by lowercase text that describes what the module does (
The title attribute (
Top Level Example) controls the text an end users sees for the Menu Item.
The module attribute should match the current module — this may seem redundant with the
id attribute, but remember, it’s only convention that forces you to use the module name in a Menu Item’s id.
The sortOrder attribute controls how this Menu Item is sorted with the other Menu Items in the system. The
9999 value ensures our module shows up under all the stock Menu Items.
Finally, the resource attribute defines the ACL rule a user must have in order to access this Menu Item. Normally, you’d define your own ACL rule in the same module, and use it here. For simplicity’s sake, we’re using a predefined ACL rule (
Magento_Backend::content) from a standard Magento module. If the logged in user does not have access to the configured ACL rule, the Menu Item will not render for them.
For Magento 1 developers, its worth noting that the ACL rule requirements for a module have been somewhat simplified. All we need to do in Magento 2 is specify an id — there’s no confusing matching of ACL hierarchies with Menu Item hierarchies. The tradeoff, of course, is it’s no longer possible to infer a Menu Item’s ACL rule based on its hierarchy.
Menu Items Hyperlinks
You may have noticed our top level Menu Item doesn’t actually link to anything. While its possible for a top level Menu Item to be a hyperlink, by convention the left side navigation items are used for organization. i.e., they contain child links. Let’s try adding a new Menu Item that actually has a hyperlink. Change your
menu.xml so it matches the following
Here we’ve add a new
<add/> node to our
menu.xml file. We’ve given this new Menu Item a new, unique
id attribute and a new
title. Normally, this Menu Item would have a different
resource from its parent (giving systems owners fine grained control over which modules show up) but to keep things simple we’re using the same rule as our parent item (
The important parts are our parent and action attributes. The
parent attribute should be a Menu Item ID that already exists, and will tell Magento this new Menu Item (
Pulsestorm_MenuTutorial::second_level_example) is a child of the parent. Magento 1 developers will want to take note — these
add nodes all exist at the same XML tree level — the parent/child relationships between them are dictated by these
action attribute is the three segment,
frontName/controllerName/actionName Magento 2 URL identifier. We’ve borrowed an
action from the CMS module for this tutorial. Clear your cache, reload the page, and your Top Level Example Menu Item should now trigger a fly-out menu that contains your single Second Level Example link.
If you click on this link, your browser will being you to the specified
If you’ve used the Magento admin, you’ve probably notices most core modules have Menu Items organized under sub-menus. You can do that with your own modules! Add the following third node to your
Here we’ve created a third level node, and set its parent to our second level node. If you clear your cache and reload the page with the above menu in place, your fly-out menu should look like this
Notice that our second level Menu Item has become a hyperlink-less parent header, with the third level item as a link. Magento Menu Items are limited to three levels of hierarchy — you can’t go any deeper than this.
Choosing a Parent Menu
In the above examples, we showed you how to create your own top level Menu items. Before you do this in your own modules, its a good idea to ask yourself if your module really needs a new top level item. Using the Menu Item system, its possible to add your Menu Items to existing Magento core menu categories, and doing so avoids jamming up the admin with too many top level modules.
For example, if you change the following Menu Item
such that its parent points to a core Magento Menu Item instead
Your menu will appear under the top level
System Menu Item instead. There’s no clear guidance on where to put your module’s Menu Items — this is something every module developer will need to decide on their own.
If you need helping finding the
id values of Magento core Menu Items and are familiar with the unix command line, the following command pipeline may be of interest
<code> $ find vendor/magento/ -name menu.xml | xargs grep 'title="System"' </code>
Putting it Together with Pestle
We ran through creating a Menu Item manually as a teaching exercise. Fortunately,
pestle has a
generate_menu command that makes creating Menu Items simple. Here’s how add a new Menu Item to Magento’s own
System -> Other Settings menu with pestle.
First, lets use pestle to create a proper ACL rule for our Menu Item with the following.
<code> $ pestle.phar generate_acl Which Module? (Pulsestorm_HelloWorld)] Pulsestorm_MenuTutorial Rule IDs? (Pulsestorm_MenuTutorial::top,Pulsestorm_MenuTutorial::config,)] Pulsestorm_MenuTutorial::menu_items,Pulsestorm_MenuTutorial::example_1 Created /path/to/magento2/app/code/Pulsestorm/MenuTutorial/etc/acl.xml </code>
and then edit the generated
acl.xml to give our rules some titles.
If you’re not sure what the above does, checkout the Understanding Access Control List Rules article.
Next, we’ll use the
generate_menu command. Even if you’re familiar with pestle, you may want to read through our description of the interactive command line workflow below, as there’s a new pestle feature in play.
<code> $ pestle.phar generate_menu Module Name? (Pulsestorm_HelloGenerate)] Pulsestorm_MenuTutorial Is this a new top level menu? (Y/N) (N)] N Select Parent Menu:  System (Magento_Backend::system)  Dashboard (Magento_Backend::dashboard)  System (Magento_Backend::system)  Marketing (Magento_Backend::marketing)  Content (Magento_Backend::content)  Stores (Magento_Backend::stores)  Products (Magento_Catalog::catalog)  (Magento_Backend::system_currency)  Customers (Magento_Customer::customer)  Find Partners & Extensions (Magento_Marketplace::partners)  Reports (Magento_Reports::report)  Sales (Magento_Sales::sales)  UMC (Umc_Base::umc) ()] 1 Use [Magento_Backend::system] as parent? (Y/N) (N)] N Select Parent Menu:  Report (Magento_Backend::system_report)  Tools (Magento_Backend::system_tools)  Data Transfer (Magento_Backend::system_convert)  Other Settings (Magento_Backend::system_other_settings)  Extensions (Magento_Integration::system_extensions)  Permissions (Magento_User::system_acl) ()] 4 Menu Link ID (Pulsestorm_MenuTutorial::unique_identifier)] Pulsestorm_MenuTutorial::a_menu_item ACL Resource (Pulsestorm_MenuTutorial::a_menu_item)] Pulsestorm_MenuTutorial::example_1 Link Title (My Link Title)] Look at me, I'm a link Three Segment Action (frontname/index/index)] pulsestorm_menututorial/index/index Sort Order? (10)] 9999 Writing: /path/to/magento2/app/code/Pulsestorm/MenuTutorial/etc/adminhtml/menu.xml Done. </code>
The first question,
<code> Module Name? (Pulsestorm_HelloGenerate)] Pulsestorm_MenuTutorial </code>
determines which module pestle will create a
menu.xml file in. The next set of questions
<code> Is this a new top level menu? (Y/N) (N)] N Select Parent Menu:  System (Magento_Backend::system) //...  UMC (Umc_Base::umc) ()] 1 Use [Magento_Backend::system] as parent? (Y/N) (N)] N Select Parent Menu:  Report (Magento_Backend::system_report) //...  Permissions (Magento_User::system_acl) ()] 4 </code>
are an interactive workflow that let you create a new menu identifier, or add your menu to an existing menu. i.e. “Is this a
parentless item” and/or “what is this Menu’s parent”. Despite asking multiple questions, this is a single pestle argument (see below, and also the callback argument documentation)
The next question
<code> Menu Link ID (Pulsestorm_MenuTutorial::unique_identifier)] Pulsestorm_MenuTutorial::a_menu_item </code>
is where you enter the
id attribute to use in
menu.xml. Then there’s
<code> ACL Resource (Pulsestorm_MenuTutorial::a_menu_item)] Pulsestorm_MenuTutorial::example_1 </code>
which is where you add the value used in the
resource attribute. The last two questions
<code> Three Segment Action (frontname/index/index)] pulsestorm_menututorial/index/index Sort Order? (10)] 9999 </code>
allow you to set the
sortOrder attributes. With the above in place, reload your page, and your Menu Item can be found under
System -> Other Settings. If you’re interested in using a pestle one liner, that would be
<code> $ pestle.phar generate_menu Pulsestorm_MenuTutorial Magento_Backend::system_other_settings Pulsestorm_MenuTutorial::a_menu_item Pulsestorm_MenuTutorial::example_1 "Look at me, I'm a link" pulsestorm_menututorial/index/index 9999 </code>
Notice how the entire interactive workflow to select a Menu Item ID is represented by one argument (
That the basics of Menu Item creation in Magento 2. If you followed along with all the code examples, you may have noticed our Look at me, I’m a link Menu Item pointed to the following URL
<code> http://magento.example.com/admin/pulsestorm_menututorial/index/index/key/ffcac30d7237841abc45a159390611334bb6665ce77cdf08f241e92f90688bda/ </code>
If you click on this link, Magento will redirect you to the Dashboard page. This happens because we have no URL endpoint setup for this URL! A Menu Item only generates our link. We still need to setup a MVC/MVVM endpoint for our page, which is what we’ll be doing next time!