Magento 2 Tutorials

Magento 2 Backend Launcher: Composer compatibility & code update

magento 2 install module composer

Today we will make minor changes to the previously developed backend launcher to get it working with the latest Magento 2 build. Additionally, we will add Composer compatibility.

Introduction

When the development of the Magento 2 Launcher module started, Magento 2 was at 2.0.0.0-dev84. Before you know it, they change the versioning system, push a few updates and at 0.1.0-alpha99 the Launcher module is the cause of exceptions being thrown in the Magento 2 backend.

On top of that, Magento 2 its Composer compatibility has changed quite a bit. In fact, it is thusly developed that without any problem we can let Composer install a module to the system. Let’s get the module to work again and take a look at the required changes for it to be compatible with Composer.

This article can be read standalone from the initial development write-up. If you want some background information on what exactly the “Backend Launcher” is, we recommend reading at least the introduction. If you rather skip the update process to the latest Magento 2 build and want to learn more about the Composer compatibility, here’s a shortcut

What’s wrong?

Throwing the finished product from the previous episode into a fresh Magento 2 installation and navigating to the backend will lead to the following exception being thrown:

Fatal error: Call to a member function setActive() on a non-object in \magento2\htdocs\app\code\Magento\Backend\App\AbstractAction.php on line 155

It is possible to manually back-step through the application run and find out what’s the cause, but it is a tedious process. A faster way might be to inspect the code base and see if we can spot anything which changed in the newer builds of Magento 2.

New layout definition

Because the code base consists of one single block and some assets being added, a logical first step is to check out whether adding that block and assets is causing problems in the newest build. Opening the view/adminhtml/layout/default.xml will quickly reveal (if you are using a sane IDE) that the schema definition (XSD file) referenced to does not exist.

xsd file missing magento 2

PHPStorm will let you know when something is wrong with the schema definition.

Newer Magento 2 builds either renamed, replaced or removed the Magento/Core/etc/layout_single.xsd file referenced to, because they probably changed the XML structure in one way or another. To find out what’s new, we take a look at a module which has a similar objective – adding an interface element to the header – as the Launcher module. The module in question is Magent_AdminNotification

Upon opening its default.xml file, the difference is quickly spotted. The root tag is no longer layout but rather page. The root tag and schema definition in the Launcher its default.xml layout definition is replaced with the correct one:

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">

Reloading the backend still refuses to show the Magento 2 dashboard. No direct exception, but a friendly message that something is wrong and a new error log has been generated. The error log its top-level exception is surprisingly understandable:

Invalid block type: Magento\Theme\Block\Html\Head\Script

New way to add assets

This exception is clear as crystal: there is an attempt to add a block which does not exist (anymore). In 0.1.0-alpha97 it got removed. Asset management is now done within the “page configuration API”.

At the moment of writing the documentation on page assets is outdated and we didn’t manage to find any other documentation on said API. No need to worry, we can simply dive into an existing Magento 2 module to find out how it’s done.

First we remove the entire node so that the backend is at least working. Then, we check the rendered HTML and pick a JS and/or CSS file to search for. We went with Magento_Rule its rules.js and searched with the latter as needle through string constants. It was found in the default.xml of Magento_Backend its adminhtml area:

<head>
        [...]
    <link src="extjs/ext-tree-checkbox.js"/>
    <css src="extjs/resources/css/ext-all.css"/>
    <css src="extjs/resources/css/ytheme-magento.css"/>
    <link src="Magento_Rule::rules.js"/>
</head>

A bit of a coupling-issue there, but at least now we have an idea of what the new syntax is. We update our module its default.xml accordingly:

<?xml version="1.0"?>
<page [...]>
    <head>
        <link src="Magenticians_Launcher::js/launcher.js"/>
        <css src="Magenticians_Launcher::css/launcher.css"/>
    </head>
[...]
</page>

Quick note on the new syntax

As seen in the example above, the link tag is used to refer to a JavaScript asset and the css tag for CSS assets. First, the naming picked is very confusing: in HTML, CSS files are included with a link tag. Secondly, using either link or css for either JavaScript or CSS seems to work – Magento 2 will automatically generate the correct HTML tag. The XML logic (or lack of?) is definitely something to look deeper into.

Edit: after a quick look (checked at alpha102) at the internals, link, script and css are all processed with the same code. Deeper down is handled which HTML to output depending on the file type supplied.

A minor rename

The backend is still working and our module its assets are properly included. The familiar plus-sign of our module is showing in the admin panel tray and everything seems to be fine. Until we actually want the use the module.

The debug console complains that the launcher_items variable is not defined. This means that the LauncherItems block has not been included in the rendered page. Odd, because everything seems to be fine; at least we didn’t get any application-breaking exceptions. Upon investigating the system.log, the following message is retrieved:

Broken reference: missing declaration of the element 'before_body_end'.

One of the joys of working with an undocumented code base! Checking the page.phtml of the Magento backend learns us that before_body_end container is still being used:

<?php echo $this->getChildHtml('before_body_end') ?>

Further inspection reveals that in all page layouts of the Magento 2 backend before_body_end is redefined as before.body.end. After checking who’s to blame, it’s probably safe to say that since 0.1.0-alpha97 all dashes in the container names are replaced with dots. Not a clue “why” but it’s nice there’s consistency.

undefined is not a function - magento 2

There’s still something wrong with the JavaScript

The container name is set straight in the default.xml layout definition and now Magento knows where the LauncherItems block should be placed.

Magento 2 uses RequireJS

After this change, there is still a JavaScript runtime error coming through in the debug console. The affected line initializes the jQuery UI Autocomplete library on the Launcher its input field. In 2.0.0.0-dev80 Magento 2 started suporting RequireJS. During initial development on 2.0.0.0-dev84, implementation was still at the beginning.

With the latest build however, RequireJS is responsible for loading jQuery UI. This means we cannot call .autocomplete nor .dialog before we are certain that the UI library has been fully loaded. This is quickly anticipated upon by wrapping the jQuery document.ready callback in a require() callback signaling a dependency on the jQuery UI library:

jQuery(document).ready(function($) {
    require(['jquery/ui'], function(ui) {
        [...]
        
        $('#magenticians-launcher-input').autocomplete({
            source: launcher_items,
        
            [...]
        });
    });
});

There we go, our module is up and running again!

Composer compatibility

Adding Composer compatibility to a Magento 2 module is really straight forward. Composer works with a variety of package sources. Because Magento 2 is still in development, we chose to use Github as our VCS repository. Later, you can always cross-publish your packages to a repository like Packagist.

Given the module is already in a Git repository, you simply create a JSON file as if it is any other project:

{
    "name": "magenticians/launcher",
    "version": "0.0.1",
    "type": "magento2-module",
    "description": "Magento 2 module which adds a launcher to the Magento 2 backend interface. Inspired by PulseStormLauncher."
}

Add author, website information et cetera as you find necessary. Now, the require section is still up for debate. Should you include a reference to magento/framework and include a reference to the packages.magento.com repository? Or refer to magento/community-edition which is on Packagist? We decided for now to leave out all requirements until the whole what-is-hosted-where issue is cleared up. It is not that often you will do a composer install from a module its directory and expect Magento 2 to be wrapped around it.

The only extra key which you have to add – and is not something regular Composer packages have – is the map key for letting the Magento 2 Composer installer know where which files go. To have separation of code and other project files, we have put the module its code base in a separate src directory. Our map looks like this:

    [...]

    "extra": {
        "map": [
            [
                "src/Magenticians/Launcher",
                "Magenticians/Launcher"
            ]
        ]
    }
}

With this mapping all files in src/Magenticians/Launcher will be deployed to app/code/Magenticians/Launcher. Done! To get the module to work within a Magento 2 installation, you only have to let Composer know where your repository is located (if it’s not cross-published on Packagist):

$ composer config repositories.magenticians_launcher vcs http://github.com/magenticians/launcher

And that it is a requirement for your current project:

$ composer require magenticians/launcher:dev_master

Final words

That’s about it! Hopefully it gives a good indication of how to stay on track with the Magento 2 code base as an outside developer. Though documentation is still sparse and more than often non-existent, with enough patience developing for the Magento 2 platform is doable, today.

It’s also good to see that Magento 2 its Composer support is coming along. Though some things can still be refined, we are glad that a major thing like installing a module works without hiccups.

As always, if we missed anything, made a mistake or if you have a question – please leave a comment below, tweet, mail or send a smoke signal. Thanks for reading and see you next time.

Subscribe Newsletter

Subscribe to get latest Magento news

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