Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Setting Up ACL and Your Own Admin Backend Menu Item

Setting Up ACL and Your Own Admin Backend Menu Item

~6 Min. Lesezeit Zuletzt aktualisiert am August 9, 2026

Before any controller can even be called, the admin area needs to know which user role is allowed to access it, and where in the menu the entry point lives. Both are configured declaratively - no PHP code required.

acl.xml: declaring permissions

Every admin resource (menu item, controller, form field) can be tied to an ACL resource ID. Roles under System > Permissions > User Roles are checked against exactly these IDs. For our practice module, a single node is enough for now:

app/code/Mironsoft/Announcement/etc/acl.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
    <acl>
        <resources>
            <resource id="Magento_Backend::admin">
                <resource id="Magento_Backend::content">
                    <resource id="Mironsoft_Announcement::announcement"
                              title="Announcements" sortOrder="50" />
                </resource>
            </resource>
        </resources>
    </acl>
</config>

The tree under Magento_Backend::admin is rendered one-to-one as a checkbox tree in the role editor. Where a custom node sits in that tree decides which section an admin sees it under - here deliberately under Magento_Backend::content, because chapter 25 later extends this structure for testimonials with separate permissions for grid, form, and delete.

Binding a controller to ACL

Every adminhtml controller declares, via the ADMIN_RESOURCE constant, which ACL resource ID is required for access:

class Index extends \Magento\Backend\App\Action
{
    const ADMIN_RESOURCE = 'Mironsoft_Announcement::announcement';
    // ...
}

Tipp: ACL permissions inherit downward: whoever has access to a parent node implicitly has access to all child nodes - but not the other way around. That's exactly why chapter 25 deliberately models grid, form, and delete as separate child nodes instead of one coarse node: only that way can a role get read access without delete access.

Adding your own menu item

The menu item itself comes from etc/adminhtml/menu.xml and also references the ACL resource ID - if a user isn't authorized for that resource, Magento automatically hides the menu item, no extra logic required.

app/code/Mironsoft/Announcement/etc/adminhtml/menu.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd">
    <menu>
        <add id="Mironsoft_Announcement::announcement"
             title="Announcements"
             module="Mironsoft_Announcement"
             sortOrder="50"
             parent="Magento_Backend::content"
             action="mironsoft_announcement/announcement/index"
             resource="Mironsoft_Announcement::announcement" />
    </menu>
</config>

The action value matches the index controller's URL path: frontName/controllerFolder/actionName. The front name comes from etc/adminhtml/routes.xml:

app/code/Mironsoft/Announcement/etc/adminhtml/routes.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="admin">
        <route id="mironsoft_announcement" frontName="mironsoft_announcement">
            <module name="Mironsoft_Announcement" />
        </route>
    </router>
</config>

Making changes visible

bin/magento cache:clean
bin/magento cache:flush

Achtung: ACL and menu changes land in heavily cached areas (the config and layout caches). A plain cache:clean is sometimes not enough if the new menu item simply won't show up - logging out and back into the admin often helps too, since the menu structure is partly built per session. Chapter 26 revisits this and similar cache symptoms in more detail.

That completes the skeleton: module, table, model triad, ACL, and menu item. Block 2 builds the first grid on top of it.