THE TOP 10 MAGENTO 2 CONCEPTS YOU NEED TO KNOW
Are you a Magento developer who has yet to make the dive into Magento 2? Have no fear, because by the end of this article, you’re sure to be a bonafide Magento 2 expert!
Well … perhaps not. Learning Magento is a hefty task, after all, and this remains true for version 2 of the platform. A few hundred words isn’t really going to get you up and running, obviously. However, if you’re like me, not knowing where to start may be the single most intimidating thing about diving in. So the real objective of this article is to give you an overview of the most significant differences from Magento 1 that you’ll encounter.
As you get your feet wet in the new platform, you may be pleasantly surprised to discover how similar the line-by-line code you’ll write really is. So a little insight into a few major architectural concepts will go a long way toward smoothing your transition to Magento 2 guru.
And before we really get started, if you don’t know already, Magento 2 has a growing collection of documentation topics for getting into the finer details.
MODULE STRUCTURE
If you’re not yet familiar with Composer, the resoundingly popular PHP dependency manager, now is the time to become so, since the software is integral to the structure of Magento 2. Instead of a single core codebase, Magento is now a collection of individual components installed via Composer. This applies to third-party libraries and Magento core modules/themes (i.e., packages) alike. Package dependencies have been formalized such that any particular “version” of Magento can really be expressed in terms of the versions of these components and their dependencies.
If you’re a developer of distributable Magento extensions, you’ll be creating Composer packages as well. On a broader note, though, the kind of encapsulation required for such self-contained components is facilitated by a change in Magento’s structure that is sure to make life easier whether your code is a marketplace extension or just a local customization: All files related to a module now reside in a single directory. Whereas in Magento 1 PHP files, layout XML, templates and static assets were scattered throughout different locations, now all files for a single feature live together. A module’s top-level directory contains the familiar Block, Model, etc. sub-directories (or really any sub-directories in which you wish to place your classes), along with a view sub-directory. Within view/adminhtml or view/frontend are layout and template directories, and a web directory that contains the module’s static assets like JavaScript and CSS.
THE PUB DIRECTORY AND CODE GENERATION
The concept of static asset files living in numerous locations, and the questions this raises, are the perfect segue into the next important topic. Automated code generation and deployment play a huge role in Magento 2, and these can be partially understood with a look at the pub directory. While it’s possible to use your Magento installation’s root directory as your web root in development environments, the required web root in production is the new pub directory that lives apart from all application code and contains only what Magento puts there (including the main application entry point, media, and static view files). Since site code now lives outside this designated web root, access control is more rigid and predictable, and the new deployment/compilation process makes the previously mentioned module encapsulation possible:
- Static view file deployment materializes all necessary static assets from their source in application code into the web-accessible pub directory. This deployment process happens automatically in development mode but requires an explicit action in production. This automatic deployment of assets is what allows all JS/CSS code to live in its appropriate module but still be web-accessible in a centralized location.
- Code generation doesn’t involve the pub directory, but it’s closely related to the concept of deployment. Certain types of PHP classes are now automatically generated by Magento and facilitate more granular and unobtrusive customization than has been possible in the past.
DEPENDENCY INJECTION
Dependency injection in Magento is a huge topic, and so I’ll cover it only in the broadest terms here. Magento 1 had its factory methods that facilitated class overrides, like Mage::getModel and Mage::helper. This is replaced in Magento 2 by a more structured approach whereby all external types are injected as parameters in a class’s constructor as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
Example.php
class Example
{
protected $request;
protected $customerAccountManagement;
public function __construct(
MagentoFrameworkAppRequestHttp $request,
MagentoCustomerApiAccountManagementInterface $customerAccountManagement
) {
$this->request = $request;
$this->customerAccountManagement = $customerAccountManagement;
}
}
|
Types referenced this way are automatically instantiated and passed in by Magento’s Object Manager. This facilitates the use of real class names and PHP namespaces instead of littering code with proprietary factory methods, it makes for more organized code by centralizing references to dependencies, and it allows a more flexible approach to overriding classes. Notice in the code above that one dependency references only an interface, not a full class. Interfaces are a big point of emphasis in Magento 2, and dependency preferences defined in a special config file – di.xml – specify the best default implementation of such an interface. This DI configuration is what third-party modules use to change the preference for a default interface or full class, either globally (the equivalent of Magento 1’s class rewrites) or for specific contexts (made possible by the centralization in constructors). External classes obtained via constructor injection are shared instances (i.e., a “singleton” pattern) and are called “injectables.”
PLUG-INS
As previously mentioned, Magento 1 class rewrites find their analog in the ability to specify dependency injection preferences. I also mentioned that more unobtrusive techniques for customization are made possible thanks to M2’s code generation, and plug-ins are the most significant example of this. Plug-ins allow you to alter or extend targeted methods instead of needing to replace entire classes.
Like preferences, plug-ins are defined in di.xml files, and they have specific “before,” “after” and “around” implementations, respectively allowing you to alter the input of an original method, operate further on its output, or surround it with your own logic. Best of all, multiple plug-ins can be defined on a single method, vastly improving compatibility between extensions that operate on the same area of the system. M2’s code generation and the Object Manager take care of running method calls through the right paces and handing them off to plug-ins as necessary. Below is a native example of the definition and implementation of a plug-in:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
di.xml
<type name=“MagentoStoreModelResourceModelGroup”>
<plugin name=“categoryStoreGroupAroundSave” type=“MagentoCatalogModelIndexerCategoryProductPluginStoreGroup”>
</type>
—–
StoreGroup.php
class StoreGroup
{
public function aroundSave(
MagentoFrameworkModelResourceModelDbAbstractDb $subject,
Closure $proceed,
MagentoFrameworkModelAbstractModel $group
) {
$needInvalidating = $this->validate($group);
$objectResource = $proceed($group);
if ($needInvalidating) {
$this->indexerRegistry->get(MagentoCatalogModelIndexerCategoryProduct::INDEXER_ID)->invalidate();
}
return $objectResource;
}
}
|
FACTORIES
On the subject of dependency injection, you may have noticed a significant ommission. “Injectables,” as shared instance objects, are really the equivalent of M1’s Mage::getSingleton factory technique. So where is the equivalent of the Mage::getModel factory method for obtaining unique object instances (that is, “non-injectables” in M2 parlance)? Such objects are instantiated using injectable classes that are dedicated to the purpose: factories. Observe the following use of a factory class to instantiate a model:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
Example.php
class Example
{
protected $customerFactory;
public function __construct(
MagentoCustomerModelCustomerFactory $customerFactory
)
{
$this->customerFactory = $customerFactory;
}
public function getCustomer($email)
{
return $this->customerFactory->create()->loadByEmail($email);
}
}
|
Your first reaction to the above might be to think that defining and maintaining one class just to handle instantiating another seems cumbersome. In this respect, the benefits of M2’s code generation come to the rescue again, because factory classes actually don’t need to be defined at all. They are automatically created by Magento whenever a reference to such a factory is encountered. The class-specific approach means that you canexplicitly define them, however, thereby handily customizing the factory technique for any particular class if necessary.
THE SERVICE LAYER
Like dependency injection, the service layer is worth a whole series of articles on its own (see the core documentation on service contracts anddesign patterns), but the basic concept can be grasped with a little explanation. Consider that one of the major challenges for customization in Magento 1 was that code almost certainly relied on referencing specific implementation details of core Magento classes, and that major changes to this core code (such as alterations of method parameters or logic) could render custom code inoperable. The M2 service layer alleviates this problem by introducing a layer in between the main business logic of a module and any external code referencing that module’s components. “Service contracts” take the form of interfaces that rigidly define entities and the prescribed ways for external code to interact with them; these interfaces and the classes that implement them form the service layer.
As an example, let’s consider the core Customer entity. MagentoCustomerModelCustomer is the model for a customer entity, and this class has corresponding resource and collection models that serve the same purpose as their M1 counterparts. Rather than reference these classes directly, however, external modules should utilize a sort of traffic controller class called MagentoCustomerModelResourceModelCustomerRepository to obtain and manipulate instances of MagentoCustomerModelDataCustomer. Both classes implement service contract interfaces, and while they use the typical data and collection models under the hood, the methods exposed in the interfaces make this implementation invisible and explicitly define the details external code can rely on. The logic of models and collections may change to any degree in the future, but the details of the service contracts will remain the same, preserving the compatibility of custom modules.
Service contract interfaces are located in the Api sub-directory of a module. If you’re producing distributable code, you are strongly advised to create service contracts and classes for your own entities to maintain future compatibility with others’ code.
PAGE CACHE
In Magento 1, the Full Page Cache was a performance enhancing feature available exclusively in Enterprise, and accommodating it in your custom developed features was not exactly the most trivial development topic. In M2, full page caching is a feature of both Community and Enterprise, living in the module Magento_PageCache, and its implications on developers are far more streamlined.
The central challenge of full page caching is those areas of a web page whose content cannot be universal for all visitors and page views. M1 focused on assembling content for such areas as part of the rendering of final HTML output, while M2 takes a different – and increasingly popular – approach. Firstly, page blocks that should be de-coupled from the main cache content are more solidly defined as “private data”: content that is dependent on a visitor’s session information. Secondly, such blocks are anonymized for all visitors in the initial rendering of the page, with the private data instead loaded via an Ajax request after the main page content is delivered. Thus the main body of a page is cached and delivered as swiftly as possible, with less essential and visitor-specific content materializing asynchronously.
In terms of the implications for developers, accommodating the page cache is almost insanely simple as compared with M1. Marking a block as private data is as simple as setting a single property, and Magento takes care of the rest:
1
2
3
4
5
6
7
8
9
10
|
ExampleBlock.php
class ExampleBlock
{
public function __construct()
{
parent::__construct();
$this->_isScopePrivate = true;
}
}
|
CSS COMPILATION
Since the responsive overhaul to its theme, CSS compilation in the form of SASS/Compass has been a part of Magento 1. The tool of choice in Magento 2 is LESS, which brings much the same capability, including CSS variables, mix-ins for repeatable styles, multi-file organization with includes, and much more.
Covering the specifics of LESS in M2 is outside the scope of this article, but an important concept to grasp is how the M2 developer deals with CSS compilation (or, to be more accurate, does not deal with it). In M1’s responsive theme, compilation was a process completely invisible to the Magento application; the developer was responsible for compiling source code into final CSS files that were then explicitly included via layout as has always been done. In M2, the CSS compilation magic is done at the application level as part of the aforementioned static file deployment process. Simply make sure LESS source files are included in the appropriate locations, and Magento will take care of the rest. (It’s worth noting that it’s still advantageous to handle compilation differently during development, but the point is that the process is heavily integrated into the application core.)
Any underscore-prefixed source files placed in the view/{area}/web/css/source directory of a module or the web/css/source directory of a theme will be compiled into the main styles, with only a single instance of a unique name being loaded site-wide, depending upon a priority order (thus allowing for override of specific partials). On top of this, the magically named _module.less and _widgets.less (for modules) and _extend.less (for themes) allow styles to be cumulative instead of directly overriding the identically named partials from other sources.
JAVASCRIPT INCLUSION AND REQUIREJS
Magento 2 has overhauled its technique for including JavaScript from a traditional approach of explicitly requiring each JS file with tags to an “include-as-needed” method primarily utilizing RequireJS. (See this page for info on RequireJS in Magento.) RequireJS is a robust tool with its own documentation, but the important thing to understand is that it allows for encapsulating JavaScript within enclosures that declare dependencies, which RequireJS then manages the loading of. This ensures that JavaScript components get loaded only when needed, and in the necessary order.
The process could almost be considered a JS version of dependency injection, and there are several ways to do it in Magento. Here’s a simple example, which requires the component “mage/url” as a dependency of the code within the enclosure, passing in the class returned by that file as the parameter of the function:
1
2
3
4
5
6
7
8
9
|
example.phtml
require([
‘mage/url’
], function(url) {
return url.setBaseUrl(‘http://www.mydifferentdomain.com’);
})
|
The other two ways introduced by Magento deal with immediately scoping calls for jQuery plug-ins to specific selectors and can be studied on the Magento documentation page. The precise definition for the location of named components is set up in a per-module file called requirejs-config.js, and like dependency injection, this configuration allows for easy extension and override of components by superseding their definition with your own.
JAVASCRIPT COMPONENTS
A JavaScript-heavy developer will be well advised to become familiar not only with the techniques for including JS code, but with some of the powerfully systematized approaches Magento takes to its own JavaScript as well. Firstly, it’s worth noting that M2 makes heavy use of jQuery widgets, both from the jQuery UI library and its own customized widgets. When you’re developing JS-heavy interfaces, it will be well worth the effort to utilize this pattern in your own code as well.
A more complex topic is the JS class uiComponent (distinct from M2’s UI Components library) and the way layout, JS and static templates can work together. In this paradigm, JS components that extend uiComponent can be initialized, and configuration passed to them, via layout XML. Among this configuration can be the definition of a static template path used to render the component, which is loaded via Ajax without the involvement of PHP, and which contains Knockout syntax to dynamically access the properties and methods exposed on the JS component. Getting a handle on this particular pattern will take some study of the Magento core usage and the capabilities of Knockout, but here is the barest snippet of an example:
1
2
3
4
5
6
7
8
|
default.xml
<item name=“minicart_content” xsi:type=“array”>
<item name=“component” xsi:type=“string”>Magento_Checkout/js/view/minicart</item>
<item name=“config” xsi:type=“array”>
<item name=“template” xsi:type=“string”>Magento_Checkout/minicart/content</item>
</item>
</item>
|
How the above fits into the layout structure and how its parent block initializes such components is beyond what I can cover in such limited space. However, know that the above defines the path to the JS file that represents this component ( minicart.js, containing a class that extends uiComponent) and a static template file loaded straight from the server ( content.html), which contains Knockout code referencing logic fromminicart.js.
There are a plethora of other smaller architectural changes in Magento 2 that could be mentioned, and you’ll become well familiar with them as your small ventures into the new platform become larger ones. For the developer looking for a place to start, however, the above topics represent some of the best ways you can spend your Magento 2 training time. Start to get a handle on these concepts, and you’re sure to start seeing M2 as a warm friend rather than an ominous stranger.