Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Magento 2 - An Intro to a Modern PHP-Based System - Northeast PHP 2015

Magento 2 - An Intro to a Modern PHP-Based System - Northeast PHP 2015

Over 200,000 companies use the Magento 1 platform to power their eCommerce needs. So when they set out to build a major new version, the Magento team had significant pressure to deliver a modern, well-designed PHP-based system. Pulling in some of the best of the PHP world through tools like Composer, phpunit and more, I believe they met that goal. In this talk, we’ll take a look at the design and architecture of Magento 2, including it’s use of dependency injections, interceptors and service contracts to provide numerous ways for developers to extend and customize the system.

Presented at Northeast PHP 2015

8a2e3b7e9a3e4037934e5680e16e53e1?s=128

Joshua Warren

August 23, 2015
Tweet

Transcript

  1. PRESENTED BY JOSHUA WARREN PRESENTED AT NORTHEAST PHP 2015 Magento

    2 AN INTRODUCTION TO A MODERN PHP- BASED SYSTEM
  2. MY EXPERIENCE

  3. JoshuaWarren.com My Experience PHP Developer Since 1999 Founded Creatuity in

    2008 Focused on the Magento platform Magento 2 contributor #NEPHP
  4. JoshuaWarren.com early adopter of both Magento 1 and Magento 2

    #NEPHP
  5. JoshuaWarren.com Frequent Magento presenter #NEPHP

  6. JoshuaWarren.com Active member of the #RealMagento community #NEPHP

  7. JoshuaWarren.com Involved in feedback and design discussions throughout the Magento

    2 Developer Beta #NEPHP
  8. JoshuaWarren.com Wrote Writing the book on Magento 2 #NEPHP

  9. A BRIEF HISTORY OF MAGENTO Photo courtesy of @YoavKutner

  10. JoshuaWarren.com Magento 1 development began in 2007 by Varien, a

    PHP development agency. #NEPHP
  11. JoshuaWarren.com In 2007, osCommerce was state of the art. #NEPHP

  12. JoshuaWarren.com Cloud-based eCommerce systems didn’t exist. #NEPHP

  13. JoshuaWarren.com PHP 5.2 was cutting-edge. #NEPHP

  14. JoshuaWarren.com Composer didn’t exist, and Zend Framework 1 was still

    in early beta. #NEPHP
  15. JoshuaWarren.com Magento 1 was built to resolve the pain points

    of osCommerce. #NEPHP
  16. JoshuaWarren.com Designed to be more flexible and to provide standardized

    ways to customize the platform. #NEPHP
  17. JoshuaWarren.com By 2010, Magento had been downloaded 1.5 million times.

    #NEPHP
  18. JoshuaWarren.com Attracted by Magento’s free, open- source approach, hundreds of

    thousands of sites were launched using Magento 1. #NEPHP
  19. JoshuaWarren.com There’s just one problem… #NEPHP

  20. JoshuaWarren.com Ecommerce development is hard. #NEPHP

  21. None
  22. JoshuaWarren.com Lightly documented ecommerce development is even harder. #NEPHP

  23. None
  24. JoshuaWarren.com Varien, now known as Magento Inc, is acquired by

    eBay in 2011. #NEPHP
  25. JoshuaWarren.com Work begins on Magento 2 almost immediately. #NEPHP

  26. JoshuaWarren.com Now the most widely-used eCommerce platform, powering over 250,000

    sites, expectations are high for the Magento 2 team. #NEPHP
  27. JoshuaWarren.com Fewer than 30 commits are made to Magento 2

    in 2012. #NEPHP
  28. JoshuaWarren.com Internal priorities continue to shift, and finally at the

    end of 2014, Magento 2 development is made public on Github. #NEPHP
  29. JoshuaWarren.com Magento commits to a release schedule for Magento 2,

    and announces the acceptance of pull requests. #NEPHP
  30. JoshuaWarren.com devdocs.magento.com launches with significant documentation of Magento 2. #NEPHP

  31. JoshuaWarren.com Developer Beta is released in December 2014; Merchant Beta

    in July 2015 #NEPHP
  32. JoshuaWarren.com Production-ready release (‘general availability’) scheduled for Q4 2015 #NEPHP

  33. MAGENTO 2 github.com/magento/magento2

  34. JoshuaWarren.com Feature parity with Magento 1 + UI/UX Improvements +

    focus on resolving technical debt #NEPHP
  35. JoshuaWarren.com Technologies #NEPHP

  36. JoshuaWarren.com #NEPHP Composer composer create-project magento/product-community-edition --stability="beta" <installation directory name>

  37. JoshuaWarren.com Each Magento 2 module is a separate Composer package

    #NEPHP
  38. JoshuaWarren.com PSR-0 thru PSR-4 #NEPHP

  39. JoshuaWarren.com Testing built in from the start. phpunit, selenium, JMeter,

    Jasmine #NEPHP
  40. JoshuaWarren.com magento2/dev/tests/ #NEPHP

  41. JoshuaWarren.com HTML5, CSS3, LESS CSS Preprocessor, JQuery, RequireJS #NEPHP

  42. JoshuaWarren.com Components from Zend Framework 1, Zend Framework 2, Symfony

    #NEPHP
  43. JoshuaWarren.com Technical Architecture #NEPHP

  44. JoshuaWarren.com Presentation Layer, Service Layer, Domain Layer, Persistence Layer #NEPHP

  45. JoshuaWarren.com #NEPHP

  46. JoshuaWarren.com Presentation Layer - views, literally and figuratively #NEPHP

  47. JoshuaWarren.com Service Layer - an intermediary between the presentation and

    model layers #NEPHP
  48. JoshuaWarren.com Service layer provides a stable, backwards-compatible interface and forms

    the foundation for dependency injection. #NEPHP
  49. JoshuaWarren.com Domain layer - business logic, including models. Contains the

    implementation of service contracts. #NEPHP
  50. JoshuaWarren.com Persistence Layer - resource models that perform CRUD operations

    on database tables. #NEPHP
  51. JoshuaWarren.com Some models use a single table, others continue to

    use the Entity-Attribute-Value design pattern used in Magento 1. #NEPHP
  52. JoshuaWarren.com Design Patterns #NEPHP

  53. JoshuaWarren.com Loose Coupling #NEPHP

  54. JoshuaWarren.com Dependency Injection #NEPHP

  55. JoshuaWarren.com Service Contracts #NEPHP

  56. JoshuaWarren.com Interceptors #NEPHP

  57. JoshuaWarren.com Semantic Versioning #NEPHP

  58. DEPENDENCY INJECTION Sorry - no cool photo here, because I

    don’t like needles…
  59. JoshuaWarren.com DI is exactly what it sounds like - injecting

    dependencies into the objects that need them. #NEPHP
  60. JoshuaWarren.com DI is designed to reduce dependencies and promote loose

    coupling #NEPHP
  61. JoshuaWarren.com DI makes unit testing much easier #NEPHP

  62. JoshuaWarren.com Magento 2 uses the Constructor Injection pattern of DI

    #NEPHP
  63. JoshuaWarren.com DI in Magento 2 is handled via XML files

    #NEPHP
  64. JoshuaWarren.com #NEPHP di.xml <config xmlns:xsi=“[…]” xsi:noNamespaceSchemaLocation=“[…]”>
 <virtualType name="Magento\SamplePaymentProvider\Block\Form\Payinstore" type="Magento\Payment\Block\Form" shared="false">

    <arguments>
 <argument name="data" xsi:type="array">
 <item name="template" xsi:type=“string"> Magento_SamplePaymentProvider::form/payinstore.phtml </item>
 </argument>
 </arguments>
 </virtualType>
 </config>
  65. INTERCEPTORS

  66. JoshuaWarren.com Plugin system based on the interceptor pattern #NEPHP

  67. JoshuaWarren.com Calls to almost any module can be intercepted and

    altered #NEPHP
  68. JoshuaWarren.com Vast improvement over the rewrite pattern in Magento 1

    - no more rewrite conflicts #NEPHP
  69. JoshuaWarren.com #NEPHP di.xml <config>
 <type name="{ObservedType}">
 <plugin name="{pluginName}" type="{PluginClassName}" sortOrder="1"

    disabled="false"/>
 </type>
 </config>
  70. JoshuaWarren.com Sort order defines order if multiple plugins intercept the

    same item #NEPHP
  71. JoshuaWarren.com Possible to intercept before, after and around a function

    #NEPHP
  72. JoshuaWarren.com #NEPHP ‘Before’ Interceptor class Plugin
 {
 public function beforeSetName(\Magento\Catalog\Model\Product

    $subject, $name)
 {
 return array('(' . $name . ')');
 }
 }
  73. JoshuaWarren.com #NEPHP ‘After’ Interceptor class Plugin
 {
 public function afterGetName(\Magento\Catalog\Model\Product

    $subject, $result)
 {
 return '|' . $result . '|';
 }
 }
  74. JoshuaWarren.com #NEPHP ‘Around’ Interceptor class Plugin
 {
 public function aroundSave(\Magento\Catalog\Model\Product

    $subject, \Closure $proceed)
 {
 $this->doSomethingBeforeProductIsSaved();
 $returnValue = $proceed();
 if ($returnValue) {
 $this->postProductToFacebook();
 }
 return $returnValue;
 }
 }
  75. SERVICE CONTRACTS Credit to Allan MacGregor for the Soylent Green

    joke.
  76. JoshuaWarren.com Set of interfaces to define the public API of

    a module #NEPHP
  77. JoshuaWarren.com This API is the interface provided to other modules

    to access its implementation #NEPHP
  78. JoshuaWarren.com Designed to hide business logic behind a stable interface

    #NEPHP
  79. JoshuaWarren.com Service contracts + semantic versioning = minor releases will

    not break existing code #NEPHP
  80. JoshuaWarren.com @deprecated = will be removed with the next major

    version release #NEPHP
  81. JoshuaWarren.com #NEPHP CustomerRepositoryInterface.php 
 namespace Magento\Customer\Api;
 /**
 * Customer CRUD

    interface.
 */
 interface CustomerRepositoryInterface
 {
 /**
 * Create customer.
 *
 * @api
 * @param \Magento\Customer\Api\Data\CustomerInterface $customer
 * @param string $passwordHash
 * @return \Magento\Customer\Api\Data\CustomerInterface
 * @throws \Magento\Framework\Exception\InputException If bad input is provided
 * @throws \Magento\Framework\Exception\State\InputMismatchException If the provided email is already used
 * @throws \Magento\Framework\Exception\LocalizedException
 */
 public function save(\Magento\Customer\Api\Data\CustomerInterface $customer, $passwordHash = null);
  82. JoshuaWarren.com #NEPHP CustomerRepositoryInterface.php /**
 * Retrieve customer.
 *
 * @api


    * @param string $email
 * @param int|null $websiteId
 * @return \Magento\Customer\Api\Data\CustomerInterface
 * @throws \Magento\Framework\Exception\NoSuchEntityException If customer with the specified email does not exist.
 * @throws \Magento\Framework\Exception\LocalizedException
 */
 public function get($email, $websiteId = null);
 /**
 * Retrieve customer.
 *
 * @api
 * @param int $customerId
 * @return \Magento\Customer\Api\Data\CustomerInterface
 * @throws \Magento\Framework\Exception\NoSuchEntityException If customer with the specified ID does not exist.
 * @throws \Magento\Framework\Exception\LocalizedException
 */
 public function getById($customerId);

  83. JoshuaWarren.com Service Contracts include Data Interfaces and Service Interfaces #NEPHP

  84. JoshuaWarren.com Data Interfaces return information about data entities #NEPHP

  85. JoshuaWarren.com Service Interfaces handle business logic #NEPHP

  86. JoshuaWarren.com Three types of service interfaces in Magento 2 (so

    far) #NEPHP
  87. JoshuaWarren.com Repository Interfaces provide access to persistent data entities #NEPHP

  88. JoshuaWarren.com CustomerRepositoryInterface, AddressRepositoryInterface, etc. #NEPHP

  89. JoshuaWarren.com Repository interfaces contain the CRUD operations #NEPHP

  90. JoshuaWarren.com Management interfaces contain management functions not related to repositories

    #NEPHP
  91. JoshuaWarren.com Validators, createAccount, changePassword, etc #NEPHP

  92. JoshuaWarren.com Metadata interfaces provide meta information - primarily about custom

    attributes #NEPHP
  93. EXTENDING MAGENTO 2

  94. JoshuaWarren.com Create your basic module file structure #NEPHP

  95. JoshuaWarren.com #NEPHP App/Code/<Vendor>/<Module>/ composer.json etc/module.xml Test/Unit/<tests go here>

  96. JoshuaWarren.com #NEPHP Composer.json { "name": "joshuaswarren/sample-module-minimal", "description": "A minimal sample

    Magento 2 module", "type": "magento2-module", "version": "1.0.0", "license": [ "OSL-3.0", "AFL-3.0" ], "require": { "php": "~5.5.0|~5.6.0", "magento/magento-composer-installer": "*" }, "extra": { "map": [ [ "*", "joshuaswarren/SampleMinimal" ] ] } }
  97. JoshuaWarren.com #NEPHP etc/module.xml <config> <module name=“Joshuaswarren_SampleMinimal" setup_version="2.0.0"> </module> </config>

  98. JoshuaWarren.com Optional config files in etc: acl.xml config.xml di.xml webapi.xml

    #NEPHP
  99. JoshuaWarren.com acl.xml defines new items for Magento’s ACL system #NEPHP

  100. JoshuaWarren.com Config.xml adds new configuration options #NEPHP

  101. JoshuaWarren.com Webapi.xml defines items to expose via the REST or

    SOAP APIs #NEPHP
  102. JoshuaWarren.com Optional subdirectories in etc: adminhtml frontend webapi_rest webapi_soap #NEPHP

  103. JoshuaWarren.com Items in the main etc directory apply globally to

    your extension #NEPHP
  104. JoshuaWarren.com Items in the 4 subdirectories apply only to that

    area - i.e., adminhtml only applies to the Magento backend #NEPHP
  105. JoshuaWarren.com Optional subdirectories: API Block Controller Helper Model #NEPHP

  106. JoshuaWarren.com Optional subdirectories: Plugin Setup Ui i18n view #NEPHP

  107. JoshuaWarren.com API contains any new service contracts your extension adds

    #NEPHP
  108. JoshuaWarren.com Block contains any new template blocks your extension adds

    #NEPHP
  109. JoshuaWarren.com Controller contains your extension’s controllers #NEPHP

  110. JoshuaWarren.com Helper contains any helper functions that your extension needs

    #NEPHP
  111. JoshuaWarren.com Model contains your extension’s models #NEPHP

  112. JoshuaWarren.com Plugin contains any interceptors your extension defines #NEPHP

  113. JoshuaWarren.com Setup contains your database migrations using Magento’s setup script

    system #NEPHP
  114. JoshuaWarren.com UI is for Magento 2’s new Magento UI library

    #NEPHP
  115. JoshuaWarren.com I18n contains internationalization files - CSV files defining the

    translations for your strings #NEPHP
  116. JoshuaWarren.com View contains the views for your extension #NEPHP

  117. JoshuaWarren.com If your extension doesn’t need one of these items,

    just omit that folder #NEPHP
  118. JoshuaWarren.com Sample: custom shipping method to allow for in-store pickup

    from several locations #NEPHP
  119. JoshuaWarren.com #NEPHP Block/System/Config/Form/Field/Locations.php namespace Magento\SampleShippingProvider\Block\System\Config\Form\Field;
 use Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray;
 /**
 * Backend

    system config array field renderer
 */
 class Locations extends AbstractFieldArray
 {
 /**
 * Initialise columns for 'Store Locations'
 *
 * @return void
 */
 protected function _construct()
 {
 $this->addColumn('title',
 ['label' => __('Title'), 'class' => 'validate-no-empty validate-alphanum-with-spaces']);
 $this->addColumn('street',
 ['label' => __('Street Address'), 'class' => 'validate-no-empty validate-alphanum-with-spaces']);
 $this->addColumn('phone',
 ['label' => __('Phone Number'), 'class' => 'validate-no-empty validate-no-empty validate-phoneStrict']);
 $this->addColumn('message',
 ['label' => __('Message'), 'class' => 'validate-no-empty']);
 $this->_addAfter = false;
 parent::_construct();
 }
 }
  120. JoshuaWarren.com #NEPHP Model/Type/Plugin/Onepage.php [1/2] namespace Magento\SampleShippingProvider\Model\Type\Plugin;
 use Magento\Checkout\Model\Type\Onepage as CheckoutOnePage;


    use Magento\SampleShippingProvider\Model\Carrier;
 /**
 * Change Shipping Address to selected Store location address
 */
 class Onepage
 {
 /**
 * @var Carrier
 */
 private $carrier;
 /**
 * @param Carrier $carrier
 */
 public function __construct(Carrier $carrier)
 {
 $this->carrier = $carrier;
 }

  121. JoshuaWarren.com #NEPHP Model/Type/Plugin/Onepage.php [2/2] /**
 * Replace shipping address with

    pickup location address
 * @param CheckoutOnePage $subject
 * @param array $result
 * @return $this
 */
 public function afterSaveShippingMethod(CheckoutOnePage $subject, array $result)
 {
 if ($result) {
 return $result;
 }
 $quote = $subject->getQuote();
 $shippingAddress = $quote->getShippingAddress();
 $shippingMethod = $shippingAddress->getShippingMethod();
 /**
 * In-Store pickup selected
 * Update Shipping Address
 */
 if (strpos($shippingMethod, $this->carrier->getCarrierCode()) !== false) {
 $locationAddress = $this->carrier->getLocationInfo($shippingMethod);
 $shippingAddress->setCountryId($locationAddress['country_id']);
 $shippingAddress->setRegionId($locationAddress['region_id']);
 $shippingAddress->setPostcode($locationAddress['postcode']);
 $shippingAddress->setCity($locationAddress['city']);
 $shippingAddress->setStreet($locationAddress['street']);
 $shippingAddress->setTelephone($locationAddress['phone']);
 }
 return $result;
 }
 }
  122. JoshuaWarren.com #NEPHP Model/Carrier.php namespace Magento\SampleShippingProvider\Model;
 use Psr\Log\LoggerInterface;
 use Magento\Framework\App\Config\ScopeConfigInterface;
 use

    Magento\Store\Model\ScopeInterface;
 use Magento\Shipping\Model\Carrier\AbstractCarrier;
 use Magento\Shipping\Model\Carrier\CarrierInterface;
 use Magento\Shipping\Model\Config;
 use Magento\Shipping\Model\Rate\ResultFactory;
 use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory;
 use Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory;
 /**
 * In-Store Pickup shipping model
 */
 class Carrier extends AbstractCarrier implements CarrierInterface
 {
 /**
 * @var string
 */
 protected $_code = 'storepickup';
 /**
 * @var bool
 */
 protected $_isFixed = true; … see https://github.com/magento/magento2-samples/blob/master/sample-module-shipping-provider/Model/Carrier.php 

  123. JoshuaWarren.com In model/carrier.php we implement all of the functions a

    shipping carrier must have in Magento 2 #NEPHP
  124. JoshuaWarren.com GetAllowedMethods CollectRates GetLocationInfo BuildRateForLocation GetLocations GetShippingOrigin #NEPHP

  125. JoshuaWarren.com #NEPHP Etc/Adminhtml/System.xml <system>
 <section id="carriers">
 <group id="storepickup" translate="label" type="text"

    sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
 <label>In-Store Pickup</label>
 <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
 <label>Enabled</label>
 <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
 <comment>
 <![CDATA[<strong style="color:red">Warning</strong>: Shipping Origin should be configured to use this method.]]>
 </comment>
 </field>
 <field id="title" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1">
 <label>Title</label>
 </field>
 […]
  126. JoshuaWarren.com #NEPHP Etc/Frontend/di.xml <config xmlns:xsi=“[…]” xsi:noNamespaceSchemaLocation=“[…]”>
 <type name="Magento\SampleShippingProvider\Model\Type\Plugin\Onepage"/>
 <type name="Magento\Checkout\Model\Type\Onepage">


    <plugin name="change_shipping_address" type="Magento\SampleShippingProvider\Model\Type\Plugin\Onepage"/>
 </type>
 </config>
  127. JoshuaWarren.com #NEPHP Etc/Config.xml <config xmlns:xsi=“[…]” xsi:noNamespaceSchemaLocation=“[…]”>
 <default>
 <carriers>
 <storepickup>
 <active>1</active>


    <model>Magento\SampleShippingProvider\Model\Carrier</model>
 <title>In-Store Pickup</title>
 <specificerrmsg>This shipping method is not available.</specificerrmsg>
 </storepickup>
 </carriers>
 </default>
 </config>
  128. JoshuaWarren.com #NEPHP Etc/Module.xml <config xmlns:xsi=“[…]” xsi:noNamespaceSchemaLocation=“[…]”>
 <module name="Magento_SampleShippingProvider" setup_version="2.0.0">
 </module>


    </config>
  129. JoshuaWarren.com #NEPHP Composer.json { "name": "magento/sample-module-shipping-provider", "description": "Demonstrate Shipping Provider",

    "type": "magento2-module", "version": "1.0.0", "license": [ "OSL-3.0", "AFL-3.0" ], "require": { "php": "~5.5.0|~5.6.0", "magento/magento-composer-installer": "*", "magento/framework": "~0.74", "magento/module-store": "~0.74", "magento/module-shipping": "~0.74", "magento/module-quote": "~0.74", "magento/module-checkout": "~0.74" }, "extra": { "map": [ [ "*", "Magento/SampleShippingProvider" ] ] } }
  130. JoshuaWarren.com We now have a complete, functioning in-store-pickup shipping extension

    for Magento 2 #NEPHP
  131. JoshuaWarren.com Take a look at the Test/Unit directory on Github

    for tests for this extension #NEPHP
  132. JoshuaWarren.com Magento 2 unit testing includes mocking, fixtures, etc. -

    everything you need for TDD #NEPHP
  133. LEARNING MORE Don’t end up like this guy ->

  134. JoshuaWarren.com devdocs.magento.com magento.stackexchange.com/questions/tagged/magento2 #NEPHP

  135. JoshuaWarren.com AlanStorm.com AlanKent.me CoderOnCode.com #NEPHP

  136. JoshuaWarren.com Upcoming events: Meet Magento New York, ZendCon, php[world] #NEPHP

  137. JoshuaWarren.com Programming With Magento 2 coming to amazon.com & phparch.com

    #NEPHP
  138. Keep in Touch! joind.in/14737 @JoshuaSWarren JoshuaWarren.com Mage2DevBook.com

  139. JoshuaWarren.com #NEPHP