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.
Legal Disclaimer
Low-level Linking
High-level Configuration
Admin Configuration
Customization Points
events.xml
routes.xml
cache.xml webapi.xml
layout
di.xml
Object B Object C Object A
Object B
acl.xml
Store Configuration
• In admin application: Stores->Configuration
• Values stored in Database
• Configured in system.xml
• Default values in config.xml
Composability
Split your code to lots of small
chunks and then compose them to
build your application
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
}
Application A Application B
Context Independence
Config Invoker
AlternativeConfig
EventManager
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. Single Responsibility – Lots of small objects
2. Open-Closed – Each object has multiple extension points
3. Liskov Substitution – Polymorphism for composability
4. Interface Segregation – Granularity
Module AB
Module B Module A
soliD
Coupled
Decoupled
Implementation
Module B
Module A Implementation Adapter
Interface
Module AB
Adapter
Module C
Implementation
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.
Extensions Compatibility Challenges
Interfaces may be changed in a new version
Extensions may depend on optional modules which is turned off
Extensions may depend on undocumented behavior
How to ensure that two extensions will be compatible in a new
version?
Challenges for Developer
How to understand what functionality of the extension is stable and
what is not?
How to implement extension in the way that it will keep backward
compatibility but can evolve?
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
*/
interface AuthorizationInterface
{
/**
* Check current user permission on resource and privilege
*
* @param string $resource
* @param string $privilege
* @return boolean
*/
public function isAllowed($resource, $privilege = null);
}
@api annotation identifies subset
of the methods with the stable APIs
Enforced by tools and static tests
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
Model
Resource Model
Client getData()
Repositories
Service Contracts
Models
Resource Models
Web API clients
M2 Module
Blocks Templates Controllers
Other M2 Modules
Services
Data Objects
Service Contracts
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
Repositories
Service Contracts
Models
Resource Models
M2 Module
Blocks Templates Controllers
Services
Data Objects
Service Contracts Interfaces
Service Contracts
Service
Interfaces Data Interface
Data interfaces
Defines data structures, used as input and
output types of the business operations
Examples: Customer, Product, Region,
Currency, etc.
Service interfaces
Defines business operations
Examples: load, delete, save, change
password, etc.
They are just PHP Interfaces
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
Models
Data Objects
Data Interfaces
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 third-
party module
Added via xml configuration, generated as an object
Review fields
Catalog Inventory fields
Rating and Reviews Module Catalog
Inventory Module
Product Data Interface
Product fields
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
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.
Legal Disclaimer
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 2
Code is generated:
On the fly (development) Autoload non-existing class that follows naming pattern
Beforehand (production) Run CLI tools php
dev/tools/Magento/Tools/Di/compiler.php
Location of generated code:
var/generation/
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
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();
// ...
}
}
app/code/Magento/Catalog/Model/Product/Copier.php
Generated Factory (Simplified)
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
); } }
var/generation/Magento/Catalog/Model/ProductFactory.php
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: \Namespace\Class\Proxy
Proxy Usage in DI Config
<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>
app/code/Magento/Catalog/etc/di.xml
Generated Proxy (Simplified)
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(); }
// ...
}
var/generation/Magento/Customer/Model/Session/Proxy.php
Interception
Primary customization approach
AOP-like mechanism
Used for plugins
Attach behavior to public methods
Before
After
Around
Plugins declared in DI config
Plugin Implementation
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);
}
}
app/code/Magento/Store/App/Action/Plugin/StoreCheck.php
Plugin Declaration in DI Config
<config>
<type name="Magento\Framework\App\Action\Action">
<plugin name="storeCheck"
type="Magento\Store\App\Action\Plugin\StoreCheck"
sortOrder="10"/>
</type>
</config>
app/code/Magento/Store/etc/di.xml
Generated Interceptor (Simplified)
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
); } } }
var/generation/Magento/Framework/App/Action/Action/Interceptor.php
Code Generation for Service Layer
Service layer – ultimate public API
Services implement stateless operations
Generated code:
Repository*
Persistor*
Search Results
Extension Attributes
* – may be removed in future releases
Generated Repository (Simplified)
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 function get($id); public function create(\Magento\Sales\Api\Data\OrderInterface $entity); public function getList(\Magento\Framework\Api\SearchCriteria $criteria); public function remove(\Magento\Sales\Api\Data\OrderInterface $entity); public function flush(); }
var/generation/Magento/Sales/Api/Data/Order/Repository.php
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: \Namespace\ClassExtensionInterf
ace
\Namespace\ClassExtension
Declaration of Extension Attributes
<config>
<custom_attributes for="Magento\Catalog\Api\Data\ProductInterface">
<attribute code="price_type" type="integer" />
</custom_attributes>
</config>
app/code/Magento/Bundle/etc/data_object.xml
Entity with Extension Attributes
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
);
// ... }
app/code/Magento/Catalog/Api/Data/ProductInterface.php
Generated Interface of Extension Attributes
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); // ... }
var/generation/Magento/Catalog/Api/Data/ProductExtensionInterface.php
Generated Implementation of Extension
Attributes
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); }
// ... }
var/generation/Magento/Catalog/Api/Data/ProductExtension.php
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
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
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.
Frontend Development
Theme creation (theme.xml, bind to store)
Layout modifications
Less files
Changes in existing images
Overrides for static files
pub
static
var
cache
page_cache
Backend Development
New blocks, models, templates
Layout modifications
Plugins modifications
New plugins, events
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>
Thank you!
Credits: Icons designed by Freepik
Andrey Konosov [email protected]
Q & A
Caching Optimizations for Code Development