文章彙整

Magento 2 事件處理 (上)

By Sabina 9 months agoNo Comments
首頁  /  Magento  /  Magento-2  /  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 ) ,並且會同時帶入兩個參數 controllerproduct。因此我們才可以在事件中直接取用。

 

原生程式:

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教學導覽

 

相關文章:

Magento2 管理商店

Magento2 訂單管理

以上內容由Astralweb 歐斯瑞編寫製作

 000

推薦文章

Category:
  Magento-2

留下回應

你的電子郵件地址不會被公開.

取得獨家電子商務祕技

建立更好的策略靈感

跟上全球的網路趨勢

絕佳的電商解決方案

電子商務戰略全指南

每月發送電商戰略指南,只要填寫E-mail即可訂閱!

請到您的信箱確認,即可完成訂閱。