Magento 2 Tutorials

Browsing configuration sections with the Magento 2 Backend Launcher

configuration manager magento 2 with faded code snippet on top - dull comes to mind but it gets the job done

Comparing with the original PulseStormLauncher, the Magento 2 Backend Launcher is still missing the ability to quickly jump through configuration sections. Today, we will check that off the to-do list.

Introduction

Previously, we <a” href=”/magento-2-backend-launcher-composer-compatibility-code-update” target=”_blank”>updated the Launcher to get it to work with the latest alpha build at that time. We also added Composer compatibility. After a composer update on our local Magento 2 installation, the latest Magento 2 build doesn’t seem to introduce any code breaking changes.

That means we do not have to make any compatibility changes and can work straight on adding this new feature: being able to quickly jump to configuration sections.

The configuration structure

To get this functionality to work, we have to discover how we can retrieve the configuration sections their labels and URLs from Magento so we can feed them to the launcher its interface.

Without existing documentation on the topic, the easiest way to find out how to access the configuration structure, is to look at examples of how Magento 2 does it itself. We start at the highest level we want to be able to jump to: the configuration tabs. To find out how to retrieve them, we open the relevant block: Magento\Backend\Block\System\Config\Tabs.

In the block its constructor Magento\Backend\Model\Config\Structure is injected which supplies ($configStructure->getTabs()) an iterator for the tabs. The template file making use of this iterator shows that the iterator iterates over Magento\Backend\Model\Config\Structure\Element\Tab objects. Each tab element might have child elements which are instances of Section. These section elements host the display label and id to build the URL.

The Section elements might have children themselves too. Those are the sections which are displayed on the right of the configuration manager. We want them available in the launcher, too.

Magento 2 configuration structure

The configuration manager and the elements we are interested in.

With this information, the boilerplate psuedocode reads like this:

  1. Get an instance of the Structure model by making use of dependency injection
  2. Get the tabs iterator and iterate over the tabs
  3. For each tab, get its sections and add them to the launcher
  4. For each section, get its child sections and add them to the launcher

Dependency injecting a helper

Currently, the LauncherItems block has all kinds of logic which doesn’t really belong in a block class. We will therefore clean the block class up a bit by moving all not necessarily related methods to an independent helper class.

Just like Magento 1.X, Magento 2 helper classes find their way in the Helper directory at the root of the module directory. The Data file is created there and it extends Magento\Framework\App\Helper\AbstractHelper.

<!--?php namespace Magenticians\Launcher\Helper; class Data extends \Magento\Framework\App\Helper\AbstractHelper { protected $_iteratorFactory; protected $_blockMenu; protected $_url; // moved from LauncherItems block public function __construct( \Magento\Framework\App\Helper\Context $context, \Magento\Backend\Model\Menu\Filter\IteratorFactory $iteratorFactory, \Magento\Backend\Block\Menu $blockMenu \Magento\Backend\Model\UrlInterface $url, ) { parent::__construct($context); $this-&gt;_iteratorFactory = $iteratorFactory;&lt;br ?--> $this-&gt;_blockMenu = $blockMenu;
$this-&gt;_url = $url;
}

// moved from LauncherItems block
public function getMenuModel() { /* ... */ }

// moved from LauncherItems block
protected function getMenuIterator($menu) { /*...*/ }

// moved from LauncherItems block
public function getMenuArray($menu, $itemsSeparator = ' ', &amp; $result = array(), $fullName = '') { /*...*/ }
}

Different from Magento 1.X is that in Magento 2 the existence of helper classes are no longer defined in the module its XML configuration. Instead, helpers are either dependency injected or retrieved by making use of the object manager. We will dependency inject it in the LauncherItems its constructor:

public function __construct(
\Magento\Backend\Block\Template\Context $context,
\Magenticians\Launcher\Helper\Data $dataHelper,
\Magento\Backend\Model\UrlInterface $url,
array $data = array()
) {
parent::__construct($context, $data);
$this-&gt;_dataHelper = $dataHelper;
$this-&gt;_url = $url;
}

Maybe needless to say, but for all methods which are moved, its calls should now read $this->_dataHelper->methodName().

Getting configuration sections

With the psuedocode above in mind, writing a method which can retrieve the configuration sections is not that hard. We start with creating a new method in the just created helper class. The parameters speak for themselves:

public function getConfigSectionsArray($itemsSeparator = ' ', $itemPrefix = '') {}

Because we need the Structure model, we will inject it in the helper its constructor:

public function __construct(
\Magento\Framework\App\Helper\Context $context,
\Magento\Backend\Model\Menu\Filter\IteratorFactory $iteratorFactory,
\Magento\Backend\Block\Menu $blockMenu,
\Magento\Backend\Model\UrlInterface $url,
\Magento\Backend\Model\Config\Structure $configStructure // &lt;&lt; here we inject it ) { parent::__construct($context); $this-&gt;_iteratorFactory = $iteratorFactory;
$this-&gt;_blockMenu = $blockMenu;
$this-&gt;_url = $url;
$this-&gt;_configStructure = $configStructure;
}

The tabs itself don’t have any value as launcher entries so we will directly iterate over their children:

public function getConfigSectionsArray($itemsSeparator = ' ', $itemPrefix = '')
{
$sections = array();

foreach ($this-&gt;_configStructure-&gt;getTabs() as $tab) {
/** @var $tab \Magento\Backend\Model\Config\Structure\Element\Tab */

foreach ($tab-&gt;getChildren() as $section) {
/** @var $section \Magento\Backend\Model\Config\Structure\Element\Section */

To build the labels of the sections, we will combine the prefix, the name of the parent tab and the section its label. The URL can be retrieved from the Url model:

$sectionLabel = $itemPrefix . $tab-&gt;getLabel() . $itemsSeparator . $section-&gt;getLabel();
$sectionUrl = $this-&gt;_url-&gt;getUrl('adminhtml/system_config/edit', array('section' =&gt; $section-&gt;getId()));

// First add global section to the launcher items...
$sections[] = ['label' =&gt; $sectionLabel, 'value' =&gt; $sectionUrl];

The only difference for the sub-sections is the way how URLs are generated. Instead of using the Url model again, the hash location is appended to the URL of the parent section. Upon seeing the hash location in the URL, the Magento 2 backend JavaScript will automatically open the affected accordion. The label simply gets appended to the parent label:

/* [...] */

// First add global section to the launcher items...
$sections[] = ['label' =&gt; $sectionLabel, 'value' =&gt; $sectionUrl];

foreach ($section-&gt;getChildren() as $subSection) {
/** @var $subSection \Magento\Backend\Model\Config\Structure\Element\Section */

// ...then add all sub sections
$sections[] = [
'label' =&gt; $sectionLabel . $itemsSeparator . $subSection-&gt;getLabel(),
'value' =&gt; $sectionUrl . '#' . $section-&gt;getId() . '_' . $subSection-&gt;getId() . '-link'
];
}
}
}

return $sections;
}

Using the result

In the LauncherItems block the specifically named getMenuJson method is renamed to a broader getItemsJson and a call to the getConfigSectionsArray method is added:

const ITEMS_SEPARATOR = ' - ';
const CONFIG_ITEMS_PREFIX = 'Configuration - ';

public function getItemsJson()
{
$menuArray = $this-&gt;_dataHelper-&gt;getMenuArray($this-&gt;_dataHelper-&gt;getMenuModel(), self::ITEMS_SEPARATOR);
$configSectionsArray = $this-&gt;_dataHelper-&gt;getConfigSectionsArray(self::ITEMS_SEPARATOR, self::CONFIG_ITEMS_PREFIX);

return json_encode(array_merge($menuArray, $configSectionsArray), JSON_UNESCAPED_SLASHES);
}

The launcher.phtml now has an updated call to getItemsJson. No client side code changes are required; even though the list of launcher items got a little bit bigger, the JavaScript will still work.

Final words

Because the configuration structure has a sane, dedicated model, not a lot of code is required to retrieve the configuration structure. Due to the missing documentation on the internals, most of the time is spent on actually finding that model and how to interact with it.

Now the Launcher module can jump through configuration sections, it’s time to add one ourselves. Booting the Launcher by clicking the “+” sign in the admin tray is a tedious task and a configurable, dedicated keyboard shortcut can solve this. Until next time.

Subscribe Newsletter

Subscribe to get latest Magento news

40% Off for 4 Months on Magento Hosting + 30 Free Migration