Design Patterns in Magento 2 – Object Manager and Factories

You are currently viewing Design Patterns in Magento 2 – Object Manager and Factories

Adobe Commerce, also known as Magento, has been a dominating e-Commerce platform for the past decade. It has reinvigorated itself with flexible communication by introducing complex architecture based on a combination of design patterns. Object Manager and Factories are noteworthy aspects of the design pattern. In fact, Magento 2 attributes a lot to Object Manager functionality. Diagram 1.0 shows the object manager workflow.  

Diagram 1.0: Object Manager Flow 

Object Manager fulfils the following:  

  • Object creation in factories and proxies.  
  • Implementing the singleton pattern by returning the same shared instance of a class when requested.  
  • Dependency management by instantiating the preferred class when a constructor requests its interface.   
  • Automatically instantiating parameters in class constructors. 

Object Manager Interface is the part of Magento framework used for the implementation of method createmethod getmethod config.   

Create method creates new objects based on the parameters of “type” and “arguments”. The first is a string type value that contains the name of the object type. The latter contains the list of arguments required for the new instance of an object. This is initialized using the factory method to generate a new instance by its type. What the factory method does is identify a preference for the interface when $type parameter is used. In other cases, class gets generated directly.  

/**  

* Create new object instance  

*  

* @param string $type  

* @param array $arguments  

* @return mixed  

*/  

public function create($type, array $arguments = []) 

{ 

return $this->_factory->create($this->_config->getPreference($type), $arguments); 

} 

Get method is particularly important because it retrieves the cached object instance. Through it, Object Manager provides part of the Object lifestyle configuration. What this means is that some types of objects can be either shared or not be it Singleton or Transient. In the di.xml file, the shared parameter is responsible for this:  

<argument xsi:type="object" shared="{shared}">{typeName}</argument> 

Implementation of this method goes like this: 

/**  

* Retrieve cached object instance  

*  

* @param string $type  

* @return mixed  

*/  

public function get($type)  

{  

    $type = ltrim($type, '\\');  

    $type = $this->_config->getPreference($type); 

    if (!isset($this->_sharedInstances[$type])) {  

        $this->_sharedInstances[$type] = $this->_factory>create($type);  

    }  

    return $this->_sharedInstances[$type];  

} 

With $type the Object Manager class identifies a preference for the interface if it is declared, and returns a cached instance of the object if it is present in the pull of previously generated (shared) objects.  

Method Config is an entry point for the whole application initialization during the HTTP calls and the area initialization. In this phase, the object manager works on collecting all preferences described in the di.xml in one place – global configuration.  

/**  

* Configure di instance  

* Note: All arguments should be pre-processed (sort order, translations, etc) before passing to method configure.  

*  

* @param array $configuration  

* @return void  

*/  

public function configure(array $configuration)  

{  

    $this->_config->extend($configuration);  

} 

Object Manager supports backward compatibility. For example, sometimes it is required to add a new parameter to the object constructor and to follow backward compatibility, a newly added object has to be declared via the manager.  

/**  

* @param ResourceConnection $resource  

* @param \Magento\Store\Model\StoreManagerInterface $storeManager  

* @param \Magento\Catalog\Model\Config $config  

* @param QueryGenerator $queryGenerator  

* @param MetadataPool|null $metadataPool  

* @param TableMaintainer|null $tableMaintainer  

*/ public function __construct(\Magento\Framework\App\ResourceConnection $resource,    

  \Magento\Store\Model\StoreManagerInterface $storeManager,  

  \Magento\Catalog\Model\Config $config,  

  QueryGenerator $queryGenerator = null,  

  MetadataPool $metadataPool = null,  

  TableMaintainer $tableMaintainer = null  

) {  

   $this->resource = $resource; 

   $this->connection = $resource->getConnection(); 

   $this->storeManager = $storeManager; 

   $this->config = $config;  

   $this->queryGenerator = $queryGenerator ?: ObjectManager::getInstance()->get(QueryGenerator::class);  

   $this->metadataPool = $metadataPool ?:  ObjectManager::getInstance()->get(MetadataPool::class);  

   $this->tableMaintainer = $tableMaintainer ?:  ObjectManager::getInstance()->get(TableMaintainer::class); }

Object Manager uses factory methods quiet a lot. Now we’ll take a look at the factories and the implementation.  

What a factory method does is defines an interface for creating an object but lets the subclasses decide which class to instantiate. The factory method lets a class defer instantiation to subclasses which is useful for constructing individual objects for a specific purpose without the requestor knowing the specific class being instantiated.  

In Magento the factory method gets applied to all layers of the application. The software application has a defined contract for object initialization and the method guarantees the fulfillment for different classes. So, in a nutshell, decoupling of both – objects and the construction takes place simultaneously.  

One of the core features of the Magento 2 is code generation and it simplifies data object factory generation. Data object is a part of the service layer of the Magento architecture and represent entities such as customer or catalog. As per interface declaration, factory method is ideal to generate new instances of those objects.  

Factory constructor consists of two parameters: Object name and class name of the data object entity and this is how it works. 

/**  

* Get default constructor definition for generated class  

*  

* @return array  

*/  

protected function _getDefaultConstructorDefinition()  

{ 

   return [  

       'name' => '__construct', 

       'parameters' => [  

            ['name' => 'objectManager', 'type' => '\\' . \Magento\Framework\ObjectManagerInterface::class],  

            ['name' => 'instanceName', 'defaultValue' => $this->getSourceClassName()], 

 ],  

'body' => "\$this->_objectManager = \$objectManager;\n\$this->_instanceName = \$instanceName;", 'docblock' => [  

   'shortDescription' => ucfirst(static::ENTITY_TYPE) . ' constructor',  

   'tags' => [  

    [  

      'name' => 'param',  

      'description' => '\Magento\Framework\ObjectManagerInterface $objectManager', 

    ],  

    ['name' => 'param', 'description' => 'string $instanceName'],  

    ],  

  ]  

 ];  

}

Factories can also be auto-generated in the following way.  

/**  

* Factory constructor  

*  

* @param \Magento\Framework\ObjectManagerInterface $objectManager  

* @param string $instanceName  

*/ public function __construct( \Magento\Framework\ObjectManagerInterface $objectManager, $instanceName = '\\Magento\\Customer\\Api\\Data\\CustomerInterface'  

) {  

    $this->_objectManager = $objectManager;  

    $this->_instanceName = $instanceName;  

} 

Factory method implementation needs only one public method – create. A $create array contains: 

  • Name of the method – create  
  • Method parameters – a not required data array that consists of initialized class properties and their values 
  • Body – the core of the factory (code that is responsible for the new object instance generation)  
  • Dockblock and tags – contains additional information  
/**  

* Returns list of methods for class generator  

*  

* @return array 

*/  

protected function _getClassMethods()  

{  

    $construct = $this->_getDefaultConstructorDefinition();  

// public function create(array $data = array())  

$create = [  

   'name' => 'create',  

   'parameters' => [['name' => 'data', 'type' => 'array', 'defaultValue' => []]],  

   'body' => 'return $this->_objectManager->create($this->_instanceName, $data);', 'docblock' => [  

                             'shortDescription' => 'Create class instance with specified parameters', 'tags' => [  

                                         ['name' => 'param', 'description' => 'array $data'], [  

                                   'name' => 'return',  

                                   'description' => $this->getSourceClassName()  

                                   ],  

                               ],  

                           ],  

                        ];  

                        return [$construct, $create];  

}

Let’s review body of the method and review it on the automatically generated example:  

/**  

* Create class instance with specified parameters  

*  

* @param array $data  

* @return \Magento\Customer\Api\Data\CustomerInterface  

*/  

public function create(array $data = [])  

{  

     return $this->_objectManager->create($this->_instanceName, $data);  

} 

In Magento 2, the core of the factory method is the object managers usage together with the create method. It takes an instance name and combines it with some parameters in order to initiate a completely new object. Factory method usage is aimed at the new object initialization by following a specific contract of decoupling objects. Like in all other software applications architecture52, Magento 2 uses an approach for handling object generations. The most frequently used aspect is the Data object initialization on the service layer. The factory method is simple to use and doesn’t have any negative effects on the application layers.  

This Post Has One Comment

  1. Good post. I learn something totally new and challenging on blogs I stumbleupon every day. It will always be interesting to read through articles from other writers and use a little something from other sites.

Leave a Reply