Enrico Aillaud - Sviluppo di un metodo di spedizione in Magento 2
-
Upload
meet-magento-italy -
Category
Presentations & Public Speaking
-
view
286 -
download
4
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