magento2 plugin

在 Magento 2 中,插件被用來擴展原生類的行爲。這些原生的行爲能夠經過建立插件來進行修改,但插件改變的是這些類的方法的行爲,而不是類自己。就是說,當某個類被其餘類所繼承,那麼做用在該類的插件並不會影響繼承這個類的其餘類的行爲。php

p.s. 在 di.xml 中經過插件(plugin)能夠改變類的某個方法,而 preference 則能夠改變整個類。html

插件的定義

插件經過 di.xml 定義,如下是插件在該配置文件中的格式及參數說明:數組

<config>
    <type name="[ObservedType]">
        <plugin name="[pluginName]"
        type="[PluginClassName]"
        sortOrder="1"
        disabled="true" />
    </type>
</config>
  • [ObservedType] - 插件的注入目標類名。包含空間名,如 Magento\Customer\Model\Resource\Visitoride

  • [pluginName] - 插件名。全站同一個域內惟一,如 catalogLog函數

  • [PluginClassName] - 插件的實體類名。包含空間名,如 Magento\Catalog\Model\Plugin\Logfetch

  • sortOrder - 執行順序。當同一個類有多個插件時,根據這個參數決定插件的執行順序ui

  • disabled - 該參數爲 true 時不執行該插件this

插件的方法

構造函數

...插件

Before-Listener 方法

當改變一個原生方法的參數,或者在執行這個原生方法以前添加一些行爲的時候,就須要在插件中用到 Before-Listener 方法。code

Before-Listener 方法的命名規則是,將原生方法的首字母改爲大寫,並在原生方法名前添加前綴 before。

Before-Listener 方法的第一個參數是原生方法所屬類的實例。若是原生方法有返回值,則第二個參數爲原生方法的返回值。參數類型能夠跟原生方法不一致,至關於在 di.xml 中用 argument 節點指定了對應參數的類。

若是須要修改原生方法的參數內容,能夠在 Before-Listener 方法中返回一個數組,數組第一到 n 個元素將依次爲原生方法的入參。

以 MagentoPersistent 模塊爲例,其 di.xml 定義了:

<type name="Magento\Quote\Model\AddressAdditionalDataProcessor">
    <plugin name="persistent_remember_me_checkbox_processor" type="Magento\Persistent\Model\Checkout\AddressDataProcessorPlugin" />
</type>

修改的是原生 MagentoQuoteModelAddressAdditionalDataProcessor 類的 process 方法:

public function process(AddressAdditionalDataInterface $additionalData)
{
    return;
}

在 MagentoPersistentModelCheckoutAddressDataProcessorPlugin 中指定了該方法的參數類型,並添加了在其執行前的行爲:

public function beforeProcess(AddressAdditionalDataProcessor $subject, AddressAdditionalData $additionalData)
{
    if (!$this->persistentHelper->isEnabled() || !$this->persistentHelper->isRememberMeEnabled()) {
        return;
    }
    $checkboxStatus = $additionalData->getExtensionAttributes()->getPersistentRememberMe();
    $isRememberMeChecked = empty($checkboxStatus) ? false : true;
    $this->persistentSession->setRememberMeChecked($isRememberMeChecked);
    $this->checkoutSession->setRememberMeChecked($isRememberMeChecked);
}
After-Listener 方法

當改變一個原生方法的返回值,或者在執行這個原生方法以後添加一些行爲的時候,就須要在插件中用到 After-Listener 方法。

After-Listener 方法的命名規則是,將原生方法的首字母改爲大寫,並在原生方法名前添加前綴 after。

After-Listener 方法的第一個參數是原生方法所屬類的實例。若是原生方法有返回值,則第二個參數爲原生方法的返回值。

After-Listener 方法的返回值類型與原生方法的返回值一致。

以 MagentoCatalog 模塊爲例,其 di.xml 定義了:

<type name="Magento\Customer\Model\Resource\Visitor">
    <plugin name="catalogLog" type="Magento\Catalog\Model\Plugin\Log" />
</type>

修改的是原生 MagentoCustomerModelResourceVisitor 類的 clean 方法:

public function clean(\Magento\Customer\Model\Visitor $object)
{
    $cleanTime = $object->getCleanTime();
    $connection = $this->getConnection();
    $timeLimit = $this->dateTime->formatDate($this->date->gmtTimestamp() - $cleanTime);
    while (true) {
        $select = $connection->select()->from(
            ['visitor_table' => $this->getTable('customer_visitor')],
            ['visitor_id' => 'visitor_table.visitor_id']
        )->where(
            'visitor_table.last_visit_at < ?',
            $timeLimit
        )->limit(
            100
        );
        $visitorIds = $connection->fetchCol($select);
        if (!$visitorIds) {
            break;
        }
        $condition = ['visitor_id IN (?)' => $visitorIds];
        $connection->delete($this->getTable('customer_visitor'), $condition);
    }

    return $this;
}

在 MagentoCatalogModelPluginLog 中添加了在其執行後的行爲:

public function afterClean(\Magento\Customer\Model\Resource\Visitor $subject, $logResourceModel)
{
    $this->_productCompareItem->clean();
    return $logResourceModel;
}
Around-Listener 方法

須要同時改變一個原生方法的參數及返回值,或者在執行這個原生方法先後都添加一些行爲的時候,就用到 Around-Listener 方法。

Around-Listener 方法的命名規則是,將原生方法的首字母改爲大寫,並在原生方法名前添加前綴 around。

Around-Listener 方法的第一個參數是原生方法所屬類的實例;第二個參數是下一個插件的類名或方法名。若是原生方法有參數,則以同一順序依次跟在第二個參數以後。

Around-Listener 方法的返回值類型與原生方法的返回值一致。

以 MagentoCatalog 模塊爲例,其 di.xml 定義了:

<type name="Magento\Eav\Model\Entity\Attribute\Set">
    <plugin name="invalidateEavIndexerOnAttributeSetSave" type="Magento\Catalog\Model\Indexer\Product\Eav\Plugin\AttributeSet" />
</type>

修改的是原生 MagentoEavModelEntityAttributeSet 類的 save 方法(繼承自 MagentoFrameworkModelAbstractModel):

public function save()
{
    $this->_getResource()->save($this);
    return $this;
}

在 MagentoCatalogModelIndexerProductEavPluginAttributeSet 中添加了在其執行先後的行爲:

public function aroundSave(\Magento\Eav\Model\Entity\Attribute\Set $subject, \Closure $proceed)
{
    $requiresReindex = false;
    if ($subject->getId()) {
        /** @var \Magento\Eav\Model\Entity\Attribute\Set $originalSet */
        $originalSet = clone $subject;
        $originalSet->initFromSkeleton($subject->getId());
        $originalAttributeCodes = array_flip($this->_attributeFilter->filter($originalSet));
        $subjectAttributeCodes = array_flip($this->_attributeFilter->filter($subject));
        $requiresReindex = (bool)count(array_merge(
            array_diff_key($subjectAttributeCodes, $originalAttributeCodes),
            array_diff_key($originalAttributeCodes, $subjectAttributeCodes)
        ));
    }
    $result = $proceed();
    if ($requiresReindex) {
        $this->_indexerEavProcessor->markIndexerAsInvalid();
    }
    return $result;
}

插件的優先處理

當多個插件同時指向同一個原生方法時,將按如下順序執行:

  1. 執行 sortOrder 最小的插件的 Before-Listener 方法

  2. 執行 sortOrder 最小的插件的 Around-Listener 方法

  3. sortOrder 值從小到大順序執行插件的其餘 Before-Listener 方法

  4. sortOrder 值從小到大順序執行插件的其餘 Around-Listener 方法

  5. sortOrder 值從大到小順序執行插件的 After-Listener 方法

參考

http://devdocs.magento.com/gu...

相關文章
相關標籤/搜索