Pro Yearly is on sale from $80 to $50! »

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

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

Newly updated version of my Magento 2 intro talk - make sure to refer to this one, not the older ones!

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 ZendCon 2015.

8a2e3b7e9a3e4037934e5680e16e53e1?s=128

Joshua Warren

October 21, 2015
Tweet

Transcript

  1. PRESENTED BY JOSHUA WARREN PRESENTED AT ZENDCON 2015 Magento 2

    AN INTRODUCTION TO A MODERN PHP- BASED SYSTEM
  2. None
  3. JoshuaWarren.com 150 slides in ~50 minutes lots of code focus

    on the concepts, download the slides later #ZendCon
  4. MY EXPERIENCE

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

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

    #ZendCon
  7. JoshuaWarren.com Frequent Magento presenter #ZendCon

  8. JoshuaWarren.com Led the Creatuity team in building 3 Magento 2

    extensions (more on the way!) #ZendCon
  9. JoshuaWarren.com Already migrating a few merchants from Magento 1 to

    Magento 2 #ZendCon
  10. JoshuaWarren.com Wrote Writing the book on Magento 2 #ZendCon

  11. JoshuaWarren.com Not a Magento employee, but working closely with the

    development, documentation & product teams on Magento 2 #ZendCon
  12. A BRIEF HISTORY OF MAGENTO Photo courtesy of @YoavKutner

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

    PHP development agency. #ZendCon
  14. JoshuaWarren.com A look at the state of the art when

    Magento 1 was built PHP & Ecommerce in 2007 osCommerce is state of the art Cloud-based / SaaS ecommerce doesn’t exist yet PHP 5.2 is cutting-edge Composer doesn’t exist - PHP has no dependency management There’s this new thing called ZF, otherwise it’s Cake or Symfony Testing? You might find a few people using PHPUnit #ZendCon
  15. JoshuaWarren.com The Painful World of osCommerce PHP & Ecommerce in

    2007 Want to add an attribute? Modify your database schema on live. Looking at add new features? Modify core PHP files. Want to upgrade your store? Good luck! #ZendCon
  16. JoshuaWarren.com Magento 1 was built to resolve the pain points

    of osCommerce. #ZendCon
  17. JoshuaWarren.com Designed to be more flexible and to provide standardized

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

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

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

  21. JoshuaWarren.com Ecommerce development is hard. #ZendCon

  22. None
  23. JoshuaWarren.com Lightly documented ecommerce development is even harder. #ZendCon

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

    eBay in 2011. #ZendCon
  26. JoshuaWarren.com Work begins on Magento 2 almost immediately. #ZendCon

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

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

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

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

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

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

    in July 2015 #ZendCon
  33. JoshuaWarren.com Production-ready release scheduled for Q4 2015 #ZendCon

  34. Timely Twitter Question! JoshuaWarren.com #ZendCon

  35. JoshuaWarren.com Magento 2 team has committed to quarterly releases. I’m

    targeting the Q1 2016 release for more cautious brands. #ZendCon
  36. Timely Twitter Answer about Q1 Release JoshuaWarren.com #ZendCon

  37. JoshuaWarren.com A quick note for any Magento 1 developers in

    the audience… #ZendCon
  38. JoshuaWarren.com #ZendCon

  39. JoshuaWarren.com …for PHP developers new to Magento. #ZendCon

  40. JoshuaWarren.com PHP developers are learning Magento 2 faster than many

    Magento 1 developers. #ZendCon
  41. JoshuaWarren.com Approach Magento 2 with a desire to learn and

    understand the underlying patterns. #ZendCon
  42. JoshuaWarren.com Don’t approach Magento 2 with the thought “how do

    I make my Magento 1 code work here” #ZendCon
  43. JoshuaWarren.com With that mind, let’s dive into Magento 2… #ZendCon

  44. MAGENTO 2 github.com/magento/magento2

  45. JoshuaWarren.com Technologies #ZendCon

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

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

    #ZendCon
  48. JoshuaWarren.com PSR-0 thru PSR-4 #ZendCon

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

    Jasmine #ZendCon
  50. JoshuaWarren.com HTML5, CSS3, LESS CSS Preprocessor, JQuery, RequireJS #ZendCon

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

    #ZendCon
  52. JoshuaWarren.com Technical Architecture #ZendCon

  53. JoshuaWarren.com Presentation Layer, Service Layer, Domain Layer, Persistence Layer #ZendCon

  54. JoshuaWarren.com #ZendCon

  55. JoshuaWarren.com Presentation Layer - views, literally and figuratively #ZendCon

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

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

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

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

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

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

  62. JoshuaWarren.com Loose Coupling #ZendCon

  63. JoshuaWarren.com Dependency Injection #ZendCon

  64. JoshuaWarren.com Service Contracts #ZendCon

  65. JoshuaWarren.com Interceptors #ZendCon

  66. JoshuaWarren.com Semantic Versioning #ZendCon

  67. JoshuaWarren.com Start your Magento 2 journey learning the basics of

    these design patterns #ZendCon
  68. DEPENDENCY INJECTION Sorry - no cool photo here, because I

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

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

    coupling #ZendCon
  71. JoshuaWarren.com DI makes unit testing much easier #ZendCon

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

    #ZendCon
  73. JoshuaWarren.com DI in Magento 2 is handled via XML files

    #ZendCon
  74. JoshuaWarren.com #ZendCon 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>
  75. INTERCEPTORS

  76. JoshuaWarren.com Plugin system based on the interceptor pattern #ZendCon

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

    altered #ZendCon
  78. JoshuaWarren.com Vast improvement over the rewrite pattern in Magento 1

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

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

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

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

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

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

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

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

    a module #ZendCon
  87. JoshuaWarren.com This API is the interface provided to other modules

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

    #ZendCon
  89. JoshuaWarren.com Service contracts + semantic versioning = minor releases will

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

    version release #ZendCon
  91. JoshuaWarren.com #ZendCon 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);
  92. JoshuaWarren.com #ZendCon 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);

  93. JoshuaWarren.com Service Contracts include Data Interfaces and Service Interfaces #ZendCon

  94. JoshuaWarren.com Data Interfaces return information about data entities #ZendCon

  95. JoshuaWarren.com Service Interfaces handle business logic #ZendCon

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

    far) #ZendCon
  97. JoshuaWarren.com Repository Interfaces provide access to persistent data entities #ZendCon

  98. JoshuaWarren.com CustomerRepositoryInterface, AddressRepositoryInterface, etc. #ZendCon

  99. JoshuaWarren.com Repository interfaces contain the CRUD operations #ZendCon

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

    #ZendCon
  101. JoshuaWarren.com Validators, createAccount, changePassword, etc #ZendCon

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

    attributes #ZendCon
  103. JoshuaWarren.com Most service contracts are in the Api folder -

    but not all. There are SPIs annotated with @Api but in the Model folder, not Api #ZendCon
  104. EXTENDING MAGENTO 2

  105. JoshuaWarren.com Check out the Magento 2 sample extensions - 12

    official samples, including 1 theme #ZendCon
  106. JoshuaWarren.com Create your basic module file structure #ZendCon

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

  108. JoshuaWarren.com #ZendCon 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" ] ] } }
  109. JoshuaWarren.com #ZendCon etc/module.xml <config> <module name=“Joshuaswarren_SampleMinimal" setup_version="2.0.0"> </module> </config>

  110. JoshuaWarren.com #ZendCon NEW! Registration.php As of last week, Magento 2

    modules need a registration.php file - this allows modules to live in vendor directory
  111. JoshuaWarren.com #ZendCon NEW! Registration.php Thanks to Mage2.Pro for this reusable

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

    #ZendCon
  113. JoshuaWarren.com acl.xml defines new items for Magento’s ACL system #ZendCon

  114. JoshuaWarren.com Config.xml adds new configuration options #ZendCon

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

    SOAP APIs #ZendCon
  116. JoshuaWarren.com Optional subdirectories in etc: adminhtml frontend webapi_rest webapi_soap #ZendCon

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

    your extension #ZendCon
  118. JoshuaWarren.com Items in the 4 subdirectories apply only to that

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

  120. JoshuaWarren.com Optional subdirectories: Plugin Setup Ui i18n view #ZendCon

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

    #ZendCon
  122. JoshuaWarren.com Plugin contains any interceptors your extension defines #ZendCon

  123. JoshuaWarren.com UI is for Magento 2’s new Magento UI library

    #ZendCon
  124. JoshuaWarren.com I18n contains internationalization files - CSV files defining the

    translations for your strings #ZendCon
  125. JoshuaWarren.com If your extension doesn’t need one of these items,

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

    from several locations #ZendCon
  127. JoshuaWarren.com #ZendCon 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();
 }
 }
  128. JoshuaWarren.com #ZendCon 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;
 }

  129. JoshuaWarren.com #ZendCon 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;
 }
 }
  130. JoshuaWarren.com #ZendCon 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 

  131. JoshuaWarren.com model/carrier.php contains all of the functions a shipping carrier

    must have in Magento 2 #ZendCon
  132. JoshuaWarren.com GetAllowedMethods CollectRates GetLocationInfo BuildRateForLocation GetLocations GetShippingOrigin #ZendCon

  133. JoshuaWarren.com #ZendCon 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>
 […]
  134. JoshuaWarren.com #ZendCon 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>
  135. JoshuaWarren.com #ZendCon Etc/Module.xml <config xmlns:xsi=“[…]” xsi:noNamespaceSchemaLocation=“[…]”>
 <module name="Magento_SampleShippingProvider" setup_version="2.0.0">
 </module>


    </config>
  136. JoshuaWarren.com #ZendCon 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" ] ] } }
  137. JoshuaWarren.com We now have a complete, functioning in-store-pickup shipping extension

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

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

    everything you need for TDD #ZendCon
  140. JoshuaWarren.com Dev Docs includes a great ‘How Do I’ on

    the Magento Testing Framework and testing in Magento 2 #ZendCon
  141. LEARNING MORE Don’t end up like this guy ->

  142. JoshuaWarren.com devdocs.magento.com - ‘How Do I?’ magento.stackexchange.com/questions/tagged/magento2 https://github.com/magento/magento2-samples #ZendCon

  143. Dev Docs Team of hard-working technical writers (not developers) Writing

    documentation for a system that has yet to be used ‘in the wild’ Very eager for feedback and input - they don’t know what documentation you need Very open to pull requests of documentation or just open an issue on Github with feedback + requests JoshuaWarren.com #ZendCon
  144. JoshuaWarren.com Magento U Courses Fundamentals of Magento 2 Development Front-end

    Course #ZendCon
  145. JoshuaWarren.com AlanStorm.com AlanKent.me CoderOnCode.com github.com/creatuity/LearningMagento2 #ZendCon

  146. JoshuaWarren.com Upcoming events: php[world] - November 2015 Magento Imagine -

    April 2016 #ZendCon
  147. JoshuaWarren.com As a freelancer… Learning Magento 2 Set aside time

    in your week to learn the design patterns Magento 2 uses Work through the sample code the Magento 2 team has provided Begin experimenting with developing with Magento 2 Do not try to learn ‘on the job’ - be careful accepting M2 work before you’re ready #ZendCon
  148. JoshuaWarren.com As an in-house developer for a merchant… Learning Magento

    2 Determine when your business is likely to migrate to Magento 2 First 2-4 weeks of your Magento 2 migration schedule should be learning Magento 2 Learn the patterns before you start! #ZendCon
  149. JoshuaWarren.com As an agency or industry partner… Learning Magento 2

    Create a tiger team of developers focused on Magento 2 Allow those developers time in the day to learn Magento 2 Those developers should implement your first Magento 2 projects That team then helps the rest of your team through the learning curve #ZendCon
  150. JoshuaWarren.com When do I need to be Magento 2 ready?

    Learning Magento 2 Magento 2 will be released late enough in the year that most merchants won’t begin using it immediately. Merchants will also wait until their mission-critical extensions are available on Magento 2. Start learning it now - but don’t panic! #ZendCon
  151. JoshuaWarren.com Programming With Magento 2 coming to amazon.com & phparch.com

    #ZendCon
  152. Keep in Touch! @JoshuaSWarren JoshuaWarren.com Mage2DevBook.com joind.in/15597

  153. JoshuaWarren.com #ZendCon