Categories


Archives


Recent Posts


Categories


Magento 2 Object Manager

astorm

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.

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

Magento 2 brings a slew of changes to the table for an experienced Magento 1 programmer. While you can still see bits and pieces of Magento 1 (EAV, areas, blocks, etc.), the new Magento 2 core team has spent the last few years swapping out the startup fueled internals of Magento 1 with a more mature, “enterprise-java” style system. More classes, more objects, and more design patterns.

Despite what some might consider an increase in code complexity, the Magento 2 core team has also spent a lot of time simplifying and clarifying ideas in Magento 1. Magento 1’s home grown class rewrite system — based around blocks, models, and helpers — has been replaced with a Kiev grown dependency injection container/object-manager system.

If your approach to Magento 1 development has been copy-paste, cookbook style development, then Magento 2 will be, for all intents and purposes, a new framework. Your old incantations will no longer work.

If, however, your approach to Magento 1 development has been an understanding of the design pattern implementations, and cultivating an ability to read code, then coming up to speed on Magento 2 will be a slight bump on the road — and in many ways a bump that should make development a more stable, predictable affair.

This article is the first in a series explaining Magento 2’s object manager/dependency-injection system. We’ll explore how programmers (you!) create objects in Magento 2, explore the additional features Magento 2’s object system brings to the table, and along the way we’ll discuss what’s changed from Magento 1 and start to explore the conventions of Magento 2.

Magento 2 Command Line Framework

One of the big changes that Magento 2’s architecture shift brings to the table is a command line framework. This is not the simple framework from Magento 1 (discussed in my short book, No Frills Command Line Magento). Instead, Magento 2 ships with a full implementation of Symfony’s Console component.

After installing Magento 2, you can see a list of the default commands that ship with the system by opening a terminal application and typing the following command.

$ php bin/magento 

You should see output that looks something like this.

Magento CLI version 0.74.0-beta16

Usage:
 command [options] [arguments]

Options:
 --help (-h)           Display this help message
 --quiet (-q)          Do not output any message
 --verbose (-v|vv|vvv) Increase the verbosity of messages: 1 for normal
                       output, 2 for more verbose output and 3 for debug
 --version (-V)        Display this application version
 --ansi                Force ANSI output
 --no-ansi             Disable ANSI output
 --no-interaction (-n) Do not ask any interactive question

Available commands:
 help                                      Displays help for a command
 list                                      Lists commands
 //... full command list snipped ...

In addition to shipping with a number of useful commands, third party developers can create Magento modules that add new commands to the system.

We’re going to use the command line framework to run our code samples in this tutorial. The command line is a nice clean place to run sample code, observe output, and not need to worry about the layers and layers of abstraction between a browser page load and your Magento code running.

Installing the Tutorial Sample Code

We’ve prepared a Magento 2 module with a simple command line interface (cli) command implemented for you. The hows and whys of Magento 2 module installation are still settling into place, so for now our best bet is to install this module manually.

We’ve placed the module source on GitHub. This module will add a command named ps:tutorial-object-manager-1, which we’ll use below. To install this module

  1. Navigate to the GitHub releases page

  2. Find the latest release (0.0.3 at the time this tutorial was written), and click on the zip or tar.gz link to download the release archive

  3. Extract the archive source into your Magento 2 installation, matching the app folder structure

You should now be able to run the following ls command from the root of your Magento installation and see the top level files of the Pulsestorm_TutorialObjectManager1 module.

$ ls -l app/code/Pulsestorm/TutorialObjectManager1/
total 0
drwxr-xr-x@ 4 alanstorm  staff  136 Jul  9 08:49 Command
drwxr-xr-x@ 3 alanstorm  staff  102 Jul  9 08:49 Model
drwxr-xr-x@ 4 alanstorm  staff  136 Jul  9 08:49 etc

You can also check that the module is installed correctly by running the module:status command

$ php bin/magento module:status
List of enabled modules:
Magento_Store
//...

List of disabled modules:
Pulsestorm_TutorialObjectManager1    

If you’ve properly extracted the module to the app/code folder, you should see output similar to the above when you run module:status. Sharp eyed readers will notice a problem though — while Pulsestorm_TutorialObjectManager1 is present, it’s listed as a disabled module. We need to tell Magento 2 that this module is enabled.

To do this, we need to edit the app/etc/config.php file. Open this file in your favorite text editor and look for the following section

#File: app/etc/config.php
<?php
return array (
  'modules' => 
  array (
    'Magento_Store' => 1,
    //... full module list snipped ...
    'Magento_Wishlist' => 1,        
  ),
);

The app/etc/config.php file is a simple PHP include file that returns an array of configured modules. The modules you see are the core modules that ship with the system. We’ll want to add our module to the end of this list.

#File: app/etc/config.php
<?php
return array (
  'modules' => 
  array (
    'Magento_Store' => 1,
    //... full module list snipped ...
    'Magento_Wishlist' => 1,
    'Pulsestorm_TutorialObjectManager1' => 1
  ),
);

Now if we run module:status, we should see our module listed in the enabled list.

$ php bin/magento module:status
List of enabled modules:
Magento_Store
//...
Pulsestorm_TutorialObjectManager1 

List of disabled modules:
None

Same as Magento 1, a module’s full name comes from combining its “namespace” folder and its “module name” folder. However, there’s a few things here that might throw a Magento 1 developer for a loop. Specifically

  1. Magento’s module namespace is now Magento_ instead of Mage_
  2. The core, community and local code pools are gone. All modules live in app/code

  3. The core team has replaced the app/etc/modules “module declaration files” with a simpler, PHP based configuration

The Magento to Mage change is, in my opinion, a neutral one, but the removal of both code pools and the module declaration files are net positive for Magento.

While the core, community, and local code-pools/include-folders allowed system owners to make quick changes to Magento functionality via code pool overrides, these files contributed heavily to a merchant’s unwillingness to upgrade their systems, and often led to subtle system problems when developers would change too much of a class file’s implicit contract.

The loss of module declaration files in app/etc/modules — while part of an interesting idea (one global config for the entire system) — will not be missed. While the original intent of these files may have been to configure which modules should, or should not, be in enabled in a system, poor early developer evangelism and core team practices meant that extension developers started packaging these files with their extensions, and any hope of these being user configurable files went out the window. The Magento 2 approach of a simple on/off configuration array makes much more sense.

Running Commands and Clearing Cache

Alright, now that we’ve installed our module, we’ll want to make sure the new ps:tutorial-object-manager-1 command shows up. Before we do that, there’s one more step to take. We’ll need to clear Magento’s cache.

Conceptually, cache clearing in Magento 2 is the same as Magento 1. There are long running operations that will run once, and then Magento caches the results for quicker loading next time. For example, although we’ve added our new module to Magento’s configuration, Magento’s configuration is cached, so the system that’s running doesn’t actually know about our module yet.

Magento 2 provides a cli command for clearing the cache. Just run the following, and you should be good to go.

$ php bin/magento cache:clean 
Cleaned cache types:
config
layout
block_html
view_files_fallback
view_files_preprocessing
collections
db_ddl
eav
full_page
translate
config_integration
config_integration_api
config_webservice

We say should because although this will clear all Magento’s cache types, it’s not 100% clear if this will clear the entire cache. Magento 1 contained a number of cache entries that were untyped (Zend’s module column names, for example), and Magento 1’s cache clearing commands wouldn’t delete these. Magento 2 is still too young for us to have discovered these edge cases, but if you’re uncertain about Magento’s cache being cleared and you’re using the default cache storage, you can nuke the entire cache by manually removing all the files and folders in Magento’s var/cache folder.

$ rm -rf /path/to/magento/var/cache/*

Additionally — although it’s not relevant to us here, Magento 2 also uses some automatic code generation features that — while not strictly a cache — may need to be regenerated if certain system configuration or code files are changed/updated. If you’re clearing your cache, it’s also a good idea to clear out these generated code files. The quickest and easiest way to do this on your development machine is to remove all the files in

$ rm -rf /path/to/magento/var/generation/*

You’ll want to be careful doing this with production systems. Magento 2 is still new enough that all the pitfalls of code generation have yet to be uncovered.

Assuming you’ve cleared your cache, let’s look for our command. You can see a list of commands by using the list command

$ php bin/magento list

//...

 ps:tutorial-object-manager-1              A cli playground for testing
                                           commands
//...

Assuming you see the command above, lets try running it!

$ php bin/magento ps:tutorial-object-manager-1
You did it!

Congratulations! You just manually installed your first Magento module.

Update: Kristof Ringleff pointed out there’s actually two commands for enabling/disabling your Magento 2 modules (module:enable and module:disable). You’ll definitely want to use these commands when you’re setting up a new module

$ php bin/magento module:enable Pulsestorm_TutorialObjectManager1
The following modules have been enabled:
- Pulsestorm_TutorialObjectManager1

To make sure that the enabled modules are properly registered, run 
'setup:upgrade'.

Cache cleared successfully.
Generated classes cleared successfully.

Alert: Generated static view files were not cleared. You can clear them 
using the --clear-static-content option. Failure to clear static view files 
might cause display issues in the Admin and storefront.

What’s nice about the module:enable/module:disable commands are they’ll

Changing our Command

We’re almost ready to start discussing the object manager. Now that we have the sample module installed, let’s try changing the implementation of ps:tutorial-object-manager-1.

Every cli command in Magento 2 is implemented with a PHP class. To change a command’s implementation, all you need to do is open this class file in your favorite text editor. Try opening up the following file, and locate the execute method

#File: app/code/Pulsestorm/TutorialObjectManager1/Command/Testbed.php    
protected function execute(InputInterface $input, OutputInterface $output)
{
    $output->writeln("You did it!");
}

Then, edit the command definition file so it looks like this instead

#File: app/code/Pulsestorm/TutorialObjectManager1/Command/Testbed.php    
protected function execute(InputInterface $input, OutputInterface $output)
{
    $output->writeln("Hello World!");
}    

If we try running the command now we should see the following output.

$ php bin/magento ps:tutorial-object-manager-1
Hello World!

All we’ve done above is pass a message to the command line framework’s writeln method — this is the framework’s equivalent of echo or print.

With that done, we’re finally ready to start talking about Magento’s object manager.

Magento 2 Objects

First off, let’s review some PHP 101. When you want to instantiate an object in PHP, you use the new keyword. Let’s give that a try in our execute method

#File: app/code/Pulsestorm/TutorialObjectManager1/Command/Testbed.php    
protected function execute(InputInterface $input, OutputInterface $output)
{
    $object = new \Pulsestorm\TutorialObjectManager1\Model\Example;
    $message = $object->getHelloMessage();
    $output->writeln(
        $message
    );                
}    

Try running the above code, and you should see something like this

$ php bin/magento ps:tutorial-object-manager-1
Hello Magento!

All we’ve done above is

  1. Instantiate an object from the class Pulsestorm\TutorialObjectManager1\Model\Example
  2. Call the resulting object’s getHelloWorld method to fetch a message string,

  3. Used the command line framework’s writeln method to output a message.

So far this is plain old PHP — if you’re not familiar with PHP namespaces or those backslash characters you may find this namespace primer useful.

There’s nothing stopping you from using plain old PHP classes in Magento 2. However, if you want to take advantage of Magento 2’s advanced object features (automatic constructor dependency injection, object proxying, etc) you’ll need to use Magento’s object manager. Give the following code a try

#File: app/code/Pulsestorm/TutorialObjectManager1/Command/Testbed.php    
protected function execute(InputInterface $input, OutputInterface $output)
{
    $manager = $this->getObjectManager();
    $object  = $manager->create('Pulsestorm\TutorialObjectManager1\Model\Example');
    $message = $object->getHelloMessage();
    $output->writeln(
        $message
    );                
}  

Run the above code, and you should see identical output

$ php bin/magento ps:tutorial-object-manager-1
Hello Magento!

The big difference between this new code and our old code are these two lines

#File: app/code/Pulsestorm/TutorialObjectManager1/Command/Testbed.php        
$manager = $this->getObjectManager();
$object  = $manager->create('Pulsestorm\TutorialObjectManager1\Model\Example');

The first, $manager = $this->getObjectManager();, fetches the object manager for us. This is not something you’d normally do when writing a Magento 2 extension — getObjectManager is a helper method we’ve added for this tutorial. We’ll return to this concept later, but for now accept that the getObjectManager will return an instance of the Magento object manager.

The object manager is a special object that Magento uses to instantiate nearly all its objects. That’s what we’ve done in our second line

#File: app/code/Pulsestorm/TutorialObjectManager1/Command/Testbed.php    
$object  = $manager->create('Pulsestorm\TutorialObjectManager1\Model\Example');

We’ve called the object manager’s create method, and passed in a PHP class name as a string. Behind the scenes, the object manager instantiates a Pulsestorm\TutorialObjectManager1\Model\Example for us.

That, in its simplest form, is Magento 2’s object manager. This may seem like a trivial difference, but by routing all object instantiation through this single point, Magento system engineers are able to give their objects a number of “super powers”.

Before we get to the super powers, and while covering it in full is outside the scope of this article, the Magento object manager’s source code is in the following class file.

#File: lib/internal/Magento/Framework/ObjectManager/ObjectManager.php
namespace Magento\Framework\ObjectManager;

class ObjectManager implements \Magento\Framework\ObjectManagerInterface
{
    //...    
}

If you’re a Magento 1 developer the object manager is a replacement for these three methods

Mage::getModel(...);
Mage::helper(...);
Mage::getSingleton('core/layout')->createBlock(...);    

While Magento 2 still has the concept of a model, a helper, and a block, you no longer need to know the class alias for these objects (core/template, model/product, etc.) and the object manager can instantiate any PHP class, not just a model, helper or block object.

Automatic Singleton Objects

We’re going to wrap up today by talking about one of the super powers Magento’s object manager gives to objects — automatic singletons. Before we do that though, we should probably describe what we mean by singleton!

Try running the following code

#File: app/code/Pulsestorm/TutorialObjectManager1/Command/Testbed.php
protected function execute(InputInterface $input, OutputInterface $output)
{
    $manager = $this->getObjectManager();
    $object  = $manager->create('Pulsestorm\TutorialObjectManager1\Model\Example');        
    $object->message = 'Hello PHP!';
    $output->writeln(
        $object->getHelloMessage()
    );                

    $object  = $manager->create('Pulsestorm\TutorialObjectManager1\Model\Example');        
    $output->writeln(
        $object->getHelloMessage()
    );                        
}

You should see output that’s something like this

$ php bin/magento ps:tutorial-object-manager-1
Hello PHP!
Hello Magento!

This code is similar to our previous example, with a few additions. First, we used the object manager to instantiate an object from the Pulsestorm\TutorialObjectManager1\Model\Example class

#File: app/code/Pulsestorm/TutorialObjectManager1/Command/Testbed.php    

$object  = $manager->create('Pulsestorm\TutorialObjectManager1\Model\Example'); 

Then we set a custom message string for the object

#File: app/code/Pulsestorm/TutorialObjectManager1/Command/Testbed.php    

$object->message = 'Hello PHP';

And then we used the cli framework’s writeln method to output the new message.

#File: app/code/Pulsestorm/TutorialObjectManager1/Command/Testbed.php    

$output->writeln(
    $object->getHelloMessage()
);                

That’s the first chunk of code. In the second chunk of code

#File: app/code/Pulsestorm/TutorialObjectManager1/Command/Testbed.php    

$object  = $manager->create('Pulsestorm\TutorialObjectManager1\Model\Example');        
$output->writeln(
    $object->getHelloMessage()
);                        

we instantiated a new Pulsestorm\TutorialObjectManager1\Model\Example object, and then output its default message. This meant our program output

Hello PHP!
Hello Magento!

was the custom message, followed by the default message.

Pretty straight forward stuff.

Now, let’s try the exact same code, except instead of using the object manager’s create method, we’ll use the object manager’s get method

#File: app/code/Pulsestorm/TutorialObjectManager1/Command/Testbed.php    

protected function execute(InputInterface $input, OutputInterface $output)
{
    $manager = $this->getObjectManager();
    $object  = $manager->get('Pulsestorm\TutorialObjectManager1\Model\Example');        
    $object->message = 'Hello PHP!';
    $output->writeln(
        $object->getHelloMessage()
    );                

    $object  = $manager->get('Pulsestorm\TutorialObjectManager1\Model\Example');        
    $output->writeln(
        $object->getHelloMessage()
    );                        
}

Run this code, and your output will be a little different.

$ php bin/magento ps:tutorial-object-manager-1
Hello PHP
Hello PHP

Instead of printing the default message the second time, our program printed the same custom message we set on the first object. Why’d this happen? Because the second object is the first object.

This is the Magento object manager’s “automatic singleton” feature. If you’re not familiar with the concept — a singleton is an object that can only be instantiated once. With a singleton, future attempts to instantiate the same object will return the original instance. This is traditionally accomplished by adding special code to a class’s constructor — but with Magento 2’s object manager any class can be turned into a singleton object.

Relating the back to our code sample above, the second time we called get Magento returned the original object with a custom message set.

If you’re coming from Magento 1, the create/get difference is similar to the

Mage::getModel(...);
Mage::getSingleton(...);

difference. Again though, this applies to all classes and objects in Magento 2, not just the model objects.

Automatic singletons are just one of the super powers Magento 2’s object manager gives its objects. Next time we’ll talk about the big super power — automatic constructor parameter dependency injection — and we’ll also reveal why Magento’s documentation (seemingly paradoxically) tells you not to use the object manager.

Originally published July 10, 2015
Series NavigationMagento 2’s Automatic Dependency Injection >>

Copyright © Alan Storm 1975 – 2017 All Rights Reserved

Originally Posted: 10th July 2015