Questa presentazione si focalizza sullo sviluppo di un modulo per Magento 2 in modo da integrare un metodo di spedizione personalizzato nel negozio.
Si trattano i seguenti punti:
Registrazione del modulo, file di configurazione e di sistema in Magento 2
Utilizzo dei Service Contract (Data Interface)
Object Manager e Dependency Injection
Creazione del Modulo (struttura e codice)
Classi di Modello
Container e Blocchi
Template e Layout
Listener before/after/around
Ordine di esecuzione dei plugin in Magento 2
Gestione dipendenze con composer, esempio di composer.json applicato al modulo
Durante la presentazioni saranno forniti esempi pratici e snippet di codice.
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
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
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
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');
}
....
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
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);
}