Applying Code Customizations to Magento 2 (Part 1) Anton Kril Architect, Magento 2 Legal Disclaimer Copyright © 2015 Magento, Inc. All Rights Reserved. Magento®, eBay Enterprise™ and their respective logos are trademarks, service marks, registered trademarks, or registered service marks of eBay, Inc. or its subsidiaries. Other trademarks or service marks contained in this presentation are the property of the respective companies with which they are associated. This presentation is for informational and discussion purposes only and should not be construed as a commitment of Magento, Inc. or eBay Enterprise (“eBay Enterprise”) or of any of their subsidiaries or affiliates. While we attempt to ensure the accuracy, completeness and adequacy of this presentation, neither Magento, Inc., eBay Enterprise nor any of their subsidiaries or affiliates are responsible for any errors or will be liable for the use of, or reliance upon, this presentation or any of the information contained in it. Unauthorized use, disclosure or dissemination of this information is expressly prohibited. Interactions with Magento2 Code Extension UI Model Service Contracts UI Vendor Model Service Contracts Customization UI Model Service Contracts Vendor Model Customization Points Customization Points Admin Configuration High-level Configuration acl.xml routes.xml layout events.xml cache.xml webapi.xml di.xml Low-level Linking Object B Object B Object C Object A Store Configuration Store Configuration • • • • In admin application: Stores->Configuration Values stored in Database Configured in system.xml Default values in config.xml High-level Configuration XML is like violence: if it doesn’t solve your problem, you aren’t using enough of it. Chris Maden High-level Configuration events.xml Event Manager layout Routing Application Subsystems EntryPoint Application Area Cron App WebAPI Web App Admin Media App Frontend Low Level Linking OOP 1. Composability 2. Modularity Composability Composability Split your code to lots of small chunks and then compose them to build your application Context Dependence Object totally dependent on on it’s environment EventManager Config Invoker Context Dependence namespace Magento\Framework\Event; class Manager implements ManagerInterface { public function dispatchEvent($eventName, array $eventData) { $observers = Mage::getConfig()->getObservers($eventName); foreach ($observers as $observer) { Mage::getSingleton('core/event_invoker')->invoke($observer); } } // some other code } Context Independence Application A Config Invoker EventManager Alternative Config Application B Config Invoker Dependency Injection Dependencies are not located within object but are provided to that object by environment Dependency Injection namespace Magento\Framework\Event; class Manager implements ManagerInterface { public function __construct( Invoker $invoker, Config $eventConfig, $prefix = '' ) { $this->_invoker = $invoker; $this->_eventConfig = $eventConfig; $this->_prefix = $prefix; } // some other code } Seams di.xml <config> <type name="Magento\Framework\Event\Manager"> <arguments> <argument name="prefix" xsi:type="string">eventPrefix</argument> <argument name="eventConfig" xsi:type="object"> My\Alternative\Event\Config </argument> </arguments> </type> </config> di.xml <config> <type name="Magento\Framework\Event\Manager"> <plugin name="logging" type="My\Module\Event\Logger"/> </type> </config> Unit Tests class ManagerTest extends \PHPUnit_Framework_TestCase { protected function setUp() { $this->_invoker = $this->getMock('Magento\Framework\Event\Invoker'); $this->_eventConfigMock = $this->getMock('Magento\Framework\Event\Config'); $this->_eventManager = new \Magento\Framework\Event\Manager( $this->_invoker, $this->_eventConfigMock ); } } SOLId 1. 2. 3. 4. Single Responsibility – Lots of small objects Open-Closed – Each object has multiple extension points Liskov Substitution – Polymorphism for composability Interface Segregation – Granularity Modularity soliD Coupled Module A Module B Implementation Decoupled Module AB Module A Adapter Module B Implementation Interface Module AB Adapter Module C Implementation Thank you! Applying Code Customizations to Magento 2 Q&A Anton Krill [email protected] Applying Code Customizations to Magento 2 (Part 2) Eugene Tulika Architect, Magento Platform Services Legal Disclaimer Copyright © 2015 Magento, Inc. All Rights Reserved. Magento®, eBay Enterprise™ and their respective logos are trademarks, service marks, registered trademarks, or registered service marks of eBay, Inc. or its subsidiaries. Other trademarks or service marks contained in this presentation are the property of the respective companies with which they are associated. This presentation is for informational and discussion purposes only and should not be construed as a commitment of Magento, Inc. or eBay Enterprise (“eBay Enterprise”) or of any of their subsidiaries or affiliates. While we attempt to ensure the accuracy, completeness and adequacy of this presentation, neither Magento, Inc., eBay Enterprise nor any of their subsidiaries or affiliates are responsible for any errors or will be liable for the use of, or reliance upon, this presentation or any of the information contained in it. Unauthorized use, disclosure or dissemination of this information is expressly prohibited. Service Contracts And Compatible Customizations Extensions Compatibility Challenges How to ensure that two extensions will be compatible in a new version? Interfaces may be changed in a new version Extensions may depend on optional modules which is turned off Extensions may depend on undocumented behavior Challenges for Developer How to implement extension in the way that it will keep backward compatibility but can evolve? How to understand what functionality of the extension is stable and what is not? Stable APIs Backward Compatible: Classes or Interfaces are not removed Methods of the classes keeps signature between versions Interfaces neither changes existing methods nor add new ones Explicit Interfaces No generic data types as “mixed”, “object” or “array” Few ways to make promises in Magento 2 Semantic Versioning of the modules makes dependencies between modules explicit { "name": "magento/module-catalog-inventory", "require": { "magento/module-customer": "0.74.0-beta2" }, "type": "magento2-module" } @api annotation identifies subset of the methods with the stable APIs Enforced by tools and static tests /** * @api */ interface AuthorizationInterface { /** * Check current user permission on resource and privilege * * @param string $resource * @param string $privilege * @return boolean */ public function isAllowed($resource, $privilege = null); } Magento 1.x Domain Level API Model is an entry point to the Module Interface implicitly defined via the database schema No single place for the business rules They can be in: Controllers Models Helpers Templates Client getData() Model Resource Model Service Contracts Other M2 Modules M2 Module Blocks Web API clients Controllers Templates Service Contracts Repositories Models Resource Models Services Data Objects Domain Level API Single way to define API for the business feature Defines strong interface Single place to implement business rules All interactions with the module are safe to go through the contracts: same behavior guaranteed M2 Module Blocks Controllers Templates Service Contracts Repositories Models Resource Models Services Data Objects Service Contracts Interfaces They are just PHP Interfaces Service interfaces Data interfaces Defines business operations Defines data structures, used as input and output types of the business operations Examples: load, delete, save, change password, etc. Examples: Customer, Product, Region, Currency, etc. Service Contracts Service Interfaces Data Interface More on Data Interfaces Has just setters and getters to describe a data Reusable across different Service interfaces Encapsulates all the data needed to process service request Can be Serialized Annotations are used to extract the data More on Service Interfaces Defines public operations supported by the module Methods are independent and stateless. Invocation of one method should not affect the result of another Methods combined in interface by cohesion principle Annotated with types information Classes Implementing Data Interfaces It can be Model: All the setters and getters should be declared explicitly No magic methods It can be Any PHP class: Implements data interface and any other methods It can be Data Object: Implements just methods from the data interface Data Interfaces Models Data Objects Implementation of Service Interfaces Resource Models: Used for persistence operations Implements load/save/delete methods and accept Data Interface as an input Services: Implements operations and business rules around them Service Interfaces Resource Models Services Use Service Contracts Define dependency on service interface in the constructor class CreateCustomer extends \Magento\Customer\Controller\Account { public function __construct( AccountManagementInterface $accountManagement ){ $this->accountManagement = $accountManagement; } public function execute() { $customer = $this->getRequest()->getParam('customer'); $password = $this->getRequest()->getParam('password'); $redirectUrl = $this->getRequest()->getParam('redirect_url'); $customer = $this->accountManagement ->createAccount($customer, $password, $redirectUrl); … } } Re-Implement Service Contracts Define a preference in DI: it will point on a new implementation All the constructors will be injected with a new implementation <preference for="Magento\Customer\Api\AccountManagementInterface" type="SomeVendor\NewExtension\Model\AccountManagement" /> <preference for="Magento\Customer\Api\Data\RegionInterface" type="SomeVendor\NewExtension\Model\Data\Region" /> Customize Service Contracts Plugins is a way to add new behavior each time Service Interface implementation is invoked /** * Plugin after create customer that updates any newsletter subscription that may have existed. * * @param CustomerRepositoryInterface $subject * @param CustomerInterface $customer * @return CustomerInterface */ public function afterSave(CustomerRepositoryInterface $subject, CustomerInterface $customer) { $this->subscriberFactory->create()->updateSubscription($customer->getId()); return $customer; } Extend Data Interfaces Extension Attributes is a way to Extend Data Interfaces from thirdparty module Added via xml configuration, generated as an object Product Data Interface Product fields Rating and Reviews Module Review fields Catalog Inventory fields Catalog Inventory Module Generated Extension Attributes <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface"> <attribute code="bundle_product_options" type="Magento\Bundle\Api\Data\OptionInterface[]" /> </extension_attributes> interface ProductExtensionInterface extends \Magento\Framework\Api\ExtensionAttributesInterface { /** * @return \Magento\Bundle\Api\Data\OptionInterface[] */ public function getBundleProductOptions(); /** * @param \Magento\Bundle\Api\Data\OptionInterface[] $bundleProductOptions * @return $this */ public function setBundleProductOptions($bundleProductOptions); ... } Summary Magento 2.x gives stronger promises on public APIs Service Contracts are the way to define API for the Business features Service Contracts are the single entry point to functionality of the module Customizable via dependency injection, plugins and extension attributes Customizations become available for all the clients of Service Contracts Thank you! Applying Code Customizations to Magento 2 Q&A Eugene Tulika [email protected] @vrann Sergii Shymko Senior Software Engineer Code Generation in Magento 2 Legal Disclaimer Copyright © 2015 Magento, Inc. All Rights Reserved. Magento®, eBay Enterprise™ and their respective logos are trademarks, service marks, registered trademarks, or registered service marks of eBay, Inc. or its subsidiaries. Other trademarks or service marks contained in this presentation are the property of the respective companies with which they are associated. This presentation is for informational and discussion purposes only and should not be construed as a commitment of Magento, Inc. or eBay Enterprise (“eBay Enterprise”) or of any of their subsidiaries or affiliates. While we attempt to ensure the accuracy, completeness and adequacy of this presentation, neither Magento, Inc., eBay Enterprise nor any of their subsidiaries or affiliates are responsible for any errors or will be liable for the use of, or reliance upon, this presentation or any of the information contained in it. Unauthorized use, disclosure or dissemination of this information is expressly prohibited. Introduction to Code Generation Automatic programming – generation of computer program Source code generation Generation based on template Allows to write code at higher abstraction level Enables aspect-oriented programming (AOP) Enables generic programming – parameterization over types Avoids writing boilerplate code Code Generation in Magento 1.x Code Generation in Magento 2 Code Generation in Magento 2 Code is generated: On the fly (development) Autoload non-existing class that follows naming pattern Beforehand (production) Run CLI tools php var/generation/ dev/tools/Magento/Tools/Di/compiler.php Location of generated code: Factories Factory creates objects Single method – create() Used for non-injectables, i.e. entities Isolation from Object Manager Type safety IDE auto-completion Class name pattern: \Namespace\ClassFactory Factory Usage app/code/Magento/Catalog/Model/Product/Copier.php namespace Magento\Catalog\Model\Product; class Copier { public function __construct( \Magento\Catalog\Model\ProductFactory $productFactory ) { $this->productFactory = $productFactory; } public function copy(\Magento\Catalog\Model\Product $product) { $duplicate = $this->productFactory->create(); // ... } } Generated Factory (Simplified) var/generation/Magento/Catalog/Model/ProductFactory.php namespace Magento\Catalog\Model; class ProductFactory { public function __construct( \Magento\Framework\ObjectManagerInterface $objectManager ) { $this->objectManager = $objectManager; } } public function create(array $data = array()) { return $this->objectManager->create( '\\Magento\\Catalog\\Model\\Product', $data ); } Proxies Implementation of GoF pattern Follows interface of subject Delays creation of subject Delays creation of dependencies Forwards calls to subject Used for optional dependencies of DI Class name pattern: Proxy Usage in DI Config app/code/Magento/Catalog/etc/di.xml <config> <type name="Magento\Catalog\Model\Resource\Product\Collection"> <arguments> <argument name="customerSession" xsi:type="object"> Magento\Customer\Model\Session\Proxy </argument> </arguments> </type> </config> Generated Proxy (Simplified) var/generation/Magento/Customer/Model/Session/Proxy.php namespace Magento\Customer\Model\Session; class Proxy extends \Magento\Customer\Model\Session { protected function getSubject() { if (!$this->subject) { $this->subject = $this->objectManager->get( '\\Magento\\Customer\\Model\\Session' ); } return $this->subject; } public function getCustomerId() { return $this->getSubject()->getCustomerId(); } // ... } Interception Primary customization approach AOP-like mechanism Used for plugins Attach behavior to public methods Before After Around Plugin Implementation app/code/Magento/Store/App/Action/Plugin/StoreCheck.php namespace Magento\Store\App\Action\Plugin; class StoreCheck { public function aroundDispatch( \Magento\Framework\App\Action\Action $subject, \Closure $proceed, \Magento\Framework\App\RequestInterface $request ) { if (!$this->storeManager->getStore()->getIsActive()) { throw new \Magento\Framework\App\InitException( 'Current store is not active.' ); } return $proceed($request); } } Plugin Declaration in DI Config app/code/Magento/Store/etc/di.xml <config> <type name="Magento\Framework\App\Action\Action"> <plugin name="storeCheck" type="Magento\Store\App\Action\Plugin\StoreCheck" sortOrder="10"/> </type> </config> Generated Interceptor (Simplified) var/generation/Magento/Framework/App/Action/Action/Interceptor.php namespace Magento\Framework\App\Action\Action; class Interceptor extends \Magento\Framework\App\Action\Action { public function dispatch( \Magento\Framework\App\RequestInterface $request ) { $pluginInfo = $this->pluginList->getNext( '\\Magento\\Framework\\App\\Action\\Action', 'dispatch' ); if (!$pluginInfo) { return parent::dispatch($request); } else { return $this->___callPlugins( 'dispatch', func_get_args(), $pluginInfo ); } } } Code Generation for Service Layer Service layer – ultimate public API Services implement stateless operations Generated code: Repository* Persistor* Search Results Extension Attributes Generated Repository (Simplified) var/generation/Magento/Sales/Api/Data/Order/Repository.php namespace Magento\Sales\Api\Data\Order; class Repository implements \Magento\Sales\Api\OrderRepositoryInterface { public function __construct( \Magento\Sales\Api\Data\OrderInterfacePersistor $orderPersistor, \Magento\Sales\Api\Data\OrderSearchResultInterfaceFactory $searchResultFactory ) { $this->orderPersistor = $orderPersistor; $this->searchResultFactory = $searchResultFactory; } } public public public public public function function function function function get($id); create(\Magento\Sales\Api\Data\OrderInterface $entity); getList(\Magento\Framework\Api\SearchCriteria $criteria); remove(\Magento\Sales\Api\Data\OrderInterface $entity); flush(); Extension Attributes Extension to data interfaces from 3rd party modules Attributes declared in configuration Attribute getters/setters generated Type-safe attribute access IDE auto-completion Class name pattern: Declaration of Extension Attributes app/code/Magento/Bundle/etc/data_object.xml <config> <custom_attributes for="Magento\Catalog\Api\Data\ProductInterface"> <attribute code="price_type" type="integer" /> </custom_attributes> </config> Entity with Extension Attributes app/code/Magento/Catalog/Api/Data/ProductInterface.php namespace Magento\Catalog\Api\Data; interface ProductInterface extends \Magento\Framework\Api\CustomAttributesDataInterface { /** * @return \Magento\Catalog\Api\Data\ProductExtensionInterface|null */ public function getExtensionAttributes(); public function setExtensionAttributes( \Magento\Catalog\Api\Data\ProductExtensionInterface $attributes ); } // ... Generated Interface of Extension Attributes var/generation/Magento/Catalog/Api/Data/ProductExtensionInterface.php namespace Magento\Catalog\Api\Data; interface ProductExtensionInterface extends \Magento\Framework\Api\ExtensionAttributesInterface { /** * @return integer */ public function getPriceType(); /** * @param integer $priceType * @return $this */ public function setPriceType($priceType); } // ... Generated Implementation of Extension Attributes var/generation/Magento/Catalog/Api/Data/ProductExtension.php namespace Magento\Catalog\Api\Data; class ProductExtension extends \Magento\Framework\Api\AbstractSimpleObject implements \Magento\Catalog\Api\Data\ProductExtensionInterface { /** * @return integer */ public function getPriceType() { return $this->_get('price_type'); } /** * @param integer $priceType * @return $this */ public function setPriceType($priceType) { return $this->setData('price_type', $priceType); } } // ... Loggers Implementation of GoF pattern Decorator Activated with the profiler Object Manager wraps instances with loggers Tracks method call stack Forwards calls to original methods Class name pattern: \Namespace\Class\Logger Summary of Code Generation DI Factory Proxy Interception Service Layer Repository Persistor Search Results Extension Attributes Logger Thank you! Code Generation in Magento 2 Q&A Sergii Shymko [email protected] Resources github.com magento/magento2 – Magento 2 Community Edition magento/magento2-community-edition – Composer project for Magento 2 CE devdocs.magento.com Factories Proxies Plugins Definition compilation tool Andrey Konosov Architect, Magento 2 Caching Optimizations for Code Development Legal Disclaimer Copyright © 2015 Magento, Inc. All Rights Reserved. Magento®, eBay Enterprise™ and their respective logos are trademarks, service marks, registered trademarks, or registered service marks of eBay, Inc. or its subsidiaries. Other trademarks or service marks contained in this presentation are the property of the respective companies with which they are associated. This presentation is for informational and discussion purposes only and should not be construed as a commitment of Magento, Inc. or eBay Enterprise (“eBay Enterprise”) or of any of their subsidiaries or affiliates. While we attempt to ensure the accuracy, completeness and adequacy of this presentation, neither Magento, Inc., eBay Enterprise nor any of their subsidiaries or affiliates are responsible for any errors or will be liable for the use of, or reliance upon, this presentation or any of the information contained in it. Unauthorized use, disclosure or dissemination of this information is expressly prohibited. Cache Structure Caches Configuration DI Event Settings Layout Category Product CMS Block HTML Nav </> Menu Footer View Files Location Fallback Data Definition Language Integrity Index Tmp EAV Template Labels Page Cache CMS Category Product Translations De Fr En It Integrations OAuth Web Services SOAP REST WSDL Frontend Development > magento cache:flush Frontend Development Theme creation (theme.xml, bind to store) Layout modifications Overrides for static files Changes in existing images Less files pub static var cache page_cache Server-side Development Backend Development New blocks, models, templates Layout modifications New plugins, events Plugins modifications var cache page_cache generation Custom Cache class Custom extends \Magento\Framework\Cache\Frontend\Decorator\TagScope { const TYPE_IDENTIFIER = ‘my_custom_cache'; const CACHE_TAG = ‘MY_CUSTOM'; public function __construct(FrontendPool $cacheFrontendPool) { parent::__construct($cacheFrontendPool->get(self::TYPE_IDENTIFIER), self::CACHE_TAG); } } Custom Cache (cache.xml) <type name=“custom" translate="label,description" instance=“My\Cache"> <label>Custom cache</label> <description>This is my storage.</description> </type> Page Cache Page Cache EVERY PAGE IS CACHEABLE BY DEFAULT Page Cache Product CMS Address Public Category Name Private Cart Login Wishlist Thank you! Caching Optimizations for Code Development Q&A Andrey Konosov [email protected] Credits: Icons designed by Freepik
© Copyright 2024