Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

Updated for Magento 2! No Frills Magento Layout is the only Magento
front end book you'll ever need. Get your copy
today!

Today we’re going to YOLO deep dive into Magento 2’s UI Components and attempt to create one from scratch. At this stage in Magento 2’s lifecycle this isn’t something third party developers can do without taking extra ordinary “not production safe” measures, but sometimes the only way to really understand a system is to inhabit it from the ground up.

Like most of these tutorials, you’ll want to make sure you’re running Magento 2 in developer mode (as opposed to production or default mode). Also, in case any of the following gets too crazy, we’ve put a completed module up on GitHub. Also also, these specifics here have been tested and developed against Magento 2.1.1 — but the concepts should apply across versions.

Baseline Admin Module with Pestle

To start, we’re going to use pestle to create a boilerplate module with a backend menu item. If you’ve not sure what the below commands are doing, you may want to work your way through the Magento 2 for PHP MVC Developers developers series.

In the code above, we’re telling Magento we want to add a pulsestorm_simple UI Component to the content block on our page. With the above in place, if we clear the Magento cache and reload the page, we’ll see the following error

The reason we’re seeing this error is we’ve configured a UI Component named pulsestorm_simple, but Magento couldn’t find a configuration file for it. The error Object DOMDocument should be created comes from PHP code trying to read an XML object that wasn’t created.

Every named UI Component needs a defined ui_component/[...].xml configuration file. So, let’s get that configuration file in place so PHP stops complaining about the DOMDocument. Create the following file.

A UI Component’s configured name attribute will match its XML filename — pulsestorm_simple and pulsestorm_simple.xml in our examples above. All UI Component files are found in a ui_component sub-folder of a specific area’s view folder. Although there’s nothing stopping you from using a UI Component in the frontend area, it’s not 100% clear if this will work as you’d expect, as Magento’s core team have mostly (only?) released UI Components configured on backend layouts.

If we clear the Magento cache and reload with the above in place, we’ll be rewarded with a new error message.

Here Magento’s objecting to our top level node name — pulsestorm_simple. As you’ll recall from the first article in our UI Component series, a UI Component file is a domain specific language (DSL) that controls the instantiation of nested PHP object files. Each node in a ui_component file is linked up with a configuration node in in the following file.

vendor/magento/module-ui/view/base/ui_component/etc/definition.xml

So, the first problem is our pulsestorm_simple node does not exists in definition.xml, and Magento’s UI Component DSL wouldn’t know which PHP class to instantiate when it encountered this node. Now, thanks to Magento’s merged configuration file loading, we can add-to/change the final merged definition.xml by adding the following file (note: this must be in the base folder for this to work)

Whenever you encounter a pulsestorm_simple UI Component node, you should instantiate a Pulsestorm\SimpleUiComponent\Component\Simple object.

You’ll want to be careful naming your nodes here — this file will be merged with Magento’s core definition.xml file, and if you use a name that’s already in use, you may change core system behavior. Including your vendor namespace (pulsestorm_ above) is a good best practice here.

If we clear our cache with the latest file in place, we’ll manage to get a changed error message. (Clearly Sisyphus’ rock was made from XML).

Our problem here? Magento is merging our definition.xml file into an XML document with schema validation. Specifically, Magento demands that the final definition.xml match the structure rules setup in

vendor/magento/module-ui/etc/ui_definition.xsd

Unfortunately, there’s no supported way of adding to these rules in Magento 2. If you know where to look (Magento\Framework\Config\Dom::validateDomDocument) and aren’t above using the object manager’spreference system to inject some custom behavior, it’s possible to have Magento ignore these XSD validation rules. Unfortunately, there’s no way to do this that wouldn’t conflict with another extension trying to do the same thing, so it’s not really an option if you’re trying to redistribute code. Magento’s more stable plugin system isn’t an option, because the validateDomDocument, while public, is a static method, and Magento’s plugin system doesn’t work with static methods.

At this point, we’re out of luck if we want to create a new, top level ui_component node. This is the first sign that the UI Component system is either reserved for Magento’s core developers, was released before it was feature complete, or both.

Skipping Schema Validation

Of course, when we said “out of luck”, we meant “out of luck, unless we want to attack the problem with a possibly unstable class preference” (i.e. YOLO).

Class preferences are the system where Magento developers (core or third party) define concrete classes that the object manager can use to instantiate interfaces. i.e. They link a default class to a PHP interface, and then when a programmer asks the object manager to instantiate that interface, Magento returns an object of the linked class.

Class preferences can also be used to replace concrete class definitions, providing functionality that’s very similar to class rewrites in Magento 1 (with all the same downsides as the rewrite system).

To keep this tutorial going, we’re going to gin up a class preference that will skip XSD validation for XML files. This is not something you’ll want to do in a production system or distributable extension. We’re only doing it now because there’s no other way to proceed.

UPDATE: Hello, from late 2017! When Magento release version 2.2, they broke this tutorial. If you’re using Magento 2.2, in addition to disabling XSD validation, you’ll also need to add a definition.map.xml file with a name="puslestorm_simple" node. Why? We have no idea and Magento haven’t really explained what this file is for. Open source doesn’t always mean open intent. If you’re using Magento 2.2 just copy the file from GitHub to your module and you should take take of any errors about undefined children keys.

UI Component Rendering Class

Before we went down that schema validation hole, we’d just added the following configuration to definition.xml

The etc/definition.xml configuration sets the default attributes and nodes that will be used whenever Magento encounters a particular parent node in a ui_component/[somefile].xml file. In the above example, we’ve configured Pulsestorm\SimpleUiComponent\Component\Simple as pulsestorm_simple‘s default class. This means when we use pulsestorm_simple here

A Magento 2 UI Component class file should extend the base abstract Magento\Ui\Component\AbstractComponent class, and will need to define a getComponentName method. It’s not clear if a component name needs to be the same as the UI Component node name or ui_componont/[filename].xml (pulsestorm_simple above), but it’s best to follow the guidelines set by Magento’s core code here. For similar reasons, we’ve also given our component class a NAME constant.

With the above in place, let’s clear the Magento cache and reload our page to get the next error!

Once again Magento is complaining about a missing XML file. Here’s where we let you in on one of the biggest surprises of the UI Component system — a UI Component object, one that extends Magento\Ui\Component\AbstractComponent, is a system for rendering XHTML templates. XHTML — as in the series of specifications that attempted to replace the non-well-formed standard of HTML4 with an HTML that had XML’s draconian parsing rules.

We’ve told Magento we want it to render a Pulsestorm\SimpleUiComponent\Component\Simple object, we have not told Magento which template a Pulsestorm\SimpleUiComponent\Component\Simple object should use. Let’s change that! Add the following to our definition.xml file

The UI Component system will look for these templates in a module’s view/[area]/ui_component folder. The value from the definition.xml file is transformed into a template path by appending a .xhtml to the file name. Notice that, although these files look like HTML, they have an XML prolog. There are XHTML files, and will need to be well formed XML.

With the above in place, clear your cache and reload the page. You’ll see yet another error, but we promise you we’re almost there.

( ! ) Fatal error: Method Magento\Ui\TemplateEngine\Xhtml\Result::__toString() must not throw an
exception, caught Error: Call to a member function getConfigData() on null
in /path/to/magento/
vendor/magento/module-ui/Component/Wrapper/UiComponent.php on line 0

Here Magento ran into an error while trying to render the XHTML template, and we’ve stumbled into another aspect of the UI Component system. In addition to being a system for rendering XHTML templates, UI Components are also a system that match up a data provider class with a specific XHTML template. The idea is a UI Component is meant to render server side data, and the data provider is the formal method for getting that information to the component.

This means our final (we swear) step for a bare bones UI Component object is configuring a data provider class. This happens in the pulsestorm_simple.xml file since each theoretical component instance renders a specific UI Component. Add the following dataSource node to our pulsestorm_simple.xml file.

DataProvider classes need to extend the base Magento\Ui\DataProvider\AbstractDataProvider class — although this class has zero abstract methods for us to define.

Alright. With the above in place, lets cross our fingers, clear the Magento cache, and reload the page.

Eureka! We’ve rendered an XHTML template!

What’s Happening Behind the Scenes

Before we get into some of the things we can do with this rendered XHTML template, let’s take a second to talk about what’s going on behind the scenes. When Magento’s layout rendering code encounters a UI Component tag, a bunch of code runs that’s equivalent to the following pseudo code

A good DSL usually lets you forget about implementation details like this — but if you’ve never encountered a DSL this sort of thing can seem strange and foreign. Whenever you’re stuck with a bit of UI Component configuration, try to remember you’re preparing values for Magento to convert into PHP code. These are not simple data attributes.

Raw Template Source

Let’s come back to our rendered template.

That’s how it looks in the browser — but what’s the actual rendered source look like. If we take a look (using the browser’s View Source menu and not the rendered DOM of a browser debugger), we’ll see the following (formatted for easier viewing below)

Not only has Magento rendered our <div> and <h1> tags from the XHTML template — they’ve also rendered a text/x-magento-init script. This is the final aspect of the UI Component system we’ll cover today. Not only does the UI Component render an XHTML template, not only does it bind that template to a data provider object: The UI Component system also renders a JSON object, and uses that JSON object to initialize an instance of the Magento_Ui/js/core/app RequireJS app/module via an x-magento-init script.

Now that we know the scope of a UI Component, let’s take a look at some features of this template/rendering engine.

XHTML Template Tags

Similar to phtml templates — you can “call through” to the underlying UI Component class in an XHTML template. There’s a special {{...}} template directive syntax you can use. For example, is we add the getEvenMoreData method to our component class

Super annoying, and another sign that the UI Component system isn’t fully baked.

Understanding UI Component Inheritance

When you place a top level node in definition.xml, you’re creating a reusable UI Component tag. This lets a UI Component programmer use your tag in a UI Component XML file loaded in through a <uiComponent/> layout tag.

The definition.xml file also lets you set defaults for your UI Component, but an end user programmer can override them.

If a theoretical UI Component programmer wanted a pulsestorm_simple UI Component, but wanted to change the template, all they’d need to do is create the same structure in their XML file. For example, if we wanted a new template

While this feature isn’t often used for templates — it is used for other UI Component configuration parameters, and is fundamental to using the system. While you won’t be adding information to definition.xml in the real world, you will be referencing definition.xml when you need to, say, debug a grid listing’s rendering parameters.

Adding Data

The last thing we’ll want to talk about today is a UI Component’s data. A UI Component’s data would be something like the rows of information for a grid listing, or the default values of a form. Behind the scenes, the UI Component system can render this backend data for you in the frontend as a javascript array/object.

All we need to do is define a getDataSourceData method on our component class.

Depending on how fried your brain is, this may be a little confusing, or a lot confusing, to you. If the data comes from the getDataSourceData method on our component class — why’d we need to configure a Pulsestorm\SimpleUiComponent\Model\DataProvider class?

Unfortunately, I don’t have a great answer for you there. Based on core code, it looks like the “correct” usage pattern is to have your component class fetch the data provider and call its getData method.

While the UI Component system, at first glance, seems like a fully object oriented domain specific language for building user interface components, and may indeed have started out as that, looking deeper it seems like a system that was nowhere near complete as Magento 2’s launch date closed in and is filled with the sort of edge cases, bugs, and weird omissions that a poorly managed or directionless dev cycle brings to mind.

Next Steps

That, in a nutshell, is the PHP portion of the UI Component system. At the end of the day, all this complexity boils down to rendering an xhtml template and tying it to a data source.

In our next UI Component article, we’re going to dive a little bit deeper, and look at the ways Magento’s javascript systems (both RequireJS, and knockout.js) interact with this system. It’s here that the bulk of the work rendering Magento’s grid listings and backend forms happens, and understanding these systems will be vital for customizing Magento’s backend UI.