LoginSignup
7
2

More than 5 years have passed since last update.

Magento2 Plugins入門

Last updated at Posted at 2017-12-12

この記事は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については制約がそれなりにありますが、うまく使いこなせれば割りと自然な形で実装できると思います

7
2
0

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
  3. You can use dark theme
What you can do with signing up
7
2