SlideShare uma empresa Scribd logo
1 de 31
Baixar para ler offline
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
MagentoFrameworkComponentComponentRegistrar::register(
MagentoFrameworkComponentComponentRegistrar::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>MagentoConfigModelConfigSourceYesno</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>MagentoShippingModelConfigSourceAllspecificcountries</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>MagentoDirectoryModelConfigSourceCountry</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>MeetmagentoShippingModelCarrierShipping</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 MeetmagentoShippingModelCarrier;
use MagentoQuoteModelQuoteAddressRateRequest;
use MagentoCatalogApiDataProductInterface;
use MagentoQuoteModelQuoteItem;
/**
* Class Shipping
* @package MeetmagentoShippingModelCarrier
*/
class Shippingmeetmagento extends MagentoShippingModelCarrierAbstractCarrier implements
MagentoShippingModelCarrierCarrierInterface
{
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 MagentoFrameworkAppConfigScopeConfigInterface $scopeConfig
* @param MagentoQuoteModelQuoteAddressRateResultErrorFactory $rateErrorFactory
* @param PsrLogLoggerInterface $logger
* @param MagentoFrameworkObjectManagerInterface $objectManager
* @param array $data
*/
public function __construct(
MagentoFrameworkAppConfigScopeConfigInterface $scopeConfig,
MagentoQuoteModelQuoteAddressRateResultErrorFactory $rateErrorFactory,
PsrLogLoggerInterface $logger,
MagentoFrameworkObjectManagerInterface $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(‘MagentoShippingModelRateResult’);
$method = $this->_objectManager->create(‘MagentoQuoteModelQuoteAddressRateResultMethod’);
$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
MagentoFrameworkObjectManag
erObjectManager
• ->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 MagentoFrameworkAppConfigScopeConfigInterface $scopeConfig
* @param MagentoQuoteModelQuoteAddressRateResultErrorFactory $rateErrorFactory
* @param PsrLogLoggerInterface $logger
* @param MagentoShippingModelRateResultFactory $rateResultFactory
* @param MagentoQuoteModelQuoteAddressRateResultMethodFactory $rateMethodFactory
* @param array $data
*/
public function __construct(
MagentoFrameworkAppConfigScopeConfigInterface $scopeConfig,
MagentoQuoteModelQuoteAddressRateResultErrorFactory $rateErrorFactory,
PsrLogLoggerInterface $logger,
MagentoFrameworkObjectManagereInterface $objectManager,
MagentoShippingModelRateResultFactory $rateResultFactory,
MagentoQuoteModelQuoteAddressRateResultMethodFactory $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('MagentoShippingModelRateResult');
$result = $this->_rateResultFactory->create();
$method = this->_objectManager->create('MagentoQuoteModelQuoteAddressRateResultMethod');
$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
MagentoCatalogModelProduct
vendorMagentomodule-shippingModelRateResult
FACTORIES
• Creano un istanza di
classi che non si
possono iniettare
direttamente
• Dipendono dall’OM
• generation/Magento
vargenerationMagentoShippingModelRateResultFactory
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
• MagentoCatalogHelperProduct -> initProduct()
• MagentoCatalogBlockProductAbstractProduct -> 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="MagentoCatalogHelperProduct">
<plugin name="MeetmagentoShippingProductHelper" type="MeetmagentoShippingPluginHelperProduct" disabled="false" sortOrder="10" />
</type>
<type name="MagentoCatalogBlockProductAbstractProduct">
<plugin name="MeetmagentoShippingPlugin1" type="MeetmagentoShippingPluginBlockCatalogProductAbstractProductPlugin1"
disabled="false" sortOrder="100"/>
<plugin name="MeetmagentoShippingPlugin2" type="MeetmagentoShippingPluginBlockCatalogProductAbstractProductPlugin2"
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 ListProductInterceptor)
• 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
ListProductInterceptor)
• 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 ListProductInterceptor)
• 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 Blocchi
Layouts
• 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="MeetmagentoShippingBlockProductShipping" name="shipmeetmagento.div" as="shipmeetmagento"
template=“Meetmagento_Shipping::placeholder.phtml" before=“product.info.price”/>
</referenceContainer>
</body>
</page>
Frontend - Blocco
<?php
namespace MeetmagentoShippingBlockProduct;
use MagentoFrameworkRegistry;
use MagentoFrameworkViewElementTemplate;
use MeetmagentoShippingModelCarrierShipping as ShippingModel;
class Shipping extends Template
{
/**
* @var Registry
*/
private $registry;
/**
* @var ShippingModel
*/
private $shippingModel;
/**
* @param TemplateContext $context
* @param array $data
* @param Registry $registry
* @param ShippingModel $shippingModel
*/
public function __construct(TemplateContext $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 MagentoCatalogApiDataProductInterface $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="MeetmagentoShippingBlockProductShipping" 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

Mais conteúdo relacionado

Mais de Meet Magento Italy

Giulio Gargiullo - Strategie di marketing digitale per avviare l’e-commerce i...
Giulio Gargiullo - Strategie di marketing digitale per avviare l’e-commerce i...Giulio Gargiullo - Strategie di marketing digitale per avviare l’e-commerce i...
Giulio Gargiullo - Strategie di marketing digitale per avviare l’e-commerce i...
Meet Magento Italy
 

Mais de Meet Magento Italy (20)

Dirk Pinamonti - Come affrontare la sfida del nuovo mercato multicanale e del...
Dirk Pinamonti - Come affrontare la sfida del nuovo mercato multicanale e del...Dirk Pinamonti - Come affrontare la sfida del nuovo mercato multicanale e del...
Dirk Pinamonti - Come affrontare la sfida del nuovo mercato multicanale e del...
 
Vinai Kopp - How i develop M2 modules
Vinai Kopp - How i develop M2 modules Vinai Kopp - How i develop M2 modules
Vinai Kopp - How i develop M2 modules
 
Eugene Shaksuvarov - Tuning Magento 2 for Maximum Performance
Eugene Shaksuvarov - Tuning Magento 2 for Maximum PerformanceEugene Shaksuvarov - Tuning Magento 2 for Maximum Performance
Eugene Shaksuvarov - Tuning Magento 2 for Maximum Performance
 
Muliadi jeo - How to sell online in Indonesia
Muliadi jeo - How to sell online in IndonesiaMuliadi jeo - How to sell online in Indonesia
Muliadi jeo - How to sell online in Indonesia
 
Max Pronko - 10 migration mistakes from Magento 1 to Magento 2
Max Pronko - 10 migration mistakes from Magento 1 to Magento 2Max Pronko - 10 migration mistakes from Magento 1 to Magento 2
Max Pronko - 10 migration mistakes from Magento 1 to Magento 2
 
Alessandro La Ciura - Progettare la migliore integrazione tra live chat ed e-...
Alessandro La Ciura - Progettare la migliore integrazione tra live chat ed e-...Alessandro La Ciura - Progettare la migliore integrazione tra live chat ed e-...
Alessandro La Ciura - Progettare la migliore integrazione tra live chat ed e-...
 
Bodin - Hullin & Potencier - Magento Performance Profiling and Best Practices
Bodin - Hullin & Potencier - Magento Performance Profiling and Best PracticesBodin - Hullin & Potencier - Magento Performance Profiling and Best Practices
Bodin - Hullin & Potencier - Magento Performance Profiling and Best Practices
 
Giulio Gargiullo - Strategie di marketing digitale per avviare l’e-commerce i...
Giulio Gargiullo - Strategie di marketing digitale per avviare l’e-commerce i...Giulio Gargiullo - Strategie di marketing digitale per avviare l’e-commerce i...
Giulio Gargiullo - Strategie di marketing digitale per avviare l’e-commerce i...
 
Vinai Kopp - FPC Hole punching in Magento 2
Vinai Kopp - FPC Hole punching in Magento 2Vinai Kopp - FPC Hole punching in Magento 2
Vinai Kopp - FPC Hole punching in Magento 2
 
Jacopo Nardiello - From CI to Prod: Running Magento at scale with Kubernetes
Jacopo Nardiello - From CI to Prod: Running Magento at scale with KubernetesJacopo Nardiello - From CI to Prod: Running Magento at scale with Kubernetes
Jacopo Nardiello - From CI to Prod: Running Magento at scale with Kubernetes
 
James Zetlen - PWA Studio Integration…With You
James Zetlen - PWA Studio Integration…With YouJames Zetlen - PWA Studio Integration…With You
James Zetlen - PWA Studio Integration…With You
 
Talesh Seeparsan - The Hound of the Malwarevilles
Talesh Seeparsan - The Hound of the MalwarevillesTalesh Seeparsan - The Hound of the Malwarevilles
Talesh Seeparsan - The Hound of the Malwarevilles
 
Miguel Balparda - A day in support
Miguel Balparda - A day in supportMiguel Balparda - A day in support
Miguel Balparda - A day in support
 
Volodymyr Kublytskyi - Develop Product, Design Platform
Volodymyr Kublytskyi - Develop Product, Design PlatformVolodymyr Kublytskyi - Develop Product, Design Platform
Volodymyr Kublytskyi - Develop Product, Design Platform
 
Rosario Toscano - Processi di ottimizzazione per una crescita continua
Rosario Toscano - Processi di ottimizzazione per una crescita continuaRosario Toscano - Processi di ottimizzazione per una crescita continua
Rosario Toscano - Processi di ottimizzazione per una crescita continua
 
Henrik Feld Jakobsen - How to sell online Scandinavia
Henrik Feld Jakobsen - How to sell online ScandinaviaHenrik Feld Jakobsen - How to sell online Scandinavia
Henrik Feld Jakobsen - How to sell online Scandinavia
 
Rabia Qureshi - How to sell online in UK
Rabia Qureshi - How to sell online in UKRabia Qureshi - How to sell online in UK
Rabia Qureshi - How to sell online in UK
 
Matteo Schuerch - How to sell online in Switzerland
Matteo Schuerch - How to sell online in SwitzerlandMatteo Schuerch - How to sell online in Switzerland
Matteo Schuerch - How to sell online in Switzerland
 
Il data-driven nell’e-commerce: il caso studio Alessi
Il data-driven nell’e-commerce: il caso studio AlessiIl data-driven nell’e-commerce: il caso studio Alessi
Il data-driven nell’e-commerce: il caso studio Alessi
 
Philippe Bernou - Seamless omnichannel solutions with Magento order management
Philippe Bernou - Seamless omnichannel solutions with Magento order managementPhilippe Bernou - Seamless omnichannel solutions with Magento order management
Philippe Bernou - Seamless omnichannel solutions with Magento order management
 

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

  • 1.
  • 2. Panoramica del modulo • Struttura del modulo in Magento 2 • Registrazione del modulo e configurazione • Backend • Classe di Modello • DI.xml e Plugin • Frontend
  • 3. Registrazione del modulo <?php MagentoFrameworkComponentComponentRegistrar::register( MagentoFrameworkComponentComponentRegistrar::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
  • 4. 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>MagentoConfigModelConfigSourceYesno</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>MagentoShippingModelConfigSourceAllspecificcountries</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>MagentoDirectoryModelConfigSourceCountry</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>
  • 5. 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>MeetmagentoShippingModelCarrierShipping</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>
  • 6. Modello - Service Contract <?php namespace MeetmagentoShippingModelCarrier; use MagentoQuoteModelQuoteAddressRateRequest; use MagentoCatalogApiDataProductInterface; use MagentoQuoteModelQuoteItem; /** * Class Shipping * @package MeetmagentoShippingModelCarrier */ class Shippingmeetmagento extends MagentoShippingModelCarrierAbstractCarrier implements MagentoShippingModelCarrierCarrierInterface { 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
  • 7. Modello - ObjectManager /** * Shippingmeetmagento constructor. * @param MagentoFrameworkAppConfigScopeConfigInterface $scopeConfig * @param MagentoQuoteModelQuoteAddressRateResultErrorFactory $rateErrorFactory * @param PsrLogLoggerInterface $logger * @param MagentoFrameworkObjectManagerInterface $objectManager * @param array $data */ public function __construct( MagentoFrameworkAppConfigScopeConfigInterface $scopeConfig, MagentoQuoteModelQuoteAddressRateResultErrorFactory $rateErrorFactory, PsrLogLoggerInterface $logger, MagentoFrameworkObjectManagerInterface $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)
  • 8. 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(‘MagentoShippingModelRateResult’); $method = $this->_objectManager->create(‘MagentoQuoteModelQuoteAddressRateResultMethod’); $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 MagentoFrameworkObjectManag erObjectManager • ->create() • ->get() • Il $method corrisponde al nostro metodo di spedizione • Il $result è quello che vedremo apparire per selezionare il metodo di spedizione
  • 9. Modello - Factories /** * Shippingmeetmagento constructor. * @param MagentoFrameworkAppConfigScopeConfigInterface $scopeConfig * @param MagentoQuoteModelQuoteAddressRateResultErrorFactory $rateErrorFactory * @param PsrLogLoggerInterface $logger * @param MagentoShippingModelRateResultFactory $rateResultFactory * @param MagentoQuoteModelQuoteAddressRateResultMethodFactory $rateMethodFactory * @param array $data */ public function __construct( MagentoFrameworkAppConfigScopeConfigInterface $scopeConfig, MagentoQuoteModelQuoteAddressRateResultErrorFactory $rateErrorFactory, PsrLogLoggerInterface $logger, MagentoFrameworkObjectManagereInterface $objectManager, MagentoShippingModelRateResultFactory $rateResultFactory, MagentoQuoteModelQuoteAddressRateResultMethodFactory $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)
  • 10. 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('MagentoShippingModelRateResult'); $result = $this->_rateResultFactory->create(); $method = this->_objectManager->create('MagentoQuoteModelQuoteAddressRateResultMethod'); $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
  • 11. 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 MagentoCatalogModelProduct vendorMagentomodule-shippingModelRateResult FACTORIES • Creano un istanza di classi che non si possono iniettare direttamente • Dipendono dall’OM • generation/Magento vargenerationMagentoShippingModelRateResultFactory
  • 12. 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'); } ....
  • 14. Risultato - Frontend (Importo < 50)
  • 15. Risultato - Frontend (Importo > 50)
  • 16. Plugin - Definizione • Plugin (o Interception) • Per osservare metodi senza dover modificare classi originali • MagentoCatalogHelperProduct -> initProduct() • MagentoCatalogBlockProductAbstractProduct -> getAddToCartUrl() • I plugin non possono essere utilizzati per: • Final Class • Final Method • Classi create senza Dependency Injection
  • 17. 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="MagentoCatalogHelperProduct"> <plugin name="MeetmagentoShippingProductHelper" type="MeetmagentoShippingPluginHelperProduct" disabled="false" sortOrder="10" /> </type> <type name="MagentoCatalogBlockProductAbstractProduct"> <plugin name="MeetmagentoShippingPlugin1" type="MeetmagentoShippingPluginBlockCatalogProductAbstractProductPlugin1" disabled="false" sortOrder="100"/> <plugin name="MeetmagentoShippingPlugin2" type="MeetmagentoShippingPluginBlockCatalogProductAbstractProductPlugin2" disabled="true" sortOrder="200"/> </type> </config>
  • 18. 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()
  • 19. 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
  • 20. Plugin - Before Listener • Prima proprietà del metodo deve essere sempre il $subject (sarà un ListProductInterceptor) • La regola di trasformazione è: • getProductPrice($product) • beforeGetProductPrice($subject,$product) public function beforeGetProductPrice( $subject, $product ) { var_dump(get_class($subject)); var_dump('Plugin1 - beforeGetProductPrice'); }
  • 21. Plugin - After Listener • La prima e unica proprietà è il $subject istanza dell’oggetto che si sta osservando (del tipo ListProductInterceptor) • La regola di trasformazione è: • getProductPrice($product) • afterGetProductPrice($subject) public function afterGetProductPrice($subject) { var_dump('Plugin1 - afterGetProductPrice'); }
  • 22. Plugin - Around Listener • Prima proprietà del metodo deve essere sempre il $subject (sarà un ListProductInterceptor) • 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); }
  • 24. Frontend - Layout Container Blocchi Layouts • Per una pagina generica, componenti: • Page layout • Page configuration • <module_dir>/view/frontend/layo ut
  • 25. 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="MeetmagentoShippingBlockProductShipping" name="shipmeetmagento.div" as="shipmeetmagento" template=“Meetmagento_Shipping::placeholder.phtml" before=“product.info.price”/> </referenceContainer> </body> </page>
  • 26. Frontend - Blocco <?php namespace MeetmagentoShippingBlockProduct; use MagentoFrameworkRegistry; use MagentoFrameworkViewElementTemplate; use MeetmagentoShippingModelCarrierShipping as ShippingModel; class Shipping extends Template { /** * @var Registry */ private $registry; /** * @var ShippingModel */ private $shippingModel; /** * @param TemplateContext $context * @param array $data * @param Registry $registry * @param ShippingModel $shippingModel */ public function __construct(TemplateContext $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 MagentoCatalogApiDataProductInterface $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(); } } }
  • 27. 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="MeetmagentoShippingBlockProductShipping" 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
  • 30. 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