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.

It’s been a long and winding road, but the end is in sight! In this, the penultimate article in our object manager tutorial, we’re going to discuss working with instance and non-injectable objects in Magento 2.

The create method will instantiate a new object each time it’s called. The get method will instantiate an object once, and then future calls to get will return the same object. This behavior is similar to Magento 1’s getModel vs. getSingleton factories

We know the parameter will be a Pulsestorm\TutorialInstanceObjects\Model\Example object, but we don’t know if it will be a new instance of a Pulsestorm\TutorialInstanceObjects\Model\Example object, or the same Pulsestorm\TutorialInstanceObjects\Model\Example object passed/injected into other constructors.

By default, all objects created via automatic constructor dependency injection are “singleton-ish” objects — i.e. they’re created via the object manager’s get method.

If you want a new instance of an object, i.e. you want the object manager to use create, you’ll need to add some additional <type/> configuration to your module’s di.xml file.

If we wanted the object manager to instantiate Pulsestorm\TutorialInstanceObjects\Model\Example as an instance object every time, we’d add the following configuration to our di.xml

This is the same <type/> tag we saw back in our argument replacement tutorial. The name attribute should be the name of the class whose behavior you want to change.

The new-to-us attribute is shared. If shared is set to false, then Magento 2 will use the create method to instantiate an object every time it encounters Pulsestorm\TutorialObjectManager1\Model\Exampleas an automatically injected constructor argument. The shared attribute has no effect on objects instantiated directly via PHP’s new keyword, or the object manager’s two methods.

This attribute is named shared due to an implementation detail in the object manager. When you use get to instantiate an object, the object manager stores all the already instantiated objects in a _sharedInstances array.

When you configure a specific type (i.e. a specific PHP class) with shared="false", you’re telling Magento 2 that you don’t want to use this _sharedInstances array.

So, all you need to remember here is shared="true" is the default, and you’ll get a singleton-ish/global object. If you change your type configuration to shared="false", the automatic constructor dependency injection system will start instantiating a new parameter every-time a programmer instantiates the attribute’s owner object.

Magento 2 Factories

While the shared attribute is a useful bit of duct tape for those times you need an injected dependency to be a brand new instance object, it’s not an ideal solution for every (or even most) cases where you don’t want singletons.

One problem with shared is the injected dependency is still dependent on its owner object being shared or un-shared. There’s lots of times where you just need a new instance of an object and might not be able to refactor your object relationships to accomplish that.

A perfect example of this are CRUD data objects, such as Magento’s CMS page objects or the catalog product objects. In Magento 1 you’d create a CMS page object like this

Mage::getModel('page/cms')->load($id);

In Magento 2, these sorts of objects are called “non-injectables”. Dependency injection is meant for objects that “do some thing”, or “provide some service”. These data objects, however, are meant to “identify this specific thing”. What we’re going to look at next is how to use these sorts of objects without automatic constructor dependency injection tying our hands.

However, if you were to do this, your CMS Page object loses out on all of Magento’s object manager features.

Fortunately, the Magento 2 core developers haven’t abandoned us. In Magento 2, you instantiate these sorts of non-injectable objects via factory objects. Like so much in Magento 2, an example is worth 1,000 words.

This may take a little getting used to, but compared to the error prone XML configuration needed to use Magento 1’s factory methods, this is already a big win.

Factory Definitions and Code Generation

There’s one last thing to cover about factory objects in Magento 2 that might answer a few questions in your head

Ugh! I need to define a bunch of boiler plate factory code? Lame.

Where can I see what a factory object looks like?

If we start with the second and more adult question, you may be in for a small surprise. Based on the factory’s full class name, (Magento\Cms\Model\PageFactory), you might expect to find it at one of the following locations

That’s because Magento 2 uses automatic code generation to create factory classes. You may remember this code generation from the proxy object article. If you’ve actually run the code above, you’ll find the PageFactory class in the following location.

var/generation/Magento/Cms/Model/PageFactory.php

While the specifics are beyond the scope of this article, whenever

PHP encounters a class name that ends in Factory

And the autoloader can’t load that class because there’s no definition file

Magento 2 will automatically create the factory.

If you’re curious how this happens this Stack Exchange answer showing the Magento/Framework/Code/Generator/Autoloader kickoff point is a good place to start.

Factories for All

Factories aren’t just for Magento core code — they’ll work with any module class. The sample module we had you install includes a Pulsestorm\TutorialInstanceObjects\Model\Example object. Let’s replace the __construct method with one that adds a factory class for the Example object.

Run our command with the above execute method in place, and you should see the following.

$ php bin/magento ps:tutorial-instance-objects
You just used a
Pulsestorm\TutorialInstanceObjects\Model\ExampleFactory
to create a
Pulsestorm\TutorialInstanceObjects\Model\Example

As you can see, this code ran without issue, despite our never defining a Pulsestorm\TutorialInstanceObjects\Model\ExampleFactory class. You can find the factory definition in the generated code folder

As for the specific implementation — right now a factory’s create method accepts an array of parameters and users the object manager to create the object. However, future versions of Magento may change how these factories work. By having the framework generate these factories Magento 2 saves us the error prone busy work of coding up this boilerplate and the core team maintains control over how the factories work.

Wrap Up

With this article and its six predecessors complete, we have a pretty good understanding of Magento’s object system, how that system impacts the shape of the Magento code base, and (most importantly) the basic understanding that’s critical if we want to apply reason to Magento 2 code in the real world.

There is, however, one last thing we need to cover, and that’s the object plugin system. The plugin system is the true successor to Magento 1’s class rewrite system, and with a solid understanding of the object manager and automatic constructor dependency injection, we’re ready to tackle this next time in our final Object Manager tutorial.