この記事はMagento Advent Calendar 2017の12日目です。
はじめに
Magento2では各処理の前後に条件付きですが、新しく別の処理を差し込むことが可能です
公式ドキュメント: Plugins (Interceptors)
上記ドキュメントのlimitationsに制約が書いてありますが、publicなメソッドならだいたいなんとかなると思います
今回は商品CSV取込Magento\CatalogImportExport\Model\Import\Product
に処理を差し込んでみます
下準備
下記の例ではベンダー名をClaves
、モジュール名をExtendCsvImport
にしています
ベンダー名は会社名や個人名に差し替えるといいです
モジュール登録
いつもの必須作業というかなんというか、モジュールを登録していきます
app/code/Claves/ExtendCsvImport/registration.php
<?php
use Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(
ComponentRegistrar::MODULE,
'Claves_ExtendCsvImport',
__DIR__
);
app/code/Claves/ExtendCsvImport/etc/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
<?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
<?php
public function validateRow(array $rowData, $rowNum)
{
// (...省略...)
}
validateRow
を実行前にSKUの重複チェック処理とproduct_websites
の空白チェック処理を追加します
app/code/Claves/ExtendCsvImport/Plugin/Magento/CatalogImportExport/Model/Import/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については制約がそれなりにありますが、うまく使いこなせれば割りと自然な形で実装できると思います