SlideShare uma empresa Scribd logo
1 de 34
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>
Registration.php

• Punto di entrata del modulo

• Serve a Magento per identificare i
moduli installati nel sistema
Module.xml

• Dichiarazione del modulo
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;





SERVICE CONTRACT

• Insieme di interfacce PHP definite per un
modulo

• Suddiviso in:
• Data Interface
• Service Interface
• Repository
• Management
• Metadata

• Vantaggi:
• Modularità
• Gestione dipendenze con composer
• Configurazione servizi come API
Modello - ObjectManager
/**

* Class Shipping

* @package MeetmagentoShippingModelCarrier

*/



class Shippingmeetmagento extends MagentoShippingModelCarrierAbstractCarrier implements

MagentoShippingModelCarrierCarrierInterface

{

protected $_code = 'shippingmeetmagento';

protected $_isFixed = true;
/**

* 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 Magento
FrameworkObjectManager
ObjectManager 

• ->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.php
FACTORIES
• Creano un istanza di
classi che non si
possono iniettare
direttamente
• Dipendono dall’OM
• generation/Magento
vargenerationMagentoShippingModelRateResultFactory
Modello - Factories
/**

* Class Shipping

* @package MeetmagentoShippingModelCarrier

*/



class Shippingmeetmagento extends MagentoShippingModelCarrierAbstractCarrier implements

MagentoShippingModelCarrierCarrierInterface

{

protected $_code = 'shippingmeetmagento';

protected $_isFixed = true;

protected $_rateResultFactory;

protected $_rateMethodFactory;
/**

* 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
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 -> getProductPrice()
• 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="MeetmagentoShippingPluginBlockCatalogProduct
AbstractProductPlugin1" disabled="false" sortOrder="100"/>

<plugin name="MeetmagentoShippingPlugin2" type="MeetmagentoShippingPluginBlockCatalogProduct
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::getProductPrice()
Classe 2

Implementa 

getProductPrice()
Plugin 1

Before / Around / After
getProductPrice()
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
MagentoCatalogBlockProductListProductInterceptor) in realtà
stiamo osservando MagentoCatalogBlockProductAbstractProduct 

• Proprietà successive al $subject dipendono dalle proprietà del metodo
osservato getProductPrice(MagentoCatalogModelProduct $product)



• 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
MagentoCatalogBlockProductListProduct
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 ListProductInterceptor)

• La seconda proprietà è sempre il $proceed di
Closure (chiama il prossimo plugin o metodo)

• La regola di trasformazione è:
• getProductPrice($product)
• aroundGetProductPrice($subject)
public function aroundGetProductPrice(

$subject,

Closure $proceed,

$product

)

{

var_dump('Plugin1 - aroundGetProductPrice');
return $proceed($product);

}
Plugin - Risultato
Plugin - Helper
namespace MeetmagentoShippingPluginHelper;



use MagentoCatalogApiDataProductInterface;

use MeetmagentoShippingModelCarrierShipping as Shipping;



class Product

{

/**

* @var Shipping

*/

private $shippingModel;



/**

* Product constructor.

* @param Shipping $shippingModel

*/

public function __construct(Shipping $shippingModel)

{

$this->shippingModel = $shippingModel;

}



/**

* @param $subject

* @param ProductInterface $result

* @return ProductInterface

*/

public function afterInitProduct($subject, ProductInterface $result)

{

if ($this->shippingModel->isAvailableForProduct($result)) {

$result->setPrice($result->getPrice() * 1);
//$result->setPrice($result->getPrice() * 2);

}



return $result;

}

}
Plugin - Helper - Risultato
Plugin - Helper - Risultato
Frontend - Layout
Container Blocchi
Layouts
Frontend - Layout
• Il layout di una pagina è suddiviso in
2 componenti principali:
• Page layout
• Page configuration

/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
https://yameveo.com
https://twitter.com/yameveo
https://facebook.com/yameveo
https://github.com/Yameveo/meetmagento.git
https://twitter.com/enr79
http://www.slideshare.net/secret/Nl11vBIR3RifXq
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
Grazie per l’attenzione

Mais conteúdo relacionado

Mais procurados

Evoluindo a arquitetura de uma aplicação com AngularJS
Evoluindo a arquitetura de uma aplicação com AngularJSEvoluindo a arquitetura de uma aplicação com AngularJS
Evoluindo a arquitetura de uma aplicação com AngularJSRodrigo Branas
 
Magento 2.x. Samo życie.
Magento 2.x. Samo życie.Magento 2.x. Samo życie.
Magento 2.x. Samo życie.SNOW.DOG
 
Palestra PythonBrasil[8]
Palestra PythonBrasil[8]Palestra PythonBrasil[8]
Palestra PythonBrasil[8]Thiago Da Silva
 
Lan acceptance for carriage
Lan   acceptance for carriageLan   acceptance for carriage
Lan acceptance for carriagecomercio01
 
HTTP Interceptors com AngularJS
HTTP Interceptors com AngularJSHTTP Interceptors com AngularJS
HTTP Interceptors com AngularJSRodrigo Branas
 
Юнит тестирование в Zend Framework 2.0
Юнит тестирование в Zend Framework 2.0Юнит тестирование в Zend Framework 2.0
Юнит тестирование в Zend Framework 2.0zfconfua
 

Mais procurados (7)

Evoluindo a arquitetura de uma aplicação com AngularJS
Evoluindo a arquitetura de uma aplicação com AngularJSEvoluindo a arquitetura de uma aplicação com AngularJS
Evoluindo a arquitetura de uma aplicação com AngularJS
 
Magento 2.x. Samo życie.
Magento 2.x. Samo życie.Magento 2.x. Samo życie.
Magento 2.x. Samo życie.
 
Palestra PythonBrasil[8]
Palestra PythonBrasil[8]Palestra PythonBrasil[8]
Palestra PythonBrasil[8]
 
Lan acceptance for carriage
Lan   acceptance for carriageLan   acceptance for carriage
Lan acceptance for carriage
 
HTTP Interceptors com AngularJS
HTTP Interceptors com AngularJSHTTP Interceptors com AngularJS
HTTP Interceptors com AngularJS
 
Юнит тестирование в Zend Framework 2.0
Юнит тестирование в Zend Framework 2.0Юнит тестирование в Zend Framework 2.0
Юнит тестирование в Zend Framework 2.0
 
Introducción a Bolt
Introducción a BoltIntroducción a Bolt
Introducción a Bolt
 

Meet Magento Milano 2016

  • 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> Registration.php
 • Punto di entrata del modulo
 • Serve a Magento per identificare i moduli installati nel sistema Module.xml
 • Dichiarazione del modulo
  • 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;
 
 
 SERVICE CONTRACT
 • Insieme di interfacce PHP definite per un modulo
 • Suddiviso in: • Data Interface • Service Interface • Repository • Management • Metadata
 • Vantaggi: • Modularità • Gestione dipendenze con composer • Configurazione servizi come API
  • 7. Modello - ObjectManager /**
 * Class Shipping
 * @package MeetmagentoShippingModelCarrier
 */
 
 class Shippingmeetmagento extends MagentoShippingModelCarrierAbstractCarrier implements
 MagentoShippingModelCarrierCarrierInterface
 {
 protected $_code = 'shippingmeetmagento';
 protected $_isFixed = true; /**
 * 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 Magento FrameworkObjectManager ObjectManager 
 • ->create() • ->get()
 • Il $method corrisponde al nostro metodo di spedizione
 • Il $result è quello che vedremo apparire per selezionare il metodo di spedizione
  • 9. 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.php FACTORIES • Creano un istanza di classi che non si possono iniettare direttamente • Dipendono dall’OM • generation/Magento vargenerationMagentoShippingModelRateResultFactory
  • 10. Modello - Factories /**
 * Class Shipping
 * @package MeetmagentoShippingModelCarrier
 */
 
 class Shippingmeetmagento extends MagentoShippingModelCarrierAbstractCarrier implements
 MagentoShippingModelCarrierCarrierInterface
 {
 protected $_code = 'shippingmeetmagento';
 protected $_isFixed = true;
 protected $_rateResultFactory;
 protected $_rateMethodFactory; /**
 * 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)
  • 11. 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
  • 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 -> getProductPrice() • 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="MeetmagentoShippingPluginBlockCatalogProduct AbstractProductPlugin1" disabled="false" sortOrder="100"/>
 <plugin name="MeetmagentoShippingPlugin2" type="MeetmagentoShippingPluginBlockCatalogProduct AbstractProductPlugin2" 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::getProductPrice() Classe 2
 Implementa 
 getProductPrice() Plugin 1
 Before / Around / After getProductPrice()
  • 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 MagentoCatalogBlockProductListProductInterceptor) in realtà stiamo osservando MagentoCatalogBlockProductAbstractProduct 
 • Proprietà successive al $subject dipendono dalle proprietà del metodo osservato getProductPrice(MagentoCatalogModelProduct $product)
 
 • 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 MagentoCatalogBlockProductListProduct Interceptor)
 • 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 (chiama il prossimo plugin o metodo)
 • La regola di trasformazione è: • getProductPrice($product) • aroundGetProductPrice($subject) public function aroundGetProductPrice(
 $subject,
 Closure $proceed,
 $product
 )
 {
 var_dump('Plugin1 - aroundGetProductPrice'); return $proceed($product);
 }
  • 24. Plugin - Helper namespace MeetmagentoShippingPluginHelper;
 
 use MagentoCatalogApiDataProductInterface;
 use MeetmagentoShippingModelCarrierShipping as Shipping;
 
 class Product
 {
 /**
 * @var Shipping
 */
 private $shippingModel;
 
 /**
 * Product constructor.
 * @param Shipping $shippingModel
 */
 public function __construct(Shipping $shippingModel)
 {
 $this->shippingModel = $shippingModel;
 }
 
 /**
 * @param $subject
 * @param ProductInterface $result
 * @return ProductInterface
 */
 public function afterInitProduct($subject, ProductInterface $result)
 {
 if ($this->shippingModel->isAvailableForProduct($result)) {
 $result->setPrice($result->getPrice() * 1); //$result->setPrice($result->getPrice() * 2);
 }
 
 return $result;
 }
 }
  • 25. Plugin - Helper - Risultato
  • 26. Plugin - Helper - Risultato
  • 27. Frontend - Layout Container Blocchi Layouts
  • 28. Frontend - Layout • Il layout di una pagina è suddiviso in 2 componenti principali: • Page layout • Page configuration
 /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>

  • 29. 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();
 }
 }
 }

  • 30. 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
  • 33. 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