Skip to content

Latest commit

 

History

History
executable file
·
1225 lines (1002 loc) · 72.5 KB

6.Magento-Developing-With-Adminhtml.md

File metadata and controls

executable file
·
1225 lines (1002 loc) · 72.5 KB

6 - Developing with Adminhtml

6.1 Describe common structure/architecture

Magento Exam QuestionDescribe the difference between Adminhtml and Frontend. What additional tools and requirements exist in the admin?

  • ACL permissions - _isAllowed, change static const ADMIN_RESOURCE
  • Base controllers are different Magento\Backend\App\Action vs Magento\Backend\App\AbstractAction which accepts 4 parameters + pathPrefix (more on this below).
  • Custom URL model which uses a secret key: key
  • The layout acl block attribute
  • File tree path structures are different:
    • app/design/adminhtml vs app/design/frontend
    • <module_dir>/view/adminhtml vs <module_dir>/view/frontend
  • UIComponents are used in Adminhtml whereas they are not in Frontend.
  • The adminhtml area code of Magento\Framework\App\Area includes and allows code needed for store management using Magento\Framework\App\State whereas frontend contains template and layout files that define the appearance of your storefront only.

Magento Exam InformationAdminhtml & Frontend do share design paths in <module_dir>/view/base & app/design/base if common functionality exists between them.

Magento Exam QuestionWhat does router id="admin", router id="standard" mean in routes.xml?

The Standard Router (A.K.A the base router) sets module frontName, Controller Action names, Controller Module & Route Name if found. It processes all standard Magento URLs.

The frontName attribute value becomes the part of our URL structure. Simply put, the URL formula for hitting the C.R.U.D action is formatted like:

<store-url>/<store-code>/<front-name>/<controller-name>/<action-name>

The id attribute is the naming convention for Layout Handle XML and is formatted:

{route-id}_{controller-name}_{action-name}.xml

Magento Exam InformationWhen configuring a custom routes.xml, it's easier to keep the id and frontName the same value.

Magento Exam InformationMore information on Routers in Section 2.3.

Magento Exam QuestionSome URLs in the backend start with "admin/admin/.." - 2 times admin, some only 1 - "admin/...". How?

Magento Section ChevronConfig In Settings

The admin appears twice in the url when you have the flag:

  Use Store Codes in the URL set = Yes.

Since the admin section is actually a store view from Magento's point of view, its code (admin) is shown in the url.

The second time it appears it's because admin is the route key for the adminhtml controllers. For example, if you set a custom admin url key in the configuration:

System > Configuration > Admin > Admin Base URL > Custom Admin Path

The url will look like https:///example.com/admin/backend/controller/action/

The First link hides behind Magento_Backend's original frontName route "adminhtml"="admin". The Second link has it's own frontName="theme" and is more beautiful.

Magento Section ChevronAdmin Workflow

  1. bootstrap.run(application)
  2. application.launch
  3. front controller.dispatch
  4. admin routerList: admin | default
  5. Magento\Backend\App\Router (admin router) - Same as frontend Magento\Framework\App\Router\Base (standard router) except for:
    • It parses 4 sections:
      1. 'areaFrontName' = area such as "admin" which uses "adminhtml" area code.
      2. 'moduleFrontName' = The modules frontName e.g. "theme".
      3. 'actionPath' The modules action Controller path e.g. "design_config".
      4. 'actionName' The modules action method path e.g. "index" + additional query parameters such as /key/4bd2...13/
    • pathPrefix = 'adminhtml' -- all controller classes are searched in Module\Controller\Adminhtml...
  6. Module_Backend registers router i.e. getUrl("adminhtml/controller/action")

6.2 Define form and grid widgets

Magento Exam QuestionDefine form structure, form templates, grids, grid containers, and elements. What steps are needed to display a grid or form?

Magento Section ChevronStep 1 - Define An ACL Resource

See Section 6.4 on how to do this.

Magento Section ChevronStep 2 - Define A Menu Item

See Section 6.4 on how to do this.

Magento Section ChevronStep 3 - Define A Route

Define a new routes.xml in your custom module. The contents of this XML file tells Magento to route requests that use the frontName "exampleadminnewpage" to this module. The file is found in:

<module_dir>/etc/adminhtml/routes.xml

Example MyVendor/MyModule/Controller/MyExampleAdminPage/Index.php:

<?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="exampleadminnewpage" frontName="exampleadminnewpage">
                       <module name="MyVendor_MyModule"/>
                   </route>
        </router>
</config>

Magento Section ChevronStep 4 - Create A Controller & Layout Handle

A layout handle is a uniquely identified set of layout instructions that serves as a name of a layout file. The id attribute of the node defined above will be the naming convention for this Layout Handle XML and is formatted and found in:

<module_dir>/view/adminhtml/{route-id}_{controller-name}_{action-name}.xml

In this example it would be exampleadminnewpage_myexampleadminnewpage_index.xml

Magento Exam InformationMore on Layout Handles here.

Example Layout Handle with uiComponent node:

<?xml version="1.0"?>
<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">
                               <uiComponent name="example_admin_new_page" />
                   </referenceContainer>
        </body>
</page>

Magento Section ChevronCreate A UI Component

Magento UI components are used to represent distinct UI elements, such as tables, buttons, dialogs, and others. They are designed for simple and flexible user interface (UI) rendering. UI component is a combination of:

  • XML declaration that specifies the component's configuration settings and inner structure.
  • JavaScript class inherited from one of the Magento JavaScript framework UI components base classes (such as UIElement, UIClass or UICollection).
  • Related template(s)

UI Component is configured through a dedicated XML file:

<module_dir>/view/<adminhtml/base>/ui_component/{ui_component_name.xml}

In this example:

MyVendor/MyModule/view/adminhtml/ui_component/example_admin_new_page.xml

For Adminhtml, UI Components take the form of either a UI Form Component or UI Listing (Grid) Component.

Magento Section ChevronStep 6 - Define a DataSource Provider Component

Magento provides the DataSource object, which is designed to interact with data in your UI component. Many of the core UI components use this DataSource component. Many UI components require that this object is included. However, there are specific requirements in order for it to work correctly.

For more information, please refer to the DataSource Provider Component section below.

Magento Section ChevronStep 7 - Define & Create Actions

<type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
        <arguments>
                   <argument name="collections" xsi:type="array">
                                <item name="example_admin_new_page_grid_data_source" xsi:type="string">MyVendor\MyModule\Model\ResourceModel\CustomAdminPage\Grid\Collection</item>
                   </argument>
        <arguments>
</type>

Example MyVendor/MyModule/view/adminhtml/ui_component/example_admin_new_page.xml:

Magento Exam QuestionDescribe the grid and form workflow.

Magento Section ChevronServer Side

  1. Layout handle loads UI component
  2. XML files of UI component are searched in each enabled module (<module_dir>/view/<area>/ui_component/<ui_component_name>.xml)
  3. All found files of UI components are merged in a single configuration object
  4. Configuration and definition.xml are merged. Objects from definition.xml have lower priority than single configuration object, received in the previous step
  5. 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 outputted JSON code:

<script type="text/x-magento-init">
{
    "*": {
        "Magento_Ui/js/core/app": {
            "types":{ ... },

            "components":{ ... }
        }
    }
}
</script>

Magento Exam InformationTo help remember this, think about how Magento Adminhtml standard router requests work then add the extra UI Component / Configuration / definition.xml

Magento Section ChevronClient Side

  1. RequireJS loads Magento_Ui/js/core/app and passes configuration as a parameter.
  2. Magento_Ui/js/core/app calls Magento_Ui/js/core/renderer/layout and passes it the configuration.
  3. layout.js creates instances of UI components and applies the configuration to it
  4. HTML template rendering with knockout.js and binding of the component to the template is performed

Data Source

The data for grid and form is loaded with the help of DataProvider. The difference lies in the moment of information upload.

Form Component

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.

Grid ComponentFor 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 for adminhtml area and Magento\Ui\Controller\Index\Render for frontend area .

Magento Exam QuestionHow is data provided to the grid or form?

Magento Section ChevronDataSource Provider Component

Magento provides the DataSource object, which is designed to interact with data in your UI component. Many of the core UI components use this DataSource component. Many UI components require that this object is included. However, there are specific requirements in order for it to work correctly.

Using Magento_Ui/view/base/ui_component/etc/definition.map.xml we can deduce full definition and what parameter will end up where:

<dataSource class="Magento\Ui\Component\DataSource">
   <aclResource/>
   <settings>
       <submitUrl />
       <validateUrl />
       <updateUrl/>
       <filterUrlParams/>
       <storageConfig/>
       <statefull/>
       <imports/>
       <exports/>
       <links/>
       <listens/>
       <ns/>
       <componentType/>
       <dataScope/>
       <deps/>
       <layout>
           <type/>
           <navContainerName/>
       </layout>
   </settings>
   <dataProvider name="some_name" class="SomeDataProviderClass">
       <settings>
           <primaryFieldName>entity_id</primaryFieldName>
           <requestFieldName>id</requestFieldName>
       </settings>
   </dataProvider>
</dataSource>

**<dataSource>**:

<argument name="data" xsi:type="array">
    <item name="js_config" xsi:type="array">
        <item name="provider" xsi:type="string">[ComponentName].[ComponentName]_data_source</item>
        <item name="deps" xsi:type="string">[ComponentName].index_listing_data_source</item>
    </item>
</argument>
<dataSource name="index_listing_data_source" component="Magento_Ui/js/grid/provider">
        <settings>
           <updateUrl path="mui/index/render"/>
        </settings>
        <aclResource>Vendor_MyModule::view</aclResource>
        <dataProvider class="MyVendor\MyModule\Ui\Component\CustomAdminPage\Listing\DataProvider" name="index_listing_data_source">
           <settings>
               <requestFieldName>id</requestFieldName>
               <primaryFieldName>main_table.id</primaryFieldName>
           </settings>
        </dataProvider>
</dataSource>

Magento Exam InformationIn our example [ComponentName] = example_admin_new_page which matches the ui_component XML file name.

The main node of interest is <dataProvider class="">. This references a PHP class that MUST implement Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface to meet that requirement, it can extend the following classes:

  • Magento\Ui\DataProvider\AbstractDataProvider
  • Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider

The node <item name="js_config"> is a good way to keep configuration data out of the JavaScript. It accepts:

  • <item name="provider" xsi:type="string"> = will output in the JSON that will contain the UI component's configuration.
  • <item name="deps" xsi:type="string"> = the name="" of your <dataSource> in which will be the provider.
  • <item name="component" xsi:type="string"> = the jsComponent of your UI Component, for a form use Magento_Ui/js/form/provider. For a grid use Magento_Ui/js/grid/provider. These can be extended if it does not do exactly what you need.

Other nodes of interest / importance include:

Javascript Component Linking

Every Javascript component should extend the core uiElement class in some way. When this class initializes it runs an initLinks() method. That method, in turn, passes a few class properties into a method that handles linking components together. This file (lib/core/element/link.js) binds the values of those parameters to actual components.

The properties Magento will parse are:

  • imports
  • exports
  • links

<exports/>

The <exports> property is used to copy a local value to some external entity. Exports's value is an object, composed of the following:

<argument name="data" xsi:type="array">
    <item name="config" xsi:type="array">
        <item name="exports" xsi:type="array">
            <item name="visible" xsi:type="string">sample_config.sample_provider:visibility</item>
        </item>
    </item>
</argument>

Here visible is the key, ${ $.provider }:visibility is the value. The value of the local visible property is assigned to the visibility property of the provider component. The latter is changed automatically if the value of visible changes if the local visible property is an ko io-es5 observable (which it isn't given in the code example above).

Within the jsComponent's defaults property:

{
    "defaults": {
        "exports": {
            "visible": "${ $.provider }:visibility"
        }
    }
}

<imports />

The <imports> property is used for tracking changes of an external entity property. imports's value is an object, composed of the following:

<argument name="data" xsi:type="array">
    <item name="config" xsi:type="array">
        <item name="imports" xsi:type="array">
            <item name="visible" xsi:type="string">sample_config.sample_provider:visibility</item>
        </item>
    </item>
</argument>

Within the jsComponent's defaults property:

{
    "defaults": {
        "imports": {
            "visible": "${ $.provider }:visibility"
        }
    }
}

<links />

The links property is used for cross tracking properties changes (import and export combined): both linked properties are tracked and changing of one results in changing the other. links's value is an object, composed of the following:

<argument name="data" xsi:type="array">
    <item name="config" xsi:type="array">
        <item name="links" xsi:type="array">
            <item name="visible" xsi:type="string">sample_config.sample_provider:visibility</item>
        </item>
    </item>
</argument>

The links property of a jsComponent is the same as duplicating a value in both imports and exports. Each of those properties expect an object that contains key/value pairs to bind the expression to. In the example above, it would appear in the defaults property like this:

{
    "defaults": {
        "links": {
            "visibility": "${ $.provider }:data.visibility"
        }
    }
}

<listens />

The <listens /> property is used to track the changes of a component's property. listens's value is an object, composed of the following:

<argument name="data" xsi:type="array">
    <item name="config" xsi:type="array">
        <item name="listens" xsi:type="array">
            <item name="sample_config.sample_provider:visibility" xsi:type="string">visibilityChanged</item>
        </item>
    </item>
</argument>

In this example, the local visibilityChanged property is a method that will be called when the visibility property of the provider component changes. It receives the new value as an argument but, if the local property is not a function, it will be set to the new value (although the external property has to be a KO observable in order for listens to have any effect).

Template Literals

Throughout Magento's core Javascript components there are strings like this: '${ $.provider }:data.totalRecords'. These are ES2015 template literals. The ${ } surrounds an expression that will be parsed as Javascript. $.provider is the expression, in this example.

When the uiElement class initializes, it will process the link that was declared in imports. Remember that one of the first things Magento does is process string literals, though, so it is actually working with something that looks more like the following:

{
    "defaults": {
        "exports": {
            "items": "checkout.sidebar.summary.cart_items:items"
        }
    }
}

Magento Exam QuestionHow can this process be customized or extended?

You can change the form data the following ways:

  1. Create a custom DataProvider and specify it in the xml file of the UI component.
  2. Create a plugin for DataProvider::getData() method
  3. Create Modifier class and set it in di.xml, if this DataProvider support Modifiers (for example, like Magento\Catalog\Ui\DataProvider\Product\Form\ProductDataProvider)

Magento Exam QuestionDescribe 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?

Magento Section ChevronUI Listing (Grid) Component

Listing is a user interface component that implements grids, lists and tiles with filtering, pagination, sorting and other features. Much like the sales_order_grid

Listing (Grid) is a Basic Component.

UI Grid Listing config tree:

Root Node Description Child Nodes
<argument name="data"> Related to the <dataSource> <item name="">, js_config, provider, deps , spinner
<button> The Button component allows user to perform a list of predefined actions by clicking on the corresponding button <argument name="data">, <item name="">, config, buttonClasses, actions, <settings>, <displayAsLink>, <title>
<columns> The Columns component is a collection of columns. It renders the <table> element. <actionsColumn name="">, <selectionsColumn name="">, <column name=""> - DateColumn, ExpandableColumn, ImagePreviewColumn, LinkColumn, MultiselectColumn, OnOffColumn, RangeColumn, ThumbnailColumn. <settings> - DragAndDrop
<container> <exportButton name="">
<dataSource component="" name=""> Another UI component that provides data in specific format which is shared among all UI components. component="Magento_Ui/js/form/provider" The listing component requires the data source to be properly configured and associated with it. <aclResource>, <dataProvider class="" name="">. <settings> - requestFieldName, primaryFieldName. <settings> - <updateUrl path="mui/index/render" />, <submitUrl path="">, <validateUrl path="">
<listingToolbar> <bookmark name="">. <columnsControls>. <filters name="">- FilterChips. <massaction name="">- TreeMassActions. <paging name="">- SizesComponent.
<filterSelect name="uiSelect"> The UI-select component is a single select/multiple select component that enables the selection of a collection of items. It extends all abstract configuration and can be configured in two modes: 1. Single - checkbox isn't displayed. 2. Multiple - checkboxes are displayed
<settings> Additional configurations such as multiple buttons

Magento Exam InformationNodes such as are part of a UI Components Basic Configuration Elements. You can find all available settings for each node in the XSD of that XML file.

UI Grid Listing Example MyVendor/MyModule/view/adminhtml/ui_component/example_admin_new_page.xml):

<?xml version="1.0" encoding="UTF-8"?>

<listing 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">custom_admin_grid.custom_admin_grid</item>
            <item name="deps" xsi:type="string">custom_admin_grid.custom_admin_grid_data_source</item>
        </item>
        <item name="spinner" xsi:type="string">convert_monitor_indexers_catalogindexers_columns</item>
    </argument>
    <settings>
        <buttons>
            <button name="add" class="MyVendor\MyModule\Block\Adminhtml\CustomAdminPage\Button\Add" />
        </buttons>
    </settings>
    <dataSource name="custom_admin_grid_data_source" component="Magento_Ui/js/grid/provider">
        <settings>
            <updateUrl path="mui/index/render"/>
        </settings>
        <aclResource>Convert_MonitorIndexers::config</aclResource>
        <dataProvider class="MyVendor\MyModule\Ui\Component\CustomAdminPage\DataProvider" name="custom_admin_grid_data_source">
            <settings>
                <requestFieldName>id</requestFieldName>
                <primaryFieldName>main_table.id</primaryFieldName>
            </settings>
        </dataProvider>
    </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"/>
        <!-- todo Add some mass actions ?
           <massaction name="listing_massaction">
           </massaction>-->
        <paging name="listing_paging"/>
    </listingToolbar>
    <columns name="convert_monitor_indexers_catalogindexers_columns">
        <selectionsColumn name="id">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="indexField" xsi:type="string">id</item>
                    <item name="sortOrder" xsi:type="number">10</item>
                </item>
            </argument>
        </selectionsColumn>
        <column name="id">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">textRange</item>
                    <item name="sorting" xsi:type="string">asc</item>
                    <item name="label" xsi:type="string" translate="true">Entity ID</item>
                    <item name="sortOrder" xsi:type="number">20</item>
                </item>
            </argument>
        </column>
        <actionsColumn name="actions" class="MyVendor\MyModule\Ui\Component\CustomAdminPage\Listing\Columns\Actions">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="indexField" xsi:type="string">id</item>
                    <item name="urlEntityParamName" xsi:type="string">id</item>
                    <item name="sortOrder" xsi:type="number">20</item>
                </item>
            </argument>
        </actionsColumn>
    </columns>
</listing>

Magento Section ChevronUI Form Component

The Form is a user interface component comprising a collection of fields that can be grouped in tabs and fieldsets. It enables CRUD operations.

Form is a Basic Component.

UI Form config tree:

Root Node Description Child Nodes
<dataSource> Another UI component that provides data in specific format which is shared among all UI components. component="Magento_Ui/js/form/provider". The listing component requires the data source to be properly configured and associated with it <aclResource>. <dataProvider class="" name=""> - <settings>, requestFieldName, primaryFieldName. <settings>
<dynamicRows> The DynamicRows component is a dynamic collection of records. The user can edit the records, change their position, and navigate through the collection. <actionDelete>
<fieldset> The Fieldset component implements a container for visually-grouped form elements, such as buttons and form fields. <checkboxset> - <argument name="data">, <item name="config">. additionalInfo. <email>. <field name=""> - <argument name="data"> - <item name="config"> - , label, visible, dataType, formElement. FileUploader component - <formElements>. <checkbox>. Input component. Multiline component. Multiselect component. Text component. Textarea component WYSIWYG compon nt. <fieldset> (can p rent itself). <file>. <hidd n>. <radioset . <select>. <settings> - <options>, <option>, <item name="value">, <item name="label">, <caption>, <label>.
<htmlContent> The HtmlContent UI component provides the ability to process and render a layout structure or a Magento block directly inside a UI component configuration. Processing and rendering is executed on the server side. <block>

Magento Exam InformationNodes such as are part of a UI Components Basic Configuration Elements. You can find all available settings for each node in the XSD of that XML file.

UI Form Example:

<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">customer_form.customer_form_data_source</item>
        </item>
        <item name="label" xsi:type="string" translate="true">Customer Information</item>
        <item name="reverseMetadataMerge" xsi:type="boolean">true</item>
    </argument>
    <dataSource name="customer_form_data_source">
        <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>
        <settings>
            <validateUrl path="customer/index/validate" />
            <submitUrl path="customer/index/save" />
        </settings>
        <dataProvider class="MyVendor\MyModule\Model\Customer\DataProvider" name="customer_form_data_source">
            <settings>
                <requestFieldName>id</requestFieldName>
                <primaryFieldName>entity_id</primaryFieldName>
            </settings>
        </dataProvider>
    </dataSource>
    <fieldset name="customer">
        <settings>
            <label translate="true">Account Information</label>
        </settings>
        <field name="entity_id" formElement="input">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="source" xsi:type="string">customer</item>
                </item>
            </argument>
            <settings>
                <dataType>text</dataType>
                <visible>false</visible>
            </settings>
        </field>
    </fieldset>
    <settings>
        <buttons>
            <button name="save_and_continue" class="MyVendor\MyModule\Block\Adminhtml\Edit\SaveAndContinueButton" />
            <button name="save" class="MyVendor\MyModule\Block\Adminhtml\Edit\SaveButton" />
            <button name="reset" class="MyVendor\MyModule\Block\Adminhtml\Edit\ResetButton" />
            <button name="order" class="MyVendor\MyModule\Block\Adminhtml\Edit\OrderButton" />
            <button name="resetPassword" class="MyVendor\MyModule\Block\Adminhtml\Edit\ResetPasswordButton" />
            <button name="unlock" class="MyVendor\MyModule\Block\Adminhtml\Edit\UnlockButton" />
            <button name="invalidateToken" class="MyVendor\MyModule\Block\Adminhtml\Edit\InvalidateTokenButton" />
            <button name="delete" class="MyVendor\MyModule\Block\Adminhtml\Edit\DeleteButton" />
            <button name="back" class="MyVendor\MyModule\Block\Adminhtml\Edit\BackButton" />
        </buttons>
        <layout>
            <navContainerName>left</navContainerName>
            <type>tabs</type>
        </layout>
        <deps>
            <dep>customer_form.customer_form_data_source</dep>
        </deps>
    </settings>
</form>

6.3 Define system configuration XML and configuration scope

Magento Exam QuestionDefine basic terms and elements of system configuration XML, including scopes.

The system.xml file allows you to manage the Magento system configuration. Use this topic as a general reference for the system.xml file. The system.xml file is located under etc/adminhtml/system.xml in a given Magento 2 extension.

Tabs // Sections // Groups // Fields

In the system.xml file, it is possible to define four different types of entities: tabs, sections, groups, and fields. Each related but rest in different sections of Admin > Stores > Configuration. Like so:

system.xml

etc/adminhtml/system.xml:

<?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="my_vendor" translate="label" sortOrder="0">
            <label>Example Vendor</label>
        </tab>
        <section id="convert" translate="label" type="text" sortOrder="0" showInDefault="1" showInWebsite="0" showInStore="0">
            <label>General</label>
            <tab>my_vendor</tab>
            <resource>MyVendor_MyModule::config</resource>
            <group id="general" showInDefault="1" showInStore="1" showInWebsite="1" sortOrder="10" translate="label">
                <label>Settings</label>
                <field id="debug" showInDefault="1" showInStore="1" showInWebsite="1" sortOrder="10" translate="label" type="select">
                    <label>Debug</label>
                    <comment>Enable debug mode for module</comment>
                    <source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model>
                </field>
            </group>
        </section>
    </system>
</config>

Magento Section ChevronTabs

Node Attribute Reference:

<tab> Node Attribute Description Type Required
id="" Defines the identifier that is used referencing the section. typeId Yes
translate="label" Defines the field that should be translatable. Usually "label". Be sure to provide a label to make the label translatable. string No
type="" Defines the input type of the rendered HTML element - defaults to text. string No
sortOrder="" Defines the sort order of the section. High numbers push the section to the bottom of the page; low numbers push the section to the top. float No
class="" Adds a defined CSS class to the rendered tab HTML element. string No

Magento Exam InformationSee Section 5.3 EAV Attribute Features for available types.

Node Reference

<tab> Node Children Description Type
label Defines the label that is displayed in the frontend. Ensure the translate="" attribute is set string

Magento Section ChevronSections

Node Attribute Reference:

<section> Node Attribute Description Type Required
id="" Defines the identifier that is used referencing the section. typeId Yes
translate="label" Defines the field that should be translatable. Usually "label". Be sure to provide a label to make the label translatable. string No
type="" Defines the input type of the rendered HTML element - defaults to text. string No
sortOrder="" Defines the sort order of the section. High numbers push the section to the bottom of the page; low numbers push the section to the top. float No
showInDefault="" Show in the Default Configuration Scope int No
showInWebsite="" Show in the Website Configuration Scope int No
showInStore="" Show in the Store Configuration Scope int No
translate="label" Defines the label that is displayed in the frontend. Ensure the translate="" attribute is set string No
canRestore="" Defines if the section can be restored to default. int No
advanced="" Deprecated since 100.0.2. bool No
extends="" By providing an identifier of another section, the content of this node will extend the section that you referenced. string No

Node Reference:

<section> Node Children Description Type
label Defines the label that is displayed in the frontend. Ensure the translate="" attribute is set string
class Adds a defined CSS class to the rendered section HTML element. string
tab References the associated tab. Expects the ID of the tab. typeTabId
header_css Neither used nor evaluated at the time of this writing. string
resource References an ACL resource to provide permission settings for this section. typeAclResourceId
resource Define one or more subgroups. typeGroup
frontend_model Specifies a different frontend model to change the rendering and modify the output. typeModel
include Used to include additional system_include.xsd compatible files. Usually used to structure large system.xml files. includeType

Magento Exam InformationSee Section 5.3 EAV Attribute Features for available type models.

Magento Section ChevronGroups

Node Attribute Reference:

<group> Node Attribute Description Type Required
id="" Defines the identifier that is used referencing the section. typeId Yes
translate="label" Defines the field that should be translatable. Usually "label". Be sure to provide a label to make the label node translatable. string No
type="" Defines the input type of the rendered HTML element - defaults to text. string No
sortOrder="" Defines the sort order of the section. High numbers push the section to the bottom of the page; low numbers push the section to the top. float No
showInDefault="" Show in the Default Configuration Scope int No
showInWebsite="" Show in the Website Configuration Scope int No
showInStore="" Show in the Store Configuration Scope int No
canRestore="" Defines if the section can be restored to default. int No
advanced="" Deprecated since 100.0.2. bool No
extends="" By providing an identifier of another group, the content of this node will extend the section that you referenced. string No

Node Reference:

<group> Node Children Description Type
label Defines the label that is displayed in the frontend. Ensure the translate="" attribute is set string
fieldset_css Adds one or more CSS classes to a group fieldset. string
frontend_model Specifies a different frontend model class namespace to change the rendering and modify the output. typeModel
clone_model Specifies a given model to clone fields. typeModel
clone_fields Enabled or disabled cloning of fields. int
help_url Not extensible. See below. typeUrl
more_url Not extensible. See below. typeUrl
demo_link Not extensible. See below. typeUrl
comment Adds a comment below the group label. By using <![CDATA[//]]> HTML can be applied. string
hide_in_single_store_mode Whether the group should be visible in single store mode. 1 hides the group; 0 shows the group. int
field Define one or more fields that should be available under this group. field
group Define one or more subgroups. unbounded
depends Can be used to declare dependencies on other fields. Is used to show specific fields/groups only when a given field has a value of 1. This node expects a section/group/field. format depends
attribute Custom attributes can be used by frontend models. Usually used to make a given frontend model more dynamic. attribute
include Used to include additional system_include.xsd compatible files. Usually used to structure large system.xml files. includeType

Magento Exam InformationSee Section 5.3 EAV Attribute Features for available type models.

Magento Exam InformationThe nodes more_url, demo_url and help_url are defined by a PayPal frontend model that is only used once. These nodes are not reusable.

Magento Section ChevronFields

Node Attribute Reference:

<field> Node Attribute Description Type Required
id="" Defines the identifier that is used referencing the section. typeId Yes
translate="label" Defines the field that should be translatable. Usually "label". Be sure to provide a label to make the label translatable. string No
type="" Defines the input type of the rendered HTML element - defaults to text. Depending on the type used, the <source_model> node is required. Available Types Include: button, checkbox, checkboxes, column, date, editablemultiselect, editor, fieldset, file, gallery, hidden, image, imagefile, label, link, multiline, multiselect, note, obscure, password, radio, radios, reset, select, submit, text, textarea, time. string
sortOrder="" Defines the sort order of the section. High numbers push the section to the bottom of the page; low numbers push the section to the top. float No
showInDefault="" Show in the Default Configuration Scope int No
showInWebsite="" Show in the Website Configuration Scope int No
showInStore="" Show in the Store Configuration Scope int
canRestore="" Defines if the section can be restored to default. int No
advanced="" Deprecated since 100.0.2. bool No
extends="" By providing an identifier of another group, the content of this node will extend the section that you referenced. string No

Node Reference:

<field> Node Children Description Type
label Defines the label that is displayed in the frontend. string
comment Adds a comment below the group label. By using HTML can be applied. string
tooltip Another possible frontend element that also can be used to describe the meaning of this field. Will be displayed as a small icon beside the field. string
hint Displays additional information. Only available with specific frontend_model. string
frontend_class Displays additional information. Only available with specific frontend_model. string
frontend_model Specifies a different frontend model to change the rendering and modify the output. typeModel
backend_model Specifies a different backend model to modify the configured values. typeModel
source_model Specifies a different source model that provides a specific set of values. typeModel
config_path Can be used to overwrite the generic config path of a field. typeConfigPath
validate Define different validation rules (comma separated). Full reference list of available validation rules is listed below. string
can_be_empty Used when type is multiselect to specify that a field can be empty. int
if_module_enabled Used to display a field only when a given module is enabled. typeModule
base_url Used in combination with upload_dir for file uploads. typeUrl
upload_dir Specify a target directory for uploads. This node has additional attributes and nodes. Look them up before using this. typeUploadDir
button_url Displays a button if button_url and button_label are specified. Usually used in combination with a custom frontend model. typeUrl
button_label Displays a button if button_label and button_url are specified. Usually used in combination with a custom frontend model. string
more_url Not extensible. See below. typeUrl
demo_url Not extensible. See below. typeUrl
hide_in_single_store_mode Whether the group should be visible in single store mode. 1 hides the group; 0 shows the group. int
source_service Service used to populate select options. complexType
options Not used. Potentially deprecated. complexType
depends Can be used to declare dependencies to other fields. Is used to only show specific fields/groups when a given field has a value of 1. This node expects a section/group/field-string. complexType
attribute Custom attributes can be used by frontend models. Usually used to make a given frontend model more dynamic. complexType
requires Not extensible. See below. complexType

Magento Exam InformationSee Section 5.3 EAV Attribute Features for available type models.

Magento Exam InformationThe nodes more_url, demo_url and help_url are defined by a PayPal frontend model that is only used once. These nodes are not reusable.

Magento Exam QuestionHow would you add a new system configuration option?

<module_dir>etc/adminhtml/system.xml

Magento Section ChevronTabs example

<?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="A_UNIQUE_ID" translate="label" class="a-custom-css-class-to-style-this-tab" sortOrder="10">
            <label>A meaningful label</label>
        </tab>
    </system>
</config>

Magento Section ChevronSections example

<?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="A_UNIQUE_ID" translate="label" class="a-custom-css-class-to-style-this-tab" sortOrder="10">
            <label>A meaningful label</label>
        </tab>
        <section id="A_UNIQUE_SECTION_ID" showInDefault="1" showInWebsite="0" showInStore="1" sortOrder="10" translate="label">
            <label>A meaningful section label</label>
            <tab>A_UNIQUE_ID</tab>
            <resource>VENDOR_MODULE::path_to_the_acl_resource</resource>
        </section>
    </system>
</config>

Magento Section ChevronGroups example

<?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="A_UNIQUE_ID" translate="label" class="a-custom-css-class-to-style-this-tab" sortOrder="10">
            <label>A meaningful label</label>
        </tab>
        <section id="A_UNIQUE_SECTION_ID" showInDefault="1" showInWebsite="0" showInStore="1" sortOrder="10" translate="label">
            <label>A meaningful section label</label>
            <tab>A_UNIQUE_ID</tab>
            <resource>VENDOR_MODULE::path_to_the_acl_resource</resource>
            <group id="A_UNIQUE_GROUP_ID" translate="label comment" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="1">
                <label>A meaningful group label</label>
                <comment>An additional comment helping users to understand the effect when configuring the fields defined in this group.</comment>
                <!-- Add your fields here. -->
            </group>
        </section>
    </system>
</config>

Magento Section ChevronFields example

<?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="A_UNIQUE_ID" translate="label" class="a-custom-css-class-to-style-this-tab" sortOrder="10">
            <label>A meaningful label</label>
        </tab>
        <section id="A_UNIQUE_SECTION_ID" showInDefault="1" showInWebsite="0" showInStore="1" sortOrder="10" translate="label">
            <label>A meaningful section label</label>
            <tab>A_UNIQUE_ID</tab>
            <resource>VENDOR_MODULE::path_to_the_acl_resource</resource>
            <group id="A_UNIQUE_GROUP_ID" translate="label" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="1">
                <label>A meaningful group label</label>
                <comment>An additional comment helping users to understand the effect when configuring the fields defined in this group.</comment>
                <field id="A_UNIQUE_FIELD_ID" translate="label" sortOrder="10" showInDefault="0" showInWebsite="0" showInStore="1" type="select">
                    <label>Feature Flag Example</label>
                    <comment>This field is an example for a basic yes or no select.</comment>
                    <tooltip>Usually these kinds of fields are used to enable or disable a given feature. Other fields might be dependent to this and will only appear if this field is set to yes.</tooltip>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
                <field id="ANOTHER_UNIQUE_FIELD_ID" translate="label" sortOrder="10" showInDefault="0" showInWebsite="0" showInStore="1" type="text">
                    <label>A meaningful field label</label>
                    <comment>A descriptive text explaining this configuration field.</comment>
                    <tooltip>Another possible frontend element that also can be used to describe the meaning of this field. Will be displayed as a small icon beside the field.</tooltip>
                    <validate>required-entry no-whitespace</validate>                    <!-- Field is required and must not contain any whitespace. -->
                    <if_module_enabled>VENDOR_MODULE</if_module_enabled>
                    <depends>
                        <!-- This field will only be visible if the field with the id A_UNIQUE_FIELD_ID is set to value 1 -->
                        <field id="A_UNIQUE_FIELD_ID">1</field>
                    </depends>
                </field>
            </group>
        </section>
    </system>
</config>

Magento Exam QuestionWhat is the difference in this process for different option types (secret, file)?

See the Fields part of this section to see the available types. Different Field Types require certain child nodes in order to operate.

Magento Section ChevronField Text

Text Fields have no requirements:

<field id="custom_text" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1">
    <label>Custom Text</label>
</field>

Magento Section ChevronField Dropdown With The Custom Source Model

Certain Select Fields require the <source_model> node, such like:

<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>MyVendor\MyModule\Model\Config\Source\Custom</source_model>
</field>

The custom <source_model> class will look something like:

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')],
        ];
    }
}

Magento Section ChevronField Yes/No Dropdown

Yes/No Dropdown Fields require the <source_model> node, such like:

<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>

Magento Section ChevronField File Upload

File Upload Fields require the <backend_model> <upload_dir> <base_url> nodes, such like:

<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>

Magento Section ChevronField Dependent

Dependent Fields are available to all types, just need to define <depends> node such like:

<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>

Magento Section ChevronField Textarea

Textarea Fields have no requirements:

<field id="custom_textarea" translate="label" type="textarea" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1">
    <label>Custom Textarea</label>
</field>

Magento Section ChevronField Secret

Secret Fields require the <backend_model> & type="obscure" node such like:

<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>

Magento Exam Informationtype="obscure" hides the 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.

Magento Exam InformationMost of the source models are located in Magento\Config\Model\Config\Source and backend models are located in Magento\Config\Model\Config\Backend.

Magento Exam QuestionSetting Default Value

To set default value for any of the custom settings, create a file:

<module_dir>/etc/config.xml

The format goes: section/group/field

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>
        <custom_section>
            <general>
                <yesno_dropdown>1</enable>
                <custom_text>Test Value</display_text>
            </general>
        </custom_section>
    </default>
</config>

Magento Exam QuestionDescribe system configuration data retrieval. How do you access system configuration options programmatically?

Programmatically requesting system configuration options are available from class: Magento\Framework\App\Config\ScopeConfigInterface

Usually within a custom module this resides within a Helper class' $this->scopeConfig property which extends class: Magento\Framework\App\Helper\AbstractHelper

Example:

<?php

namespace MyVendor\MyModule\Helper;

use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Store\Model\ScopeInterface;

class Config extends AbstractHelper
{
    const XML_PATH_VENDOR = 'myvendor/';

    /** @return string */
    public function getTestValue(): string
    {
        return (string) $this->scopeConfig->getValue(
            static::XML_PATH_VENDOR . 'settings/test_value',
            ScopeInterface::SCOPE_STORE
        );
    }
}

6.4 Utilize ACL to set menu items and permissions

Magento Exam QuestionDescribe how to set up a menu item and permissions.

Access Control List (ACL) rules allow an admin to limit the permissions of users in Magento. For example, you can use ACL rules to authorize the users to access menus, controllers, API endpoints and conditionally render layout blocks.

ACL is defined in:

<module_dir>/etc/adminhtml/acl.xml

Example:

<?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_MyModule::menu" title="Custom Menu" sortOrder="10">
                    <resource id="Vendor_MyModule::create" title="Create" sortOrder="50" />
                    <resource id="Vendor_MyModule::delete" title="Delete" sortOrder="100" />
                    <resource id="Vendor_MyModule::view" title="View" sortOrder="150">
                        <resource id="Vendor_MyModule::view_additional" title="View Additional Information" sortOrder="10" />
                    </resource>
                </resource>
            </resource>
        </resources>
    </acl>
</config>
<resource> Attribute Description
id Unique string. Should be in the format Vendor_ModuleName::resourceName
title Title which is displayed in the menu bar
sortOrder Position in which menu is displayed

Magento Exam QuestionHow would you add a new menu item in a given tab?

Using declarative schema in menu.xml you can add items to Magento's main left navigation of the admin.

Menu Items are defined in:

<module_dir>etc/adminhtml/menu.xml

Example:

<?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_MyModule::menu" title="Custom Menu" module="Vendor_MyModule" sortOrder="10" resource="Vendor_MyModule::menu"/>
        <add id="Vendor_MyModule::create" title="Create" module="Vendor_MyModule" sortOrder="10" parent="Vendor_MyModule::menu" action="custommenu/create/index" resource="Vendor_MyModule::create"/>
        <add id="Vendor_MyModule::delete" title="Delete" module="Vendor_MyModule" sortOrder="20" parent="Vendor_MyModule::menu" action="custommenu/delete/index" resource="Vendor_MyModule::delete"/>
        <add id="Vendor_MyModule::view" title="View" module="Vendor_MyModule" sortOrder="30" parent="Vendor_MyModule::menu" action="custommenu/view/index" resource="Vendor_MyModule::view"/>
    </menu>
</config>
<add> Attribute Description
id Unique string. Should be in the format: Vendor_ModuleName::resourceName
title Title which is displayed in menu bar
module Module which containing the current menu
sortOrder Position in which menu to be displayed
parent The another menu which is parent of current menu
action Url of the page which needs to be displayed after clicking the menu. It should be in following format: front_name/controller_path/action
resource ACL rule to restrict the access

Magento Exam QuestionHow would you add a new tab in the Admin menu?

Magento Section Chevron Primary Sidebar Navigation Menu

Define an node WITHOUT a parent="" attribute in:

<module_dir>/etc/adminhtml/menu.xml

More info on how to define this above.

Magento Exam InformationIn contrast, define a parent="" attribute if you'd like your menu item to appear under a sub-menu / tab.

Magento Section ChevronSystem Stores Configuration Tab

If you'd rather your new settings be in a Tab format under:

Admin > Stores > Configuration

Then you will need to configure a new system.xml Under and existing tab, you need to specify the existing tab's ID such as:

<tab>general</tab>

Otherwise you will need to create your own <tab>. See Section 6.3 for more detail on how to achieve this.

Magento Exam QuestionHow do menu items relate to ACL permissions?

Using the resource="" attribute within the <add> nodes determine the resource each action accesses.

Defined in:

<module_dir>/etc/adminhtml/menu.xml

File will render your menu items hidden from unauthorized users. More info on how to define this above.

Magento Exam QuestionDescribe how to check for permissions in the permissions management tree structures. How would you add a new user with a given set of permissions?

ACL permissions are viewed within the admin path:

Admin > System > Permissions > User Roles

Admin > System > Permissions > All Users

Here contains all the available roles, their permissions and allows us to create new users or modify the current users, as well as assign user roles.

When defining a new user with a custom ACL:

After clicking the Add New Role button, enter values for Role Name and Your Password. Then, click a Role Resources tab and select Resource Access as Custom. You should see any custom ones here.

Magento Exam QuestionHow can you do that programmatically?

Magento Section ChevronWithin an action Controller

To restrict permissions to actions with ACL, override _isAllowed method or ADMIN_RESOURCE constant in the class of this action. The following is available to those actions extending Magento\Backend\App\AbstractAction:

protected function _isAllowed()
{
    return $this->_authorization->isAllowed('MyVendor_MyModule::config');
}

OR, define a constant in your action Controller:

const ADMIN_RESOURCE = 'MyVendor_MyModule::config';

Magento Section ChevronCreating a new User Role

Inject Magento\Authorization\Model\RoleFactory & 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');

/** @var \Magento\Authorization\Model\Rules $rule */
$rule = $this->rulesFactory->create();
$rule->setRoleId($role->getId())
    ->setResourceId($this->rootResource->getId())
    ->setPrivilegies(null)
    ->setPermission('allow');

Magento Section ChevronCreating a new User

Inject 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);

Magento Section ChevronVia CLI

php bin/magento admin:user:create