magento 2 - an intro to a modern php-based system - northeast php 2015
Post on 08-Jan-2017
2.440 Views
Preview:
TRANSCRIPT
PRESENTED BY JOSHUA WARREN
PRESENTED AT
NORTHEAST PHP 2015
Magento 2AN INTRODUCTION TO A MODERN PHP-
BASED SYSTEM
MY EXPERIENCE
JoshuaWarren.com
My Experience
PHP Developer Since 1999
Founded Creatuity in 2008
Focused on the Magento platform
Magento 2 contributor
#NEPHP
JoshuaWarren.com
early adopter of both Magento 1 and Magento 2
#NEPHP
JoshuaWarren.com
Frequent Magento presenter
#NEPHP
JoshuaWarren.com
Active member of the #RealMagento community
#NEPHP
JoshuaWarren.com
Involved in feedback and design discussions throughout the Magento 2 Developer Beta
#NEPHP
JoshuaWarren.com
Wrote Writing the book on Magento 2
#NEPHP
A BRIEF HISTORY OF MAGENTO
Photo courtesy of @YoavKutner
JoshuaWarren.com
Magento 1 development began in 2007 by Varien, a PHP development
agency.
#NEPHP
JoshuaWarren.com
In 2007, osCommerce was state of the art.
#NEPHP
JoshuaWarren.com
Cloud-based eCommerce systems didn’t exist.
#NEPHP
JoshuaWarren.com
PHP 5.2 was cutting-edge.
#NEPHP
JoshuaWarren.com
Composer didn’t exist, and Zend Framework 1 was still in early beta.
#NEPHP
JoshuaWarren.com
Magento 1 was built to resolve the pain points of osCommerce.
#NEPHP
JoshuaWarren.com
Designed to be more flexible and to provide standardized ways to
customize the platform.
#NEPHP
JoshuaWarren.com
By 2010, Magento had been downloaded 1.5 million times.
#NEPHP
JoshuaWarren.com
Attracted by Magento’s free, open-source approach, hundreds of
thousands of sites were launched using Magento 1.
#NEPHP
JoshuaWarren.com
There’s just one problem…
#NEPHP
JoshuaWarren.com
Ecommerce development is hard.
#NEPHP
JoshuaWarren.com
Lightly documented ecommerce development is even harder.
#NEPHP
JoshuaWarren.com
Varien, now known as Magento Inc, is acquired by eBay in 2011.
#NEPHP
JoshuaWarren.com
Work begins on Magento 2 almost immediately.
#NEPHP
JoshuaWarren.com
Now the most widely-used eCommerce platform, powering over 250,000 sites, expectations are high
for the Magento 2 team.
#NEPHP
JoshuaWarren.com
Fewer than 30 commits are made to Magento 2 in 2012.
#NEPHP
JoshuaWarren.com
Internal priorities continue to shift, and finally at the end of 2014,
Magento 2 development is made public on Github.
#NEPHP
JoshuaWarren.com
Magento commits to a release schedule for Magento 2, and
announces the acceptance of pull requests.
#NEPHP
JoshuaWarren.com
devdocs.magento.com launches with significant documentation of
Magento 2.
#NEPHP
JoshuaWarren.com
Developer Beta is released in December 2014; Merchant Beta in
July 2015
#NEPHP
JoshuaWarren.com
Production-ready release (‘general availability’) scheduled for Q4 2015
#NEPHP
MAGENTO 2
github.com/magento/magento2
JoshuaWarren.com
Feature parity with Magento 1 + UI/UX Improvements + focus on
resolving technical debt
#NEPHP
JoshuaWarren.com
Technologies
#NEPHP
JoshuaWarren.com #NEPHP
Composer
composer create-project magento/product-community-edition --stability="beta" <installation directory name>
JoshuaWarren.com
Each Magento 2 module is a separate Composer package
#NEPHP
JoshuaWarren.com
PSR-0 thru PSR-4
#NEPHP
JoshuaWarren.com
Testing built in from the start. phpunit, selenium, JMeter, Jasmine
#NEPHP
JoshuaWarren.com
magento2/dev/tests/
#NEPHP
JoshuaWarren.com
HTML5, CSS3, LESS CSS Preprocessor, JQuery, RequireJS
#NEPHP
JoshuaWarren.com
Components from Zend Framework 1, Zend Framework 2,
Symfony
#NEPHP
JoshuaWarren.com
Technical Architecture
#NEPHP
JoshuaWarren.com
Presentation Layer, Service Layer, Domain Layer, Persistence Layer
#NEPHP
JoshuaWarren.com #NEPHP
JoshuaWarren.com
Presentation Layer - views, literally and figuratively
#NEPHP
JoshuaWarren.com
Service Layer - an intermediary between the presentation and
model layers
#NEPHP
JoshuaWarren.com
Service layer provides a stable, backwards-compatible interface
and forms the foundation for dependency injection.
#NEPHP
JoshuaWarren.com
Domain layer - business logic, including models. Contains the
implementation of service contracts.
#NEPHP
JoshuaWarren.com
Persistence Layer - resource models that perform CRUD
operations on database tables.
#NEPHP
JoshuaWarren.com
Some models use a single table, others continue to use the
Entity-Attribute-Value design pattern used in Magento 1.
#NEPHP
JoshuaWarren.com
Design Patterns
#NEPHP
JoshuaWarren.com
Loose Coupling
#NEPHP
JoshuaWarren.com
Dependency Injection
#NEPHP
JoshuaWarren.com
Service Contracts
#NEPHP
JoshuaWarren.com
Interceptors
#NEPHP
JoshuaWarren.com
Semantic Versioning
#NEPHP
DEPENDENCY INJECTION
Sorry - no cool photo here, because I don’t like needles…
JoshuaWarren.com
DI is exactly what it sounds like - injecting dependencies into the
objects that need them.
#NEPHP
JoshuaWarren.com
DI is designed to reduce dependencies and promote loose
coupling
#NEPHP
JoshuaWarren.com
DI makes unit testing much easier
#NEPHP
JoshuaWarren.com
Magento 2 uses the Constructor Injection pattern of DI
#NEPHP
JoshuaWarren.com
DI in Magento 2 is handled via XML files
#NEPHP
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>
INTERCEPTORS
JoshuaWarren.com
Plugin system based on the interceptor pattern
#NEPHP
JoshuaWarren.com
Calls to almost any module can be intercepted and altered
#NEPHP
JoshuaWarren.com
Vast improvement over the rewrite pattern in Magento 1 - no
more rewrite conflicts
#NEPHP
JoshuaWarren.com #NEPHP
di.xml
<config> <type name="{ObservedType}"> <plugin name="{pluginName}" type="{PluginClassName}" sortOrder="1" disabled="false"/> </type></config>
JoshuaWarren.com
Sort order defines order if multiple plugins intercept the
same item
#NEPHP
JoshuaWarren.com
Possible to intercept before, after and around a function
#NEPHP
JoshuaWarren.com #NEPHP
‘Before’ Interceptor
class Plugin{ public function beforeSetName(\Magento\Catalog\Model\Product $subject, $name) { return array('(' . $name . ')'); }}
JoshuaWarren.com #NEPHP
‘After’ Interceptor
class Plugin{ public function afterGetName(\Magento\Catalog\Model\Product $subject, $result) { return '|' . $result . '|'; }}
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; }}
SERVICE CONTRACTS
Credit to Allan MacGregor for the Soylent Green joke.
JoshuaWarren.com
Set of interfaces to define the public API of a module
#NEPHP
JoshuaWarren.com
This API is the interface provided to other modules to access its
implementation
#NEPHP
JoshuaWarren.com
Designed to hide business logic behind a stable interface
#NEPHP
JoshuaWarren.com
Service contracts + semantic versioning = minor releases will not
break existing code
#NEPHP
JoshuaWarren.com
@deprecated = will be removed with the next major version release
#NEPHP
JoshuaWarren.com #NEPHP
CustomerRepositoryInterface.phpnamespace 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);
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);
JoshuaWarren.com
Service Contracts include Data Interfaces and Service Interfaces
#NEPHP
JoshuaWarren.com
Data Interfaces return information about data entities
#NEPHP
JoshuaWarren.com
Service Interfaces handle business logic
#NEPHP
JoshuaWarren.com
Three types of service interfaces in Magento 2 (so far)
#NEPHP
JoshuaWarren.com
Repository Interfaces provide access to persistent data entities
#NEPHP
JoshuaWarren.com
CustomerRepositoryInterface, AddressRepositoryInterface, etc.
#NEPHP
JoshuaWarren.com
Repository interfaces contain the CRUD operations
#NEPHP
JoshuaWarren.com
Management interfaces contain management functions not related
to repositories
#NEPHP
JoshuaWarren.com
Validators, createAccount, changePassword, etc
#NEPHP
JoshuaWarren.com
Metadata interfaces provide meta information - primarily about
custom attributes
#NEPHP
EXTENDING MAGENTO 2
JoshuaWarren.com
Create your basic module file structure
#NEPHP
JoshuaWarren.com #NEPHP
App/Code/<Vendor>/<Module>/
composer.json etc/module.xml Test/Unit/<tests go here>
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" ] ] } }
JoshuaWarren.com #NEPHP
etc/module.xml
<config> <module name=“Joshuaswarren_SampleMinimal" setup_version="2.0.0"> </module> </config>
JoshuaWarren.com
Optional config files in etc: acl.xml
config.xml di.xml
webapi.xml
#NEPHP
JoshuaWarren.com
acl.xml defines new items for Magento’s ACL system
#NEPHP
JoshuaWarren.com
Config.xml adds new configuration options
#NEPHP
JoshuaWarren.com
Webapi.xml defines items to expose via the REST or SOAP APIs
#NEPHP
JoshuaWarren.com
Optional subdirectories in etc: adminhtml frontend
webapi_rest webapi_soap
#NEPHP
JoshuaWarren.com
Items in the main etc directory apply globally to your extension
#NEPHP
JoshuaWarren.com
Items in the 4 subdirectories apply only to that area - i.e., adminhtml
only applies to the Magento backend
#NEPHP
JoshuaWarren.com
Optional subdirectories: API
Block Controller
Helper Model
#NEPHP
JoshuaWarren.com
Optional subdirectories: Plugin Setup
Ui i18n view
#NEPHP
JoshuaWarren.com
API contains any new service contracts your extension adds
#NEPHP
JoshuaWarren.com
Block contains any new template blocks your extension adds
#NEPHP
JoshuaWarren.com
Controller contains your extension’s controllers
#NEPHP
JoshuaWarren.com
Helper contains any helper functions that your extension
needs
#NEPHP
JoshuaWarren.com
Model contains your extension’s models
#NEPHP
JoshuaWarren.com
Plugin contains any interceptors your extension defines
#NEPHP
JoshuaWarren.com
Setup contains your database migrations using Magento’s setup
script system
#NEPHP
JoshuaWarren.com
UI is for Magento 2’s new Magento UI library
#NEPHP
JoshuaWarren.com
I18n contains internationalization files - CSV files defining the
translations for your strings
#NEPHP
JoshuaWarren.com
View contains the views for your extension
#NEPHP
JoshuaWarren.com
If your extension doesn’t need one of these items, just omit that
folder
#NEPHP
JoshuaWarren.com
Sample: custom shipping method to allow for in-store pickup from
several locations
#NEPHP
JoshuaWarren.com #NEPHP
Block/System/Config/Form/Field/Locations.phpnamespace 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(); }}
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; }
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; }}
JoshuaWarren.com #NEPHP
Model/Carrier.phpnamespace 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
JoshuaWarren.com
In model/carrier.php we implement all of the functions a shipping carrier must have in Magento 2
#NEPHP
JoshuaWarren.com
GetAllowedMethods CollectRates
GetLocationInfo BuildRateForLocation
GetLocations GetShippingOrigin
#NEPHP
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>[…]
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>
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>
JoshuaWarren.com #NEPHP
Etc/Module.xml<config xmlns:xsi=“[…]” xsi:noNamespaceSchemaLocation=“[…]”> <module name="Magento_SampleShippingProvider" setup_version="2.0.0"> </module></config>
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" ] ] } }
JoshuaWarren.com
We now have a complete, functioning in-store-pickup
shipping extension for Magento 2
#NEPHP
JoshuaWarren.com
Take a look at the Test/Unit directory on Github for tests for
this extension
#NEPHP
JoshuaWarren.com
Magento 2 unit testing includes mocking, fixtures, etc. -
everything you need for TDD
#NEPHP
LEARNING MOREDon’t end up like this guy ->
JoshuaWarren.com
devdocs.magento.com
magento.stackexchange.com/questions/tagged/magento2
#NEPHP
JoshuaWarren.com
AlanStorm.com AlanKent.me
CoderOnCode.com
#NEPHP
JoshuaWarren.com
Upcoming events: Meet Magento New York, ZendCon, php[world]
#NEPHP
JoshuaWarren.com
Programming With Magento 2 coming to amazon.com & phparch.com
#NEPHP
Keep in Touch!
joind.in/14737
@JoshuaSWarren
JoshuaWarren.com
Mage2DevBook.com
JoshuaWarren.com #NEPHP
top related