Enrico Aillaud - Sviluppo di un metodo di spedizione in Magento 2

Post on 13-Apr-2017

286 views 4 download

Transcript of Enrico Aillaud - Sviluppo di un metodo di spedizione in Magento 2

Panoramica del modulo

• Struttura del modulo in Magento 2

• Registrazione del modulo e configurazione

• Backend

• Classe di Modello

• DI.xml e Plugin

• Frontend

Registrazione del modulo

<?php

\Magento\Framework\Component\ComponentRegistrar::register(

\Magento\Framework\Component\ComponentRegistrar::MODULE,

'Meetmagento_Shipping',

__DIR__

);

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">

<module name="Meetmagento_Shipping" setup_version="1.0.0">

<sequence>

<module name="Magento_OfflineShipping"/>

<module name="Magento_Catalog"/>

</sequence>

</module>

</config>

Module.xml

• Dichiarazione del modulo

• Dipendenza su:

• Magento_OfflineShipping

• Magento_Catalog

• Dipendenze sulle classi che osserviamo

Registration.php

• Punto di entrata del modulo

• Serve a Magento per identificare i moduli

installati nel sistema

Backend - system.xml

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">

<system>

<section id="carriers">

<group id="shippingmeetmagento" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1"

showInStore="1">

<label>Shipping Meet Magento</label>

<field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1"

showInStore="0">

<label>Abilitato</label>

<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>

</field>

<field id="title" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1"

showInStore="1">

<label>Titolo</label>

</field>

<field id="name" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1"

showInStore="1">

<label>Nome del metodo di spedizione</label>

</field>

<field id="price" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1"

showInStore="0">

<label>Prezzo di invio</label>

<validate>validate-number validate-zero-or-greater</validate>

</field>

<field id="sallowspecific" translate="label" type="select" sortOrder="50" showInDefault="1"

showInWebsite="1" showInStore="0">

<label>Paesi permessi per l'invio</label>

<frontend_class>shipping-applicable-country</frontend_class>

<source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model>

</field>

<field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1"

showInWebsite="1" showInStore="0">

<label>Invio a paesi selezionati</label>

<source_model>Magento\Directory\Model\Config\Source\Country</source_model>

<can_be_empty>1</can_be_empty>

</field>

<field id="attribute_set" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0">

<label>Attribute set dei prodotti attivi</label>

</field>

<field id="free_shipping_amount" translate="label" type="text" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0">

<label>Importo minimo carrello per l'invio gratuito</label>

</field>

<field id="special_product_amount" translate="label" type="text" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="0">

<label>Importo minimo prodotto invio entro 24h</label>

</field>

</group>

</section>

</system>

</config>

Backend - config.xml

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">

<default>

<carriers>

<shippingmeetmagento>

<active>0</active>

<sallowspecific>0</sallowspecific>

<model>Meetmagento\Shipping\Model\Carrier\Shipping</model>

<name>Shipping Meet Magento Method</name>

<price>4.99</price>

<title>Shipping Meet Magento</title>

<specificerrmsg>Metodo di spedizione non disponibile.</specificerrmsg>

<attribute_set>0,15</attribute_set>

<free_shipping_amount>50</free_shipping_amount>

<special_product_amount>35</special_product_amount>

</shippingmeetmagento>

</carriers>

</default>

</config>

Modello - Service Contract

<?php

namespace Meetmagento\Shipping\Model\Carrier;

use Magento\Quote\Model\Quote\Address\RateRequest;

use Magento\Catalog\Api\Data\ProductInterface;

use Magento\Quote\Model\Quote\Item;

/**

* Class Shipping

* @package Meetmagento\Shipping\Model\Carrier

*/

class Shippingmeetmagento extends \Magento\Shipping\Model\Carrier\AbstractCarrier implements

\Magento\Shipping\Model\Carrier\CarrierInterface

{

protected $_code = 'shippingmeetmagento';

protected $_isFixed = true;

protected $_rateResultFactory;

protected $_rateMethodFactory;

SERVICE CONTRACT

• Insieme di interfacce PHP definite da

un modulo

• Suddiviso in:

• Data Interface

• Service Interface

Modello - ObjectManager

/**

* Shippingmeetmagento constructor.

* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig

* @param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory

* @param \Psr\Log\LoggerInterface $logger

* @param \Magento\Framework\ObjectManagerInterface $objectManager

* @param array $data

*/

public function __construct(

\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,

\Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory,

\Psr\Log\LoggerInterface $logger,

\Magento\Framework\ObjectManagerInterface $objectManager,

array $data = []

)

{

$this->_objectManager = $objectManager;

parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data);

}

• Nel costruttore dovrò avere:

• $scopeConfig

• $rateErrorFactory

• $logger

• $data

• Estendo con:

• $objectManager

• Necessario implementare il

metodo pubblico

collectRates(RateRequest

$request)

Modello - ObjectManager

/**

* @param RateRequest $request

* @return bool

*/

public function collectRates(RateRequest $request)

{

if (!$this->getConfigFlag('active')) {

return false;

}

if ($request->getAllItems()) {

.....

}

$shippingPrice = $this->getConfigData(‘price');

$result = $this->_objectManager->create(‘Magento\Shipping\Model\Rate\Result’);

$method = $this->_objectManager->create(‘Magento\Quote\Model\Quote\Address\RateResult\Method’);

$method->setCarrier('shippingmeetmagento');

$method->setCarrierTitle($this->getConfigData('title'));

$method->setMethod('shippingmeetmagento');

$method->setMethodTitle($this->getConfigData('name'));

$method->setPrice($shippingPrice);

$method->setCost($shippingPrice);

$result->append($method);

return $result;

}

• Inizializzazione oggetti tramite

ObjectManager che è a sua volta

istanza della classe

Magento\Framework\ObjectManag

er\ObjectManager

• ->create()

• ->get()

• Il $method corrisponde al nostro

metodo di spedizione

• Il $result è quello che vedremo

apparire per selezionare il metodo

di spedizione

Modello - Factories

/**

* Shippingmeetmagento constructor.

* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig

* @param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory

* @param \Psr\Log\LoggerInterface $logger

* @param \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory

* @param \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory

* @param array $data

*/

public function __construct(

\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,

\Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory,

\Psr\Log\LoggerInterface $logger,

\Magento\Framework\ObjectManagereInterface $objectManager,

\Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory,

\Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory,

array $data = []

)

{

$this->_rateResultFactory = $rateResultFactory;

$this->_rateMethodFactory = $rateMethodFactory;

parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data);

}

• Nel costruttore dovrò avere:

• $scopeConfig

• $rateErrorFactory

• $logger

• $data

• Estendo aggiungendo

• $rateResultFactory

• $rateMethodFactory

• Necessario implementare il

metodo pubblico

collectRates(RateRequest

$request)

Modello - Factories

/**

* @param RateRequest $request

* @return bool

*/

public function collectRates(RateRequest $request)

{

if (!$this->getConfigFlag('active')) {

return false;

}

if ($request->getAllItems()) {

.....

}

$shippingPrice = $this->getConfigData(‘price');

$result = this->_objectManager->create('Magento\Shipping\Model\Rate\Result');

$result = $this->_rateResultFactory->create();

$method = this->_objectManager->create('Magento\Quote\Model\Quote\Address\RateResult\Method');

$method = $this->_rateMethodFactory->create();

$method->setCarrier('shippingmeetmagento');

$method->setCarrierTitle($this->getConfigData('title'));

$method->setMethod('shippingmeetmagento');

$method->setMethodTitle($this->getConfigData('name'));

$method->setPrice($shippingPrice);

$method->setCost($shippingPrice);

$result->append($method);

return $result;

}

• Inizializzazione oggetti tramite

classi Factory

• ->create()

• ->get()

• Il $method corrisponde al nostro

metodo di spedizione

• Il $result è quello che vedremo

apparire per selezionare il

metodo di spedizione

Perché?

Oggetti istanziabili

dall’objectManager

SINGLETON

Oggetti NON istanziabili

dall’objectManager

Oggetti con ciclo di

vita temporaneo

Oggetti che hanno

bisogno di input

esterni

Sessione

Magento\Catalog\Model\Product

vendor\Magento\module-shipping\Model\Rate\Result

FACTORIES

• Creano un istanza di

classi che non si

possono iniettare

direttamente

• Dipendono dall’OM

• generation/Magento

var\generation\Magento\Shipping\Model\Rate\ResultFactory

Modello - Metodi

/**

* @param ProductInterface $product

* @return bool

*/

public function isAvailableForProduct(ProductInterface $product)

{

$attributeSets = array_map("intval", explode(",", $this->getConfigData('attribute_set')));

return in_array($product->getAttributeSetId(), $attributeSets) && $this->getConfigFlag('active');

}

/**

* @param RateRequest $request

* @return bool

*/

public function collectRates(RateRequest $request)

{

if (!$this->getConfigFlag('active')) {

return false;

}

$total = 0;

if ($request->getAllItems()) {

foreach ($request->getAllItems() as $item) {

$total += $item->getPrice();

}

}

if($this->getConfigData('free_shipping_amount') < $total){

$shippingPrice = $this->getConfigData(0);

} else {

$shippingPrice = $this->getConfigData('price');

}

....

Risultato - Backend

Risultato - Frontend (Importo < 50)

Risultato - Frontend (Importo > 50)

Plugin - Definizione

• Plugin (o Interception)

• Per osservare metodi senza dover modificare classi originali

• Magento\Catalog\Helper\Product -> initProduct()

• Magento\Catalog\Block\Product\AbstractProduct -> getAddToCartUrl()

• I plugin non possono essere utilizzati per:

• Final Class

• Final Method

• Classi create senza Dependency Injection

Plugin - DI.xml

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

<type name="Magento\Catalog\Helper\Product">

<plugin name="MeetmagentoShippingProductHelper" type="Meetmagento\Shipping\Plugin\Helper\Product" disabled="false" sortOrder="10" />

</type>

<type name="Magento\Catalog\Block\Product\AbstractProduct">

<plugin name="MeetmagentoShippingPlugin1" type="Meetmagento\Shipping\Plugin\Block\Catalog\Product\AbstractProductPlugin1"

disabled="false" sortOrder="100"/>

<plugin name="MeetmagentoShippingPlugin2" type="Meetmagento\Shipping\Plugin\Block\Catalog\Product\AbstractProductPlugin2"

disabled="true" sortOrder="200"/>

</type>

</config>

Plugin - Interception

•INTERCEPTION:

Pattern di sviluppo software che consente di inserire dinamicamente codice senza

cambiare necessariamente il comportamento della classe originale.

Classe 1

Richiama

Classe2::getAddToCartUrl()

Classe 2

Implementa

getAddToCartUrl()

Plugin 1

Before / Around / After

getAddToCartUrl()

Plugin - Listener

1. Before Listener

• Convenzione: before{NomeMetodo} -> beforeGetProductPrice()

• Non ha bisogno di restituire un valore

3. Around Listener

• Convenzione: around{NomeMetodo} -> aroundGetProductPrice()

• Deve restituire un valore

2. After Listener

• Convenzione: after{NomeMetodo} -> afterGetProductPrice()

• Non ha bisogno di restituire un valore

Plugin - Before Listener

• Prima proprietà del metodo deve essere sempre il

$subject (sarà un ListProduct\Interceptor)

• La regola di trasformazione è:

• getProductPrice($product)

• beforeGetProductPrice($subject,$product)

public function beforeGetProductPrice(

$subject,

$product

)

{

var_dump(get_class($subject));

var_dump('Plugin1 - beforeGetProductPrice');

}

Plugin - After Listener

• La prima e unica proprietà è il $subject istanza

dell’oggetto che si sta osservando (del tipo

ListProduct\Interceptor)

• La regola di trasformazione è:

• getProductPrice($product)

• afterGetProductPrice($subject)

public function afterGetProductPrice($subject)

{

var_dump('Plugin1 - afterGetProductPrice');

}

Plugin - Around Listener

• Prima proprietà del metodo deve essere sempre il

$subject (sarà un ListProduct\Interceptor)

• La seconda proprietà è sempre il $proceed di

\Closure

• La regola di trasformazione è:

• getProductPrice($product)

• aroundGetProductPrice($subject)

public function aroundGetProductPrice(

$subject,

\Closure $proceed,

$product

)

{

return $proceed($product);

}

Plugin - Risultato

Frontend - Layout

Container BlocchiLayouts

• Per una pagina generica, componenti:

• Page layout

• Page configuration

• <module_dir>/view/frontend/layo

ut

Frontend - Layout

• Suddivisi in:

• Page - <page>

• Layout - <layout>

• Base layout in:

• <dir_modulo>/view/frontend/layout

/vendor/magento/module-

catalog/view/frontend/layout/catalog_product_view.xml

app/code/Meetmagento/Shipping/view/frontend/layout/catalog_

product_view.xml

<?xml version="1.0"?>

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">

<body>

<referenceContainer name="product.info.main">

<block class="Meetmagento\Shipping\Block\Product\Shipping" name="shipmeetmagento.div" as="shipmeetmagento"

template=“Meetmagento_Shipping::placeholder.phtml" before=“product.info.price”/>

</referenceContainer>

</body>

</page>

Frontend - Blocco<?php

namespace Meetmagento\Shipping\Block\Product;

use Magento\Framework\Registry;

use Magento\Framework\View\Element\Template;

use Meetmagento\Shipping\Model\Carrier\Shipping as ShippingModel;

class Shipping extends Template

{

/**

* @var Registry

*/

private $registry;

/**

* @var ShippingModel

*/

private $shippingModel;

/**

* @param Template\Context $context

* @param array $data

* @param Registry $registry

* @param ShippingModel $shippingModel

*/

public function __construct(Template\Context $context, array $data = [], Registry $registry, ShippingModel $shippingModel)

{

parent::__construct($context, $data);

$this->_isScopePrivate = true;

$this->registry = $registry;

$this->shippingModel = $shippingModel;

}

/**

* @return string

*/

public function toHtml()

{

/** @var \Magento\Catalog\Api\Data\ProductInterface $product */

$product = $this->registry->registry('current_product');

$condition = $product->getPrice() > $this->shippingModel->getConfigData('special_product_amount');

if (!$this->shippingModel->isAvailableForProduct($product) || !$condition) {

return '';

} else {

$this->setMessage('Spedizione entro 24h');

return parent::toHtml();

}

}

}

Frontend - Layout e Template

<?xml version="1.0"?>

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">

<body>

<referenceContainer name="product.info.main">

<block class="Meetmagento\Shipping\Block\Product\Shipping" name="shipmeetmagento.div" as="shipmeetmagento" template="Meetmagento_Shipping::placeholder.phtml"

before="product.info.price"/>

</referenceContainer>

</body>

</page>

<div style="

background: greenyellow;

color: black;

width: 190px;

text-align: center;

padding: 3px;

">

<?php echo $this->getMessage(); ?>

</div>

• view/frontend/templates/placeholder.phtml

• view/frontend/layout/catalog_product_view.xml

Layout - Risultato

Grazie per l’attenzione

Fonti

• Documentazione ufficiale Magento 2

http://devdocs.magento.com

• Alan Storm

http://alanstorm.com/category/magento-2

• Magento 2 Developer’s Guide

Autore: Branko Ajzele Casa Editrice: Packt

• Marc Espinosa

• http://www.meetup.com/Barcelona-Magento-Commerce-

Meetup/members/182506058/

• https://github.com/mespinosaz/magento2-meetup-shipping-module

https://yameveo.com

https://twitter.com/yameveo

https://facebook.com/yameveo

Github: https://github.com/Yameveo/meetmagento.git

https://twitter.com/enr79