search
LoginSignup
2
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

Magento2 Plugins入門

この記事はMagento Advent Calendar 2017の12日目です。

はじめに

Magento2では各処理の前後に条件付きですが、新しく別の処理を差し込むことが可能です
公式ドキュメント: Plugins (Interceptors)
上記ドキュメントのlimitationsに制約が書いてありますが、publicなメソッドならだいたいなんとかなると思います

今回は商品CSV取込Magento\CatalogImportExport\Model\Import\Productに処理を差し込んでみます

下準備

下記の例ではベンダー名をClaves、モジュール名をExtendCsvImportにしています
ベンダー名は会社名や個人名に差し替えるといいです

モジュール登録

いつもの必須作業というかなんというか、モジュールを登録していきます

app/code/Claves/ExtendCsvImport/registration.php

registration.php
<?php
use Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(
    ComponentRegistrar::MODULE,
    'Claves_ExtendCsvImport',
    __DIR__
);

app/code/Claves/ExtendCsvImport/etc/module.xml

module.xml
<?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="Claves_ExtendCsvImport" setup_version="1.0.0">
        <sequence>
            <module name="Magento_CatalogImportExport"/>
        </sequence>
    </module>
</config>

sequenceブロックには処理を差し込みたい既存クラスのモジュール名を書いておきましょう

Dependency injection

app/code/Claves/ExtendCsvImport/etc/di.xml

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="Magento\CatalogImportExport\Model\Import\Product">
        <plugin name="Claves_ExtendCsvImport_Plugin_Magento_CatalogImportExport_Model_Import_Product"
                type="Claves\ExtendCsvImport\Plugin\Magento\CatalogImportExport\Model\Import\ProductPlugin"
                sortOrder="1"
        />
    </type>
</config>

typeブロック

nameには元のclassを指定します

pluginブロック

sortOrderは処理の差し込み順です。他のPluginsとの優先順位づけに利用します
nameは他のplugin名と被らないようにしましょう。Claves_ExtendCsvImport::CatalogImportExportのように記載しているPluginsもありました
typeは処理を差し込むphpファイルのパスと揃えてください

処理を差し込む

今回差し込む既存の処理はvalidateRowです。商品CSV取込のバリデーションを行っています

app/code/Magento/CatalogImportExport/Model/Import/Product.php

Product.php
<?php
public function validateRow(array $rowData, $rowNum)
{
    // (...省略...)
}

validateRowを実行前にSKUの重複チェック処理とproduct_websitesの空白チェック処理を追加します

app/code/Claves/ExtendCsvImport/Plugin/Magento/CatalogImportExport/Model/Import/ProductPlugin.php

ProductPlugin.php
<?php
namespace Claves\ExtendCsvImport\Plugin\Magento\CatalogImportExport\Model\Import;

use Magento\CatalogImportExport\Model\Import\Product;
use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface as ValidatorInterface;

class ProductPlugin extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
{
    /**
     * @var \Psr\Log\LoggerInterface
     */
    protected $logger;

    private $appliedSkus;

    /**
     * @param \Psr\Log\LoggerInterface $logger
     */
    public function __construct(\Psr\Log\LoggerInterface $logger)
    {
        $this->logger = $logger;
        $this->appliedSkus = array();
    }

    public function validateRow(array $rowData, $rowNum)
    {
    }

    public function _importData()
    {
    }

    public function getEntityTypeCode()
    {
        return 'catalog_product';
    }

    public function beforeValidateRow(Product $subject, array $rowData, $rowNum)
    {
        if (isset($this->_validatedRows[$rowNum])) {
            // check that row is already validated
            return array($rowData, $rowNum);
        }
        $this->_validatedRows[$rowNum] = true;

        if (empty($rowData[Product::COL_PRODUCT_WEBSITES])) {
            $subject->addRowError(ValidatorInterface::ERROR_INVALID_WEBSITE, $rowNum,
                Product::COL_PRODUCT_WEBSITES,
                Product::COL_PRODUCT_WEBSITES . ' is empty.');
        }
        $rowScope = $subject->getRowScope($rowData);
        // $this->logger->debug($rowScope . $rowData[Product::COL_SKU]);
        if (Product::SCOPE_DEFAULT == $rowScope && isset($this->appliedSkus[$rowData[Product::COL_SKU]])) {
            $subject->addRowError(ValidatorInterface::ERROR_DUPLICATE_UNIQUE_ATTRIBUTE, $rowNum,
                Product::COL_SKU,
                Product::COL_SKU . ' is duplicate.');
        }
        $this->appliedSkus[$rowData[Product::COL_SKU]] = true;
        return array($rowData, $rowNum);
    }
}

AbstractEntityを継承しているためちょっと長いですが、beforeValidateRowだけが大事です

実装するメソッドのルール

  • 処理名はbeforeValidateRowと差し込む処理の前にbeforeをつけてキャメルケースにします
  • 既存の処理の後に差し込む場合はafter、前後に差し込む場合はaroundをつけます。
  • 第一引数は既存のクラス(のオブジェクト)、第二引数以降は既存の処理の引数です
  • 既存の処理の引数と同じオブジェクトを返却します。複数ある場合はArrayに詰めて返します

処理の中身について詳細には触れませんがひとつだけ、第一引数で既存のクラスを渡されていることからも分かるように、publicメソッドならなんでも呼び出せます。上記例で言うと$subject->addRowError(...)でエラーメッセージを追加しています

所感

Magento2は既存の処理を拡張する仕組みが複数用意されていて、そのどれもが強力です。Pluginsについては制約がそれなりにありますが、うまく使いこなせれば割りと自然な形で実装できると思います

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2
Help us understand the problem. What are the problem?