Magento 2 Certified Professional Developer Guide
Section 6: Developing with Adminhtml
6.1 Describe common structure/architecture
Describe the difference between Adminhtml and frontend. What additional tools and requirements exist in the admin?
Adminhtml and frontend are areas. Frontend displays the website to a user, while adminhtml is the website admin panel, aimed at those who manage it. In adminhtml, you can create / modify / delete objects and categories, manage orders and customers, configure the website, etc.
To log in to the admin panel, you need to know its basic URL (for example, https://www.website.com/admin_1pf3534g/) and then pass authentication.
Controller actions and blocks in adminhtml are located in the Adminhtml subfolder of Controller and Block folders respectively.
Controller actions and blocks in adminhtml inherit the classes, different from those inherited by actions and blocks in frontend.
In adminhtml:
Block inherits Magento\Backend\Block\Template
Action inherits Magento\Backend\App\AbstractAction
In frontend:
Block inherits Magento\Framework\View\Element\Template
Action inherits Magento\Framework\App\Action\Action
UI Components
Adminhtml components in the module view folder can contain an additional ui_component directory. The directory contains UI Components that speed up and simplify development, as well as minimize code duplicates. UI Components are also used in the out-of-the-box frontend in Checkout, but UI Components configuration is written in layout xml files, not in ui components xml files.
ui component file name matches the ui component name (except for the .xml suffix), that can be added into layout using the following instructions:
<uiComponent name="notification_area" aclResource="Magento_AdminNotification::show_list"/>
Ui_component folder contains one or several XML declarations for grids or forms.
Standard UI components come in two types:
- Basic
- Secondary
Basic components include form elements and listings.
On the screenshot, you can see .xml file for template definition. It contains a link to the listing basic component connection, aimed at displaying grids, lists and fragments. Sorting, search and dividing into pages is added to the built-in functions, which are very helpful for new components and modules development.
Below is the list of extensions for the basic components listing:
- Filters
- Pagination
- Tabs
- Buttons and their functional load
- Tables
- Multiselectors
- Loaders
- Editors
- Headings
These are secondary components that serve as additional elements for expanding the basic components. Using them is not obligatory, but it is a good practice for a developer, for they will:
- Create a unified and clear interface
- Simplify and speed up the development process
- Create a simple and intellectually clear environment for support and expansion.
UI_Components enhances development, support and Magento interface expansion, allowing the components to work independently and with no loss to operation speed.
To connect Ui components to your module, located in the admin part of the interface, Magento uses a specialized xml file:
<module_dir>/view/adminhtml/ui_component/<ui_component_name>.xml.
Then, depending on Magento version (we use Magento 2.2 for demonstration), we set up the necessary components in xml configuration:
Configuration file has access to ListingToolbar component, introduced Magento 2.1 version. It is located in the area of Magento_Ui/grid names and is related to listing classes components. Listing Toolbar contains a number of additional components, configured directly in configuration file.
For example, Bookmark is responsible for displaying and saving the positions of all current elements, forms and buttons states in the component. It also accounts for saving and comparing information through the database, directly in ui_bookmark table.
Columns Control is responsible for displaying and hiding active list columns.
Filters component receives the information about the required secondary elements; in this case, it is an element of select form.
Using <argument name=”class”> attribute, we rewrite the standard element class into the one we need.
Using <argument name=”data”><item name =”name”> attribute, we set the available parameters at the selection list.
All components contain information about the current state and receive users’ values. For example, Columns Control receives the parameters on the minimal or maximal number of the displayed columns.
Afterward, XML configuration files are united into a single one, then into json and are passed into the client like this. Each configuration file of a single ui component, that includes the set parameters in a parent XML, overrides them as new ones.
As you can see, UI components library is the main instrument for the Mangento admin area, and a considerable part of the store’s admin panel is built with it:
Obtaining UI components skills allows to significantly speed up development and expansion of Magento store interface, as well as to unify its structure for creating a more intuitive area for further support and development.
6.2 Define form and grid widgets
Define form structure, form templates, grids, grid containers, and elements. What steps are needed to display a grid or form?
Form
Contains modified fields of a certain entity. To create a form, you need to:
- Create form xml file of ui component configuration: <module_dir>/view/<area>/ui_component/<ui_component_name>.xml
- Create DataProvider class in the module and define this class in XML configuration file of UI component.
- Add a set of fields (the Fieldset component with the component of the Field) for entity or to implement the upload of meta info in the DataProvide
- Add UI component в layout: <uiComponent name=”<ui_component_name>”/>
Grid
Contains the list of entities, filters, bookmarks, paging, columns controls. Allows to search entity, modify list sorting, hide and add columns, change their order. To create a grid, you need to:
- Create listing xml file of UI component configuration: <module_dir>/view/<area>/ui_component/<ui_component_name>.xml
- Set DataSource
- Add filter, columns, toolbar, …
- Add ui component into layout: <uiComponent name=”<ui_component_name>”/>
Describe the grid and form workflow. How is data provided to the grid or form?
At the server:
- Layout loads UI component
- XML files of UI component are searched in each enabled module (<module_dir>/view/<area>/ui_component/<ui_component_name>.xml)
- All found files of UI components are merged in a single configuration object
- Configuration and definition.xml are merged. Objects from definition.xml have lower priority than single configuration object, received in the previous step
- The received configuration is transformed into JSON and embedded into the page with Magento\Ui\TemplateEngine\Xhtml\Result class
As a result, we get the following code:
<script type="text/x-magento-init"> { "*":{ "Magento_Ui/js/core/app":{ "types":{ ... }, "components":{ ... } } } } </script>
At the client side:
- RequireJS loads Magento_Ui/js/core/app and passes configuration as a parameter
- Magento_Ui/js/core/app calls Magento_Ui/js/core/renderer/layout and passes it the configuration
- layout.js creates instances of UI components and applies the configuration to it
- HTML template rendering with knockout.js and binding of the component to the template is performed
The data for grid and form is loaded with the help of DataProvider. The difference lies in the moment of information upload.
For the form component, the data of an entity are passed inside the current page’s html code in the <script type = “text / x-magento-init”> … </script> tag.
For the grid component, the data is loaded through an additional ajax request for the controller action mui / index / render, processed by the \ Magento \ Ui \ Controller \ Adminhtml \ Index \ Render class for adminhtml area and \ Magento \ Ui \ Controller \ Index \ Render for frontend area .
How can this process be customized or extended?
You can change the form data the following ways:
- Create a custom DataProvider and specify it in xml file of UI compoment.
- Create a plugin for DataProvider::getData() method
- Create Modifier class and set it in di.xml, if this DataProvider support Modifiers (for example, like \Magento\Catalog\Ui\DataProvider\Product\Form\ProductDataProvider)
“Describe how to create a simple form and grid for a custom entity. Given a specific entity with different types of fields (text, dropdown, image, file, date, and so on) how would you create a grid and a form?”
Let us consider the examine of a form. It includes text, textarea, select, multiselect, image. The data is loaded via the Vendor\Module\Model\MyEntity\DataProvider class.
<?xml version="1.0" ?> <form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> <argument name="data" xsi:type="array"> <item name="js_config" xsi:type="array"> <item name="provider" xsi:type="string">my_form.my_entity_form_data_source</item> <item name="deps" xsi:type="string">my_form.my_entity_form_data_source</item> </item> <item name="label" translate="true" xsi:type="string">General Information</item> <item name="config" xsi:type="array"> <item name="dataScope" xsi:type="string">data</item> <item name="namespace" xsi:type="string">my_form</item> </item> <item name="template" xsi:type="string">templates/form/collapsible</item> <item name="buttons" xsi:type="array"> <item name="back" xsi:type="string">Vendor\Module\Block\Adminhtml\MyEntity\Edit\BackButton</item> <item name="delete" xsi:type="string">Vendor\Module\Block\Adminhtml\MyEntity\Edit\DeleteButton</item> <item name="save" xsi:type="string">Vendor\Module\Block\Adminhtml\MyEntity\Edit\SaveButton</item> <item name="save_and_continue" xsi:type="string">Vendor\Module\Block\Adminhtml\MyEntity\Edit\SaveAndContinueButton</item> </item> </argument> <dataSource name="my_entity_form_data_source"> <argument name="dataProvider" xsi:type="configurableObject"> <argument name="class" xsi:type="string">Vendor\Module\Model\MyEntity\DataProvider</argument> <argument name="name" xsi:type="string">my_entity_form_data_source</argument> <argument name="primaryFieldName" xsi:type="string">id</argument> <argument name="requestFieldName" xsi:type="string">id</argument> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="submit_url" path="*/*/save" xsi:type="url"/> </item> </argument> </argument> <argument name="data" xsi:type="array"> <item name="js_config" xsi:type="array"> <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item> </item> </argument> </dataSource> <fieldset name="General"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="label" xsi:type="string"/> </item> </argument> <field name="name"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="dataType" xsi:type="string">text</item> <item name="label" translate="true" xsi:type="string">Name</item> <item name="formElement" xsi:type="string">input</item> <item name="source" xsi:type="string">MyEntity</item> <item name="sortOrder" xsi:type="number">50</item> <item name="validation" xsi:type="array"> <item name="required-entry" xsi:type="boolean">true</item> </item> </item> </argument> </field> <field name="about"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="dataType" xsi:type="string">text</item> <item name="label" translate="true" xsi:type="string">About</item> <item name="formElement" xsi:type="string">textarea</item> <item name="source" xsi:type="string">MyEntity</item> <item name="sortOrder" xsi:type="number">65</item> </item> </argument> </field> <field name="country_id"> <argument name="data" xsi:type="array"> <item name="options" xsi:type="object">Magento\Directory\Model\Config\Source\Country</item> <item name="config" xsi:type="array"> <item name="dataType" xsi:type="string">text</item> <item name="label" xsi:type="string" translate="true">Country</item> <item name="formElement" xsi:type="string">select</item> <item name="source" xsi:type="string">MyEntity</item> <item name="sortOrder" xsi:type="number">70</item> <item name="component" xsi:type="string">Magento_Ui/js/form/element/country</item> <item name="validation" xsi:type="array"> <item name="required-entry" xsi:type="boolean">true</item> </item> </item> </argument> </field> <field name="region_ids"> <argument name="data" xsi:type="array"> <item name="options" xsi:type="object">Magento\Directory\Model\ResourceModel\Region\Collection</item> <item name="config" xsi:type="array"> <item name="dataType" xsi:type="string">int</item> <item name="dataScope" xsi:type="string">region_ids</item> <item name="label" xsi:type="string" translate="true">States</item> <item name="formElement" xsi:type="string">multiselect</item> <item name="source" xsi:type="string">MyEntity</item> <item name="sortOrder" xsi:type="number">80</item> <item name="validation" xsi:type="array"> <item name="required-entry" xsi:type="boolean">true</item> </item> <item name="filterBy" xsi:type="array"> <item name="target" xsi:type="string"> <![CDATA[${ $.provider }:${ $.parentScope }.country_id]]></item> <item name="field" xsi:type="string">country_id</item> </item> </item> </argument> </field> <field name="image"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="label" xsi:type="string">Image</item> <item name="visible" xsi:type="boolean">true</item> <item name="source" xsi:type="string">MyEntity</item> <item name="formElement" xsi:type="string">fileUploader</item> <item name="elementTmpl" xsi:type="string">ui/form/element/uploader/uploader</item> <item name="previewTmpl" xsi:type="string">Caskers_MyEntity/image-preview</item> <item name="sortOrder" xsi:type="number">90</item> <item name="uploaderConfig" xsi:type="array"> <item name="url" xsi:type="url" path="vendor_module/upload/image"/> </item> </item> </argument> </field> </fieldset> </form>
Result:
Let us consider the grid. As DataProvider, Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider is used. The collection for grid is set in di.xml:
<?xml version="1.0" ?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory"> <arguments> <argument name="collections" xsi:type="array"> <item name="my_listing_grid_data_source" xsi:type="string"> Vendor\Module\Model\ResourceModel\MyEntity\Grid\Collection </item> </argument> </arguments> </type> <virtualType name="Vendor\Module\Model\ResourceModel\MyEntity\Grid\Collection" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult"> <arguments> <argument name="mainTable" xsi:type="string">vendor_my_entity</argument> <argument name="resourceModel" xsi:type="string">Vendor\Module\Model\ResourceModel\MyEntity\Collection</argument> </arguments> </virtualType> </config>
UI component GRID file:
<?xml version="1.0" ?> <listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> <argument name="context" xsi:type="configurableObject"> <argument name="class" xsi:type="string">Magento\Framework\View\Element\UiComponent\Context</argument> <argument name="namespace" xsi:type="string">my_listing_index</argument> </argument> <argument name="data" xsi:type="array"> <item name="js_config" xsi:type="array"> <item name="provider" xsi:type="string">my_listing_index.my_listing_grid_data_source</item> <item name="deps" xsi:type="string">my_listing_index.my_listing_grid_data_source</item> </item> <item name="spinner" xsi:type="string">my_listing_columns</item> <item name="buttons" xsi:type="array"> <item name="add" xsi:type="array"> <item name="name" xsi:type="string">add</item> <item name="label" translate="true" xsi:type="string">Add new Entity</item> <item name="class" xsi:type="string">primary</item> <item name="url" xsi:type="string">*/*/new</item> </item> </item> </argument> <dataSource name="my_listing_grid_data_source"> <argument name="dataProvider" xsi:type="configurableObject"> <argument name="class" xsi:type="string"> Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider </argument> <argument name="name" xsi:type="string">my_listing_grid_data_source</argument> <argument name="primaryFieldName" xsi:type="string">id</argument> <argument name="requestFieldName" xsi:type="string">id</argument> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item> <item name="update_url" path="mui/index/render" xsi:type="url"/> <item name="storageConfig" xsi:type="array"> <item name="indexField" xsi:type="string">id</item> </item> </item> </argument> </argument> </dataSource> <listingToolbar name="listing_top"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="sticky" xsi:type="boolean">true</item> </item> </argument> <bookmark name="bookmarks"/> <columnsControls name="columns_controls"/> <filters name="listing_filters"/> <paging name="listing_paging"/> </listingToolbar> <columns name="my_listing_columns"> <selectionsColumn name="ids"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="indexField" xsi:type="string">id</item> </item> </argument> </selectionsColumn> <column name="name"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">text</item> <item name="sorting" xsi:type="string">asc</item> <item name="label" translate="true" xsi:type="string">Name</item> </item> </argument> </column> <actionsColumn class="Vendor\Module\Ui\Component\Listing\Column\MyEntityActions" name="actions"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="indexField" xsi:type="string">id</item> </item> </argument> </actionsColumn> </columns> </listing>
Result:
6.3 Define system configuration XML and configuration scope
Define basic terms and elements of system configuration XML, including scopes. How would you add a new system configuration option?
Magento 2 allows to extend the system configuration with modules and integrate the custom module configuration into the Magento 2 system menu.
To add such custom configurations, use <module_dir>/etc/adminhtml/system.xml file.
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <tab id="custom_tab" translate="label" sortOrder="100"> <label>Custom Tab</label> </tab> <section id="custom_section" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Custom Config Section</label> <tab>custom_tab</tab> <group id="general" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>General</label> <field id="yesno_dropdown" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Custom Yes/No Dropdown</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="custom_dropdown" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Dropdown with custom source model example</label> <source_model>Vendor\Module\Model\Config\Source\Custom</source_model> </field> <field id="custom_text" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Custom Text</label> </field> <field id="logo" translate="label" type="image" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Custom Image</label> <backend_model>Magento\Config\Model\Config\Backend\Image</backend_model> <upload_dir config="system/filesystem/media" scope_info="1">logo</upload_dir> <base_url type="media" scope_info="1">logo</base_url> <comment><![CDATA[Allowed file types: jpeg, gif, png.]]></comment> </field> <field id="depends_example" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Dependant text field example with validation</label> <depends> <field id="*/*/custom_dropdown">1</field> </depends> <validate>validate-no-empty</validate> </field> <field id="custom_textarea" translate="label" type="textarea" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Custom Textarea</label> </field> <field id="custom_secret" type="obscure" translate="label" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Custom Secret Field</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> </group> </section> </system> </config> Now let us examine the code details: <tab id="custom_tab" translate="label" sortOrder="100"> <label>Custom Tab</label> </tab>
This xml code allows to add a new line into Magento 2 configuration menu
Then
<section id="custom_section" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Custom Config Section</label> <tab>custom_tab</tab> ... </section>
This code adds a new section and assigns it to custom_tab tab. Here, we can use any tab from the existing ones.
showInDefault=”1″, showInWebsite=”1″ and showInStore=”1″ parameters set the set the scope where our section will be displayed.
Setting Default Value
To set default value for any of the custom settings, create a <module_dir>/etc/config.xml file.
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> <default> <custom_section> <general> <yesno_dropdown>1</enable> <custom_text>Test Value</display_text> </general> </helloworld> </default> </config>
What is the difference in this process for different option types (secret, file)?
Simple Text Field
<field id="custom_text" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Custom Text</label> </field>
Yes/No Dropdown
<field id="yesno_dropdown" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Custom Yes/No Dropdown</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field>
Dropdown With The Custom Source model
<field id="custom_dropdown" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Dropdown with custom source model example</label> <source_model>Vendor\Module\Model\Config\Source\Custom</source_model> </field>
And our custom model can look like this:
<module_dir>/Model/Config/Source/Custom.php
<?php namespace Vendor\Model\Model\Config\Source; class Custom implements \Magento\Framework\Option\ArrayInterface { /** * @return array */ public function toOptionArray() { return [ ['value' => 0, 'label' => __('Zero')], ['value' => 1, 'label' => __('One')], ['value' => 2, 'label' => __('Two')], ]; } }
File Upload
<field id="logo" translate="label" type="image" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Custom Image</label> <backend_model>Magento\Config\Model\Config\Backend\Image</backend_model> <upload_dir config="system/filesystem/media" scope_info="1">logo</upload_dir> <base_url type="media" scope_info="1">logo</base_url> <comment><![CDATA[Allowed file types: jpeg, gif, png.]]></comment> </field>
Dependent Field
<field id="depends_example" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Dependant text field example with validation</label> <depends> <field id="*/*/yesno_dropdown">1</field> </depends> <validate>validate-no-empty</validate> </field>
<validate>validate-no-empty</validate> validates that the field is not empty when the configuration is saved.
depends tag allows to display this field only if at yesno_dropdown the Yes value was selected.
Textarea
<field id="custom_textarea" translate="label" type="textarea" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Custom Textarea</label> </field>
Secret Field
<field id="custom_secret" type="obscure" translate="label" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Custom Secret Field</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field>
type=”obscure” hides field’s value from the frontend, but the information from it will still be saved as plain text. Setting Magento\Config\Model\Config\Backend\Encrypted as a backend model allows to encrypt the data in the database.
Most of the source models are located in app/code/Magento/Config/Model/Config/Source and backend models are located in app/code/Magento/Config/Model/Config/Backend.
Describe system configuration data retrieval. How do you access system configuration options programmatically?
To access system configuration options programmatically, create a class. Inside it, create a method for obtaining the value of the needed configuration and determine the constant that contains the path to the configuration value. Example:
<module_dir>/Helper/Config.php
<?php namespace BelVG\Test\Helper; use Magento\Framework\App\Helper\AbstractHelper; use Magento\Store\Model\ScopeInterface; class Config extends AbstractHelper { const XML_PATH_TEST_VALUE = 'belvg/settings/test_value'; /** * @return string */ public function getTestValue() { return (string) $this->scopeConfig->getValue(self::XML_PATH_TEST_VALUE, ScopeInterface::SCOPE_STORE); } }
If your configuration values are encrypted and stored in the database, set backend_model in etc/config.xml file so that scopeConfig->getValue would return the decrypted configuration values. Example:
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> <default> <belvg> <settings> <test_value backend_model="Magento\Config\Model\Config\Backend\Encrypted"/> </settings> </belvg> </default> </config>
6.4 Utilize ACL to set menu items and permissions
Describe how to set up a menu item and permissions. How would you add a new menu item in a given tab?
Magento 2 allows to customize and add new points to backend menu.
To add new menu, use the following file
<module_dir>/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="Vendor_Module::second_level" title="Second Level Menu" module="Vendor_Module" sortOrder="10" action="Vendor_Module/action_path" resource="Magento_Backend::content" parent="Magento_Backend::system_design l"/> </menu> </config>
where
- id – record identifier; should be unique. {Vendor_Module}::{menu_description}.
- title – displayed title
- module – sets the module that module item belongs to in Vendor_Module format.
- sortOrder – sets the position of a menu item. The options with smaller values will be displayed higher.
- parent – id of other menu item. Defines the menu as embedded.
- action – url pages, which the menu item refers to.
- resource – sets ACL rule, necessary for viewing a certain menu item.
How would you add a new tab in the Admin menu?
The process of adding a new tab is similar to adding a new item, but the item, responsible for a new tab, does not contain a parent item.
<module_dir>/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="Vendor_Module::first_level" title="First Level Menu" module="Vendor_Module" sortOrder="51" resource="Magento_Backend::content"/> <add id="Vendor_Module::second_level" title="Second Level Menu" module="Vendor_Module" sortOrder="10" action="Vendor_Module/action_path" resource="Magento_Backend::content" parent="Vendor_Module::first_level"/> </menu> </config>
How do menu items relate to ACL permissions?
To restrict access to a certain menu item with a certain ACL rule, insert it in the resource parameter during the menu configuration:
<module_dir>/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="Vendor_Module::first_level" title="First Level Menu" module="Vendor_Module" sortOrder="51" resource="Vendor_Module::acl1"/> <add id="Vendor_Module::second_level" title="SEcond Level Menu" module="Vendor_Module" sortOrder="10" action="Vendor_Module/action_path" resource="Vendor_Module::acl2" parent="Vendor_Module::first_level"/> </menu> </config>
Describe how to check for permissions in the permissions management tree structures. How would you add a new user with a given set of permissions?
Magento 2 allows to separate the Admin Users permissions and create admin users groups with a variety of permissions.
Defining ACL
Use <module_dir>/etc/acl.xml file to create a new ACL rule.
<?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="Vendor_Module::acl1" title="ACL Parent Rule" sortOrder="51"> <resource id="Vendor_Module::acl2" title="ACL Child Rule" sortOrder="10"/> </resource> </resource> </resources> </acl> </config>
where,
- id – resource identifier. Applied in menu and actions configuration for identifying the resource. It should be unique and set in the following format: Vendor_Module::resource_name.
- title – resource title
- sortOrder – resource order in resource tree
System section > Permissions > User Roles contains all the available roles and their permissions.
System section > Permissions > All Users allows to create new users or modify the current users, as well as assign user roles.
How can you do that programmatically?
To restrict permissions to actions with ACL, override _isAllowed method or ADMIN_RESOURCE constant in the class of this action.
protected function _isAllowed() { return $this->_authorization->isAllowed(‘Vendor_Module::acl2'); }
or
const ADMIN_RESOURCE = 'Vendor_Module::acl2';
To create user role, use classes Magento\Authorization\Model\RoleFactory and Magento\Authorization\Model\RulesFactory
$role = $this->roleFactory->create(); $role->setParentId(0) ->setTreeLevel(1) ->setSortOrder(1) ->setRoleType(Group::ROLE_TYPE) ->setUserId(0) ->setUserType(UserContextInterface::USER_TYPE_ADMIN) ->setRoleName('Example Administrator'); $role->save(); /** @var \Magento\Authorization\Model\Rules $rule */ $rule = $this->rulesFactory->create(); $rule->setRoleId($role->getId()) ->setResourceId($this->rootResource->getId()) ->setPrivilegies(null) ->setPermission('allow'); $rule->save();
To create a user, create a class Magento\User\Model\UserFactory:
$adminUser = $this->userFactory->create(); $adminUser->setRoleId(ROLE_ID) ->setEmail('admin' . $i . '@example.com') ->setFirstName('Firstname') ->setLastName('Lastname') ->setUserName('admin' . $i) ->setPassword('123123q') ->setIsActive(1) ->save();
Partner With Us
Let us know more about your project. Contact us and let’s grow your business.