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

Joshua Warren

August 23, 2015
Tweet

More Decks by Joshua Warren

Other Decks in Technology

Transcript

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

    2 AN INTRODUCTION TO A MODERN PHP- BASED SYSTEM
  2. JoshuaWarren.com My Experience PHP Developer Since 1999 Founded Creatuity in

    2008 Focused on the Magento platform Magento 2 contributor #NEPHP
  3. JoshuaWarren.com Attracted by Magento’s free, open- source approach, hundreds of

    thousands of sites were launched using Magento 1. #NEPHP
  4. JoshuaWarren.com Now the most widely-used eCommerce platform, powering over 250,000

    sites, expectations are high for the Magento 2 team. #NEPHP
  5. JoshuaWarren.com Internal priorities continue to shift, and finally at the

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

    and announces the acceptance of pull requests. #NEPHP
  7. JoshuaWarren.com Some models use a single table, others continue to

    use the Entity-Attribute-Value design pattern used in Magento 1. #NEPHP
  8. JoshuaWarren.com DI is exactly what it sounds like - injecting

    dependencies into the objects that need them. #NEPHP
  9. 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>
  10. 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;
 }
 }
  11. 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);
  12. 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);

  13. 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" ] ] } }
  14. JoshuaWarren.com Items in the 4 subdirectories apply only to that

    area - i.e., adminhtml only applies to the Magento backend #NEPHP
  15. 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();
 }
 }
  16. 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;
 }

  17. 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;
 }
 }
  18. 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 

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

    shipping carrier must have in Magento 2 #NEPHP
  20. 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>
 […]
  21. 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>
  22. 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" ] ] } }