Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

An admin page

An admin page is built the same way as a storefront page: a route, an action, a layout, a template. There are three differences: the files live in adminhtml folders, the page is protected by a permission, and you reach it through a menu item. Let's create a "Reviews" page.

app/code/Acme/Hello/
├── Controller/
│   └── Adminhtml/
│       └── Review/
│           └── Index.php                   new file
├── etc/
│   ├── acl.xml                             new file
│   └── adminhtml/
│       ├── menu.xml                        new file
│       └── routes.xml                      new file
└── view/
    └── adminhtml/
        ├── layout/
        │   └── hello_review_index.xml      new file
        └── templates/
            └── reviews.phtml               new file

The permission

Permissions are described in the etc/acl.xml file. An administrator ticks them for staff roles under System → User Roles.

<?xml version="1.0"?>
<!-- app/code/Acme/Hello/etc/acl.xml -->
<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="Acme_Hello::reviews" title="Hello Reviews" sortOrder="100"/>
            </resource>
        </resources>
    </acl>
</config>

The file declares the Acme_Hello::reviews permission. A permission ID consists of the module name and a word after two colons.

The admin route

<?xml version="1.0"?>
<!-- app/code/Acme/Hello/etc/adminhtml/routes.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="admin">
        <route id="hello" frontName="hello">
            <module name="Acme_Hello" before="Magento_Backend"/>
        </route>
    </router>
</config>

It differs from a storefront route by the adminhtml folder and the router id="admin" value. The page URL will be /admin/hello/review/index.

The action

Admin actions live in the Controller/Adminhtml folder and extend Magento\Backend\App\Action:

// app/code/Acme/Hello/Controller/Adminhtml/Review/Index.php
namespace Acme\Hello\Controller\Adminhtml\Review;
 
use Magento\Backend\App\Action;
use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\Controller\ResultFactory;
 
class Index extends Action implements HttpGetActionInterface
{
    public const ADMIN_RESOURCE = 'Acme_Hello::reviews';
 
    public function execute()
    {
        $page = $this->resultFactory->create(ResultFactory::TYPE_PAGE);
        $page->setActiveMenu('Acme_Hello::reviews');
        $page->getConfig()->getTitle()->prepend(__('Reviews'));
 
        return $page;
    }
}

The ADMIN_RESOURCE constant closes the page to staff without the permission: the parent class performs the check. The resultFactory property comes from it too. The setActiveMenu() method highlights the menu item.

The menu item

<?xml version="1.0"?>
<!-- app/code/Acme/Hello/etc/adminhtml/menu.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd">
    <menu>
        <add id="Acme_Hello::reviews" title="Hello Reviews" module="Acme_Hello" sortOrder="50"
             parent="Magento_Catalog::catalog" action="hello/review/index" resource="Acme_Hello::reviews"/>
    </menu>
</config>

The item appears in the Catalog menu. The action attribute is the page URL without /admin, and resource is the permission without which the item is hidden.

Layout and template

The layout name is built the same way as on the storefront, but the file lives in view/adminhtml/layout:

<?xml version="1.0"?>
<!-- app/code/Acme/Hello/view/adminhtml/layout/hello_review_index.xml -->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <block name="hello.reviews" template="Acme_Hello::reviews.phtml"/>
        </referenceContainer>
    </body>
</page>
<!-- app/code/Acme/Hello/view/adminhtml/templates/reviews.phtml -->
<p>The list of reviews will be here.</p>

Checking

bin/magento cache:clean

Log in to the admin panel again and open Catalog → Hello Reviews: a page with the title "Reviews" and the text from the template appears. In the next lesson a table replaces the text.

A permission for the settings

Now the settings section from the previous lesson can be protected as well: add the Acme_Hello::config permission to acl.xml and put it into the resource tag of system.xml.

Common mistake

The page URL is typed into the browser by hand, and the result is a redirect to the admin dashboard. Admin URLs are protected by a secret key — it is appended to the URL, and a request without it is rejected. Reach your pages through the menu or build links with the getUrl() method.

Thanks for reading!

Donate to the site — we work hard for you.