Download - Magento 2 Design Patterns
MAGENTO 2 DESIGN PATTERNSby Max Pronko
Magento Meetup Dublin 13February 9, 2016
ABOUT ME
➤ 6 years with Magento
➤ Magento 2 Blogger
➤ Father
➤ from Ukraine
➤ CTO @ GiftsDirect
MAGENTO 2 ARCHITECTURE DESIGN GOALS
What:
➤ Streamline customisations
➤ Simplify external integrations
How:
➤ Loose coupling between Modules
➤ SOLID Principles
➤ Design Patterns
➤ Configuration over customisation
3
S.O.L.I.D.Principles
4
SINGLE RESPONSIBILITY PRINCIPLE
➤ Class should have only 1 reason to change.
namespace Magento\Cms\Controller\Page;
class View extends \Magento\Framework\App\Action\Action { /** code */
public function execute() { $pageId = $this->getRequest()->getParam('page_id', $this->getRequest()->getParam('id', false)); $resultPage = $this->_objectManager->get('Magento\Cms\Helper\Page')->prepareResultPage($this, $pageId); if (!$resultPage) { $resultForward = $this->resultForwardFactory->create(); return $resultForward->forward('noroute'); } return $resultPage; } }
5
OPEN/CLOSED PRINCIPLE
➤ Classes should be open for extension, but closed for modification.
namespace Magento\Catalog\Controller\Adminhtml\Category;
class RefreshPath extends \Magento\Catalog\Controller\Adminhtml\Category { /** code */
public function execute() { $categoryId = (int)$this->getRequest()->getParam('id'); if ($categoryId) { $category = $this->_objectManager->create('Magento\Catalog\Model\Category')->load($categoryId);
/** @var \Magento\Framework\Controller\Result\Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); return $resultJson->setData(['id' => $categoryId, 'path' => $category->getPath()]); } } }
6
INTERFACE SEGREGATION PRINCIPLE
➤ Client should not be forced to depend on methods it does not use.
namespace Magento\Payment\Gateway\Http;
interface TransferInterface { public function getClientConfig(); public function getMethod(); public function getHeaders(); public function shouldEncode(); //Client Zend public function getBody(); public function getUri(); //Client Zend public function getAuthUsername(); // None public function getAuthPassword(); // None }
namespace Magento\Payment\Gateway\Http\Transfer;
interface AuthInterface { public function getUsername(); public function getPassword(); } interface ZendUrlEncoderInterface { public function shouldEncode(); } interface ConfigInterface { public function getValue(); }
namespace Magento\Payment\Gateway\Http;
interface TransferInterface { public function getMethod(); public function getHeaders(); public function getBody(); }
Decoupling example
8
Might be improved
DEPENDENCY INVERSION PRINCIPLE
➤ High-level modules should not depend on low-level modules. Both should depend on abstractions.namespace Magento\Framework;
class Image { /** * @var Image\Adapter\AdapterInterface */ protected $_adapter;
public function __construct( \Magento\Framework\Image\Adapter\AdapterInterface $adapter, $fileName = null ) { $this->_adapter = $adapter; $this->_fileName = $fileName; if (isset($fileName)) { $this->open(); } }
/** code */ }
9
SOME TIPS
➤ Create tiny classes to avoid monsters
➤ Build your code on abstractions (interfaces)
➤ Look inside code for good practices
➤ Refactor, refactor, refactor
10
DESIGN PATTERNSMagento 2 Edition
“Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice
-Christopher Alexander
DESIGN PATTERNS
12
MAGENTO 2 DESIGN PATTERNS
OBJECT MANAGER
➤ Dependency Injection Manager
➤ Knows how to instantiate classes
➤ Creates objects
➤ Provides shared pool of objects
➤ Enables lazy initialisation
OBJECT MANAGER
15
Patterns:
➤ Dependency Injection
➤ Singleton
➤ Builder
➤ Abstract Factory
➤ Factory Method
➤ Decorator
➤ Value Object
➤ Composition
➤ Strategy
➤ CQRS (command query responsibility segregation)
➤ more…
OBJECT MANAGER
Magento/Framework
16
FACTORY METHOD
17
namespace Magento\Catalog\Model;
class Factory { protected $_objectManager;
public function __construct(\Magento\Framework\ObjectManagerInterface $objectManager) { $this->_objectManager = $objectManager; }
public function create($className, array $data = []) { $model = $this->_objectManager->create($className, $data);
if (!$model instanceof \Magento\Framework\Model\AbstractModel) { throw new \Magento\Framework\Exception\LocalizedException( __('%1 doesn\'t extends \Magento\Framework\Model\AbstractModel', $className) ); } return $model; } }
➤ Creates family of related objects.
FACTORY METHOD
Magento\FrameworkMagento\Catalog\Model
OBSERVERnamespace Magento\Catalog\Model;
use Magento\Catalog\Api\Data\ProductInterface; use Magento\Framework\DataObject\IdentityInterface; use Magento\Framework\Pricing\SaleableInterface;
class Product extends \Magento\Catalog\Model\AbstractModel implements IdentityInterface, SaleableInterface, ProductInterface { protected $_eventPrefix = 'catalog_product';
public function validate() { $this->_eventManager->dispatch($this->_eventPrefix . '_validate_before', $this->_getEventData()); $result = $this->_getResource()->validate($this); $this->_eventManager->dispatch($this->_eventPrefix . '_validate_after', $this->_getEventData()); return $result; } }
File: Meetup\Module\etc\adminhtml\events.xml <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> <event name="catalog_product_validate_before"> <observer name="meetup_module_product_validator" instance="Meetup\Module\Observer\ProductObserver" /> </event> </config>
OBSERVER
Domain Model Framework
20
➤ Distributed event handling system.
PROXY
File: app/code/Magento/Catalog/etc/di.xml <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\Catalog\Model\ResourceModel\Product\Collection"> <arguments> <argument name="catalogUrl" xsi:type="object">Magento\Catalog\Model\ResourceModel\Url\Proxy</argument> <argument name="customerSession" xsi:type="object">Magento\Customer\Model\Session\Proxy</argument> </arguments> </type> </config>
➤ Support for resource consuming objects.
21
PROXYnamespace Magento\Catalog\Model\ResourceModel\Url;
use Magento\Framework\ObjectManagerInterface;
class Proxy extends \Magento\Catalog\Model\ResourceModel\Url { public function __construct( ObjectManagerInterface $objectManager, $instanceName = '\\Magento\\Catalog\\Model\\ResourceModel\\Url', $shared = true ) { $this->_objectManager = $objectManager; $this->_instanceName = $instanceName; $this->_isShared = $shared; }
protected function _getSubject() { if (!$this->_subject) { $this->_subject = true === $this->_isShared ? $this->_objectManager->get($this->_instanceName) : $this->_objectManager->create($this->_instanceName); } return $this->_subject; }
public function _getProductAttribute($attributeCode, $productIds, $storeId) { return $this->_getSubject()->_getProductAttribute($attributeCode, $productIds, $storeId); }
public function load(\Magento\Framework\Model\AbstractModel $object, $value, $field = null) { return $this->_getSubject()->load($object, $value, $field); } }
COMPOSITE
namespace Magento\Payment\Gateway\Request;
use Magento\Framework\ObjectManager\TMap; use Magento\Framework\ObjectManager\TMapFactory;
class BuilderComposite implements BuilderInterface { private $builders;
public function __construct( TMapFactory $tmapFactory, array $builders = [] ) { $this->builders = $tmapFactory->create( [ 'array' => $builders, 'type' => BuilderInterface::class ] ); }
public function build(array $buildSubject) { $result = []; foreach ($this->builders as $builder) { $result = $this->merge($result, $builder->build($buildSubject)); }
return $result; }
protected function merge(array $result, array $builder) { return array_replace_recursive($result, $builder); } }
COMPOSITE
Magento/Payment/Gateway
#ThanksQ&A
/maxpronkocom @max_pronkowww.maxpronko.com
MAGENTO 2 DESIGN PATTERNS
➤ Object Manager
➤ Event Observer
➤ Composition
➤ Factory Method
➤ Singleton
➤ Builder
➤ Factory
➤ State
➤ Strategy
➤ Adapter
➤ Command
➤ CQRS
➤ MVVM