Magento 2 事件處理 (上)
Magento 內建有許多的事件,其實使用起來非常方便,設定上也不會太過於繁瑣,又可以取得想要的參數,今天我們就來看一下事件的呼叫是如何使用的。
1. 什麼是事件 ( Event )
事件 ( Event ) 是一種在 Magento 內實踐設計模式(Design Pattern)中觀察者模式(Observer Pattern)的一種實作方法。
而根據維基百科對於 觀察者模式 (Observer Pattern) 的定義:
一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實時事件處理系統。
對於設計模式( Design Pattern ) 有興趣的人,可以參閱 深入淺出設計模式 ( 傳送門 ) 這本書。
而 Magento 內因為已經幫我們實作完底層的結構,我們僅需要管理其用法,今天我們會在文章內說明如何使用,讓大家在撰寫 Magento 程式的時候更加順暢。
2. 三種事件處理
事件處理有分為三種,須依照不同的需求,放置在不同的位置,若前後端皆需使用的話,則放在通用的事件,而檔案名稱統一皆為 event.xml。
事件區分 | 路徑 | 檔名 |
前端 | vendor/extension/etc/frontend/ | event.xml |
後端 | vendor/extension/etc/adminhtml/ | event.xml |
通用 | vendor/extension/etc/ | event.xml |
3. xml 格式
在模組內註冊需要使用的事件,並指定 instance 到指定的 class ,下面的範例註冊了 controller_action_catalog_product_save_entity_after 這個事件,這是屬於一個原生的事件,而我們便可以從指定的 class 抓到相對應的內容。
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> <event name="controller_action_catalog_product_save_entity_after"> <observer name="custom_after_save_product_event_inventory" instance="Vendor\Extension\Observer\AfterSaveProductEvent" /> </event> </config>
其中裡面幾個參數:
* name – 每一個事件必須要有獨立且唯一的命名
* instance – 必須指定一個 Class 的 namespace,並且繼承 magento 的 interface
* disabled – 開啟或關閉此事件
* shared – 此 instance 是否要與其他事件共用,預設為 false
4. PHP 類別
由上面指定的 instance 對應到 Vendor\Extension\Observer\AfterSaveProductEvent 這個 class,而這邊要注意的是,務必要 implements 原生的 interface (Magento\Framework\Event\ObserverInterface)。
<?php namespace Vendor\Extension\Observer; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; /** * Class AfterSaveProductEvent * @package Vendor\Extension\Observer\Adminhtml */ class AfterSaveProductEvent implements ObserverInterface { /** * @param Observer $observer * @return AfterSaveProductEvent * @throws LocalizedException * @throws \Exception */ public function execute(Observer $observer) { $product = $observer->getData('product'); $controller = $observer->getData('controller'); return $this; } }
我們可以看到在第 26 行的地方,可以使用 getData 的方法,但是卻不知道有什麼參數可以取得。這時候,我們回到原生的 Magento 程式碼中第 138 行可以看到,在儲存完 product entity 之後會呼叫這個事件 ( Event ) ,並且會同時帶入兩個參數 controller 及 product。因此我們才可以在事件中直接取用。
原生程式:
Magento\Catalog\Controller\Adminhtml\Product\Save
<?php /** * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Catalog\Controller\Adminhtml\Product; use Magento\Backend\App\Action; use Magento\Catalog\Controller\Adminhtml\Product; use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\App\Request\DataPersistorInterface; /** * Class Save * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Save extends \Magento\Catalog\Controller\Adminhtml\Product { /** * @var Initialization\Helper */ protected $initializationHelper; /** * @var \Magento\Catalog\Model\Product\Copier */ protected $productCopier; /** * @var \Magento\Catalog\Model\Product\TypeTransitionManager */ protected $productTypeManager; /** * @var \Magento\Catalog\Api\CategoryLinkManagementInterface */ protected $categoryLinkManagement; /** * @var \Magento\Catalog\Api\ProductRepositoryInterface */ protected $productRepository; /** * @var DataPersistorInterface */ protected $dataPersistor; /** * @var StoreManagerInterface */ private $storeManager; /** * Save constructor. * * @param Action\Context $context * @param Builder $productBuilder * @param Initialization\Helper $initializationHelper * @param \Magento\Catalog\Model\Product\Copier $productCopier * @param \Magento\Catalog\Model\Product\TypeTransitionManager $productTypeManager * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ public function __construct( \Magento\Backend\App\Action\Context $context, Product\Builder $productBuilder, Initialization\Helper $initializationHelper, \Magento\Catalog\Model\Product\Copier $productCopier, \Magento\Catalog\Model\Product\TypeTransitionManager $productTypeManager, \Magento\Catalog\Api\ProductRepositoryInterface $productRepository ) { $this->initializationHelper = $initializationHelper; $this->productCopier = $productCopier; $this->productTypeManager = $productTypeManager; $this->productRepository = $productRepository; parent::__construct($context, $productBuilder); } /** * Save product action * * @return \Magento\Backend\Model\View\Result\Redirect * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function execute() { $storeId = $this->getRequest()->getParam('store', 0); $store = $this->getStoreManager()->getStore($storeId); $this->getStoreManager()->setCurrentStore($store->getCode()); $redirectBack = $this->getRequest()->getParam('back', false); $productId = $this->getRequest()->getParam('id'); $resultRedirect = $this->resultRedirectFactory->create(); $data = $this->getRequest()->getPostValue(); $productAttributeSetId = $this->getRequest()->getParam('set'); $productTypeId = $this->getRequest()->getParam('type'); if ($data) { try { $product = $this->initializationHelper->initialize( $this->productBuilder->build($this->getRequest()) ); $this->productTypeManager->processProduct($product); if (isset($data['product'][$product->getIdFieldName()])) { throw new \Magento\Framework\Exception\LocalizedException(__('Unable to save product')); } $originalSku = $product->getSku(); $product->save(); $this->handleImageRemoveError($data, $product->getId()); $this->getCategoryLinkManagement()->assignProductToCategories( $product->getSku(), $product->getCategoryIds() ); $productId = $product->getEntityId(); $productAttributeSetId = $product->getAttributeSetId(); $productTypeId = $product->getTypeId(); $this->copyToStores($data, $productId); $this->messageManager->addSuccessMessage(__('You saved the product.')); $this->getDataPersistor()->clear('catalog_product'); if ($product->getSku() != $originalSku) { $this->messageManager->addNoticeMessage( __( 'SKU for product %1 has been changed to %2.', $this->_objectManager->get( \Magento\Framework\Escaper::class )->escapeHtml($product->getName()), $this->_objectManager->get( \Magento\Framework\Escaper::class )->escapeHtml($product->getSku()) ) ); } $this->_eventManager->dispatch( 'controller_action_catalog_product_save_entity_after', ['controller' => $this, 'product' => $product] ); //... 略
5. 完成
如此一來,即可完成事件的註冊,及使用事件來取得資料。若是依照上面步驟沒有辦法成功的話,請記得試試執行 compile 的指令。
bin/magento setup:di:compile
下一個篇幅,我們將會介紹進階的事件處理給大家,請大家要定期收看喔!
更多的相關文章,此參閱我們Magento教學導覽!
相關文章:
我要留言