カスタム商品属性を配送料のフィルターとして追加する方法
公開: 2020-04-07多くの場合、Magento 2 では、条件の設定に使用できる標準の商品属性の数が制限されています。 ビジネス ニーズを満たすには、追加のカスタマイズが必要になります。
この記事では、それを達成する方法と、配送料のフィルターとしてカスタム商品属性を追加する方法を学びます。
ノート:
- GitHub で完全なコード例を参照してください。
- 配送料のフィルタとして「容積重量」属性を追加する例の最初の部分は、こちらから入手できます。
- オリジナルの Magento 2 Shipping Suite Ultimate モジュールが必要です。
目的を達成するために正確に何をすべきかを議論することにまっすぐ進みましょう。
目次
- カスタム商品属性の追加に関するステップバイステップガイド
- 手順 1. ベース ファイルを追加して新しいモジュールを作成する
- ステップ 2. モジュール構造を作成する
- ステップ 3. ユーザー インターフェイス
- 形
- グリッド
カスタム商品属性の追加に関するステップバイステップガイド
手順 1. ベース ファイルを追加して新しいモジュールを作成する
モジュールに名前を付けることから始めます。
> app/code/MageWorx/ShippingRateByProductAttribute/registration.php <?php /** * Copyright MageWorx. All rights reserved. * See LICENSE.txt for license details. */ \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'MageWorx_ShippingRateByProductAttribute', __DIR__ );
次に、その要件を検出して宣言します。 また、コンポーザーのモジュールに名前を付け、バージョンを設定し、簡単な説明を追加する必要があります。
> app/code/MageWorx/ShippingRateByProductAttribute/composer.json { "name": "mageworx/module-shipping-rate-by-product-attribute", "description": "Shipping Rules Extension: Adds product attribute to the Rates", "require": { "magento/module-shipping": ">=100.1.0 < 101", "magento/module-ui": ">=100.1.0 < 102", "mageworx/module-shippingrules": ">=2.7.1" }, "type": "magento2-module", "version": "1.0.0", "license": [ "OSL-3.0", "AFL-3.0" ], "autoload": { "files": [ "registration.php" ], "psr-4": { "MageWorx\\ShippingRateByProductAttribute\\": "" } } }
さらに、Magento 2 構成で初期名とバージョンを設定し、シーケンスを宣言できます。
> app/code/MageWorx/ShippingRateByProductAttribute/etc/module.xml <?xml version="1.0"?> <!-- /** * Copyright MageWorx. All rights reserved. * See LICENSE.txt for license details. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="MageWorx_ShippingRateByProductAttribute" setup_version="1.0.0"> <sequence> <module name="MageWorx_ShippingRules" /> </sequence> </module> </config>
ステップ 2. モジュール構造を作成する
管理者側から作成された「shippingnew」という名前の製品属性があるとします。 これはドロップダウン入力タイプで、「A、B、C、D」などの名前のオプションがほとんどありません。これらのオプションは、ゾーンごとに商品を発送する方法を示しています。 各値には独自の価格があり、最高価格の製品は、チェックアウト時に配送方法のコストを変更します。
まず、配送料の拡張条件用に別のテーブルを作成する必要があります。 後で、モデルの通常の拡張属性を使用してそれらを追加します (「送料」モデルは `\Magento\Framework\Model\AbstractExtensibleModel` を拡張します)。
> app/code/MageWorx/ShippingRateByProductAttribute/Setup/InstallSchema.php <?php /** * Copyright MageWorx. All rights reserved. * See LICENSE.txt for license details. */ namespace MageWorx\ShippingRateByProductAttribute\Setup; use Magento\Framework\DB\Ddl\Table; use Magento\Framework\Setup\InstallSchemaInterface; use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\SchemaSetupInterface; /** * Class InstallSchema */ class InstallSchema implements InstallSchemaInterface { /** * Installs DB schema for a module * * @param SchemaSetupInterface $setup * @param ModuleContextInterface $context * @return void * @throws \Zend_Db_Exception */ public function install(SchemaSetupInterface $setup, ModuleContextInterface $context) { $installer = $setup; $installer->startSetup(); $ratesTable = $installer->getTable(\MageWorx\ShippingRules\Model\Carrier::RATE_TABLE_NAME); /** * Create table 'mageworx_shippingrules_rates_shippingnew' */ $table = $installer->getConnection()->newTable( $installer->getTable('mageworx_shippingrules_rates_shippingnew') )->addColumn( 'rate_id', Table::TYPE_INTEGER, null, ['unsigned' => true, 'nullable' => false], 'Rate Id' )->addColumn( 'shippingnew', Table::TYPE_TEXT, '120', ['nullable' => false], 'shippingnew attribute value' )->addForeignKey( $installer->getFkName('mageworx_shippingrules_rates_shippingnew', 'rate_id', $ratesTable, 'rate_id'), 'rate_id', $ratesTable, 'rate_id', Table::ACTION_CASCADE )->addIndex( $installer->getIdxName( 'mageworx_shippingrules_rates_product_attributes', ['rate_id', 'shippingnew'], \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_UNIQUE ), ['rate_id', 'shippingnew'], ['type' => \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_UNIQUE] )->setComment( 'Product Attributes For Shipping Suite Rates' ); $installer->getConnection()->createTable($table); } }
次のようにテーブルに名前を付けました: `'mageworx_shippingrules_rates_shippingnew'`。 2列しかありません。 そのうちの 1 つは外部キーとして使用されます。 これは、Magento 2 の MageWorx Shipping Suite Ultimate モジュールの通常のテーブル「mageworx_shippingrules_rates」とリンクされる「rate_id」列です。別の列には、「shippingnew」属性の値が含まれます。
オブザーバーにカスタム データをテーブルにロード/保存/削除させる前に、少なくとも 2 つのモデル (通常のモデルとリソース モデル) を作成する必要があります。
> app/code/MageWorx/ShippingRateByProductAttribute/Model/ShippingNew.php <?php /** * Copyright MageWorx. All rights reserved. * See LICENSE.txt for license details. */ namespace MageWorx\ShippingRateByProductAttribute\Model; use Magento\Framework\Model\AbstractModel; /** * Class ShippingNew */ class ShippingNew extends AbstractModel { /** * Prefix of model events names * * @var string */ protected $_eventPrefix = 'mageworx_shippingnew'; /** * Parameter name in event * * In observe method you can use $observer->getEvent()->getObject() in this case * * @var string */ protected $_eventObject = 'shippingnew'; /** * Set resource model and Id field name * * @return void */ protected function _construct() { parent::_construct(); $this->_init('MageWorx\ShippingRateByProductAttribute\Model\ResourceModel\ShippingNew'); $this->setIdFieldName('rate_id'); } }
ノート:
- ` _eventPrefix ` は、モデル イベントを検出するために使用されます。
- `_eventObject` は、イベント オブジェクトにデータを格納するために使用されます。 この名前を使用して、イベント オブジェクトからモデルを取得できます。
- `$this->_init( 'MageWorx\ShippingRateByProductAttribute\Model\ResourceModel\ ShippingNew' );` は、モデルを対応するリソース モデルにリンクします。
- `$this->setIdFieldName( 'rate_id' );` は、テーブルのどのフィールドをキーとして使用する必要があるかを示します (通常は id と呼びます)
> app/code/MageWorx/ShippingRateByProductAttribute/Model/ResourceModel/ShippingNew.php <?php /** * Copyright MageWorx. All rights reserved. * See LICENSE.txt for license details. */ namespace MageWorx\ShippingRateByProductAttribute\Model\ResourceModel; use Magento\Framework\Model\ResourceModel\Db\AbstractDb; /** * Class ShippingNew */ class ShippingNew extends AbstractDb { /** * Resource initialization * * @return void */ protected function _construct() { $this->_init('mageworx_shippingrules_rates_shippingnew', 'rate_id'); } /** * @param $rateId * @param int $shippingNew * @return int * @throws \Magento\Framework\Exception\LocalizedException */ public function insertUpdateRecord($rateId, int $shippingNew) { $rowsAffected = $this->getConnection()->insertOnDuplicate( $this->getMainTable(), [ 'rate_id' => $rateId, 'shippingnew' => $shippingNew ] ); return $rowsAffected; } /** * @param $rateId * @return int * @throws \Magento\Framework\Exception\LocalizedException */ public function deleteRecord($rateId) { $rowsAffected = $this->getConnection()->delete( $this->getMainTable(), [ 'rate_id = ?' => $rateId ] ); return $rowsAffected; } }
ノート:
- $this->_init( 'mageworx_shippingrules_rates_shippingnew' , 'rate_id' ); メイン テーブル名と id フィールド名を設定します。
- public function insertUpdateRecord($rateId, int $shippingNew) は、カスタム テーブルの属性値を更新するのに役立つメソッドです。
- public function deleteRecord($rateId) は、列を削除するように設計されています。
後で、これらのメソッドをオブザーバーで使用します。
それでは、新しいデータを拡張属性として Shipping Rate モデルに追加しましょう。
> app/code/MageWorx/ShippingRateByProductAttribute/etc/extension_attributes.xml <?xml version="1.0"?> <!-- /** * Copyright MageWorx. All rights reserved. * See LICENSE.txt for license details. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd"> <!-- Rate Extension --> <extension_attributes for="MageWorx\ShippingRules\Api\Data\RateInterface"> <attribute code="shippingnew" type="int"> <join reference_table="mageworx_shippingrules_rates_shippingnew" reference_field="rate_id" join_on_field="rate_id"> <field>shippingnew</field> </join> </attribute> </extension_attributes> </config>
また、カスタム条件の通常の操作も処理する必要があります。
> app/code/MageWorx/ShippingRateByProductAttribute/etc/events.xml <?xml version="1.0"?> <!-- /** * Copyright MageWorx. All rights reserved. * See LICENSE.txt for license details. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> <!-- Add Extension Attributes to the Rates Collection --> <!-- Save custom attribute value during rate saving --> <event name="mageworx_shippingrules_rate_save_after"> <observer name="mageworx_save_shippingnew_attribute" instance="MageWorx\ShippingRateByProductAttribute\Observer\SaveShippingNewRateAttribute" /> </event> <!-- Add custom attribute value to the rates collection --> <event name="rates_collection_render_filters_before"> <observer name="mageworx_add_shippingnew_attribute" instance="MageWorx\ShippingRateByProductAttribute\Observer\AddShippingNewToRatesCollection" /> </event> <!-- Take care of filtering the rates grid --> <event name="mageworx_suitable_rates_collection_load_before"> <observer name="mageworx_filter_rates_by_shippingnew_attribute" instance="MageWorx\ShippingRateByProductAttribute\Observer\FilterRatesCollectionByShippingNewAttribute" /> </event> <!-- 3 event observers for the Export/Import rates with custom attribute in conditions --> <event name="mageworx_rates_export_collection_join_linked_tables_after"> <observer name="mageworx_join_shipping_new_table_to_export_rates_collection" instance="MageWorx\ShippingRateByProductAttribute\Observer\JoinShippingNewTableToExportRatesCollection" /> </event> <event name="mageworx_filter_rates_data_before_insert"> <observer name="mageworx_remove_shipping_new_before_insert" instance="MageWorx\ShippingRateByProductAttribute\Observer\RemoveShippingNewBeforeInsert" /> </event> <event name="mageworx_shippingrules_import_insert_rates"> <observer name="mageworx_shippingrules_import_insert_update_shipping_new" instance="MageWorx\ShippingRateByProductAttribute\Observer\InsertUpdateShippingNewDuringImport" /> </event> </config>
最初のイベントは、rates 条件のカスタム属性値を保存/更新/削除するためのものです。
次の 2 つのイベントは、この属性値をコレクションに追加するためのものです。
最後の 3 つのイベントは、インポート/エクスポート機能用です。
それらを 1 つずつ詳細に分析してみましょう。
> app/code/MageWorx/ShippingRateByProductAttribute/Observer/SaveShippingNewRateAttribute.php <?php /** * Copyright MageWorx. All rights reserved. * See LICENSE.txt for license details. */ namespace MageWorx\ShippingRateByProductAttribute\Observer; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Exception\LocalizedException; use MageWorx\ShippingRules\Api\Data\RateInterface; /** * Class SaveShippingNewRateAttribute * * Saves custom attribute (`shippingnew`) values after model was saved */ class SaveShippingNewRateAttribute implements ObserverInterface { /** * @var \MageWorx\ShippingRateByProductAttribute\Model\ResourceModel\ShippingNew */ private $resource; /** * @var \Magento\Framework\Message\ManagerInterface */ private $messagesManager; /** * SaveVolumeWeightRateAttribute constructor. * * @param \MageWorx\ShippingRateByProductAttribute\Model\ResourceModel\ShippingNew $resource * @param \Magento\Framework\Message\ManagerInterface $messagesManager */ public function __construct( \MageWorx\ShippingRateByProductAttribute\Model\ResourceModel\ShippingNew $resource, \Magento\Framework\Message\ManagerInterface $messagesManager ) { $this->resource = $resource; $this->messagesManager = $messagesManager; } /** * @param Observer $observer * @return void */ public function execute(Observer $observer) { /** @var RateInterface $model */ $model = $observer->getEvent()->getData('rate'); if (!$model instanceof RateInterface) { return; } $shippingNewValue = $model->getData('shippingnew') !== '' ? $model->getData('shippingnew') : null; if ($shippingNewValue === null) { try { $this->resource->deleteRecord($model->getRateId()); } catch (LocalizedException $deleteException) { $this->messagesManager->addErrorMessage( __('Unable to delete the Shipping Category for the Rate %1', $model->getRateId()) ); } } else { try { $this->resource->insertUpdateRecord($model->getRateId(), $shippingNewValue); } catch (LocalizedException $saveException) { $this->messagesManager->addErrorMessage( __('Unable to save the Shipping Category for the Rate %1', $model->getRateId()) ); } } return; } }
それはそれと同じくらい簡単です。 レートを保存するときは、カスタム属性値も保存する必要があります。 その値が「null」に等しい場合は、レコードを削除するだけです。
> app/code/MageWorx/ShippingRateByProductAttribute/Observer/AddShippingNewToRatesCollection.php <?php /** * Copyright MageWorx. All rights reserved. * See LICENSE.txt for license details. */ namespace MageWorx\ShippingRateByProductAttribute\Observer; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; /** * Class AddShippingNewToRatesCollection * * Adds custom attribute to the rates collection. * It will be used later during quote validation. */ class AddShippingNewToRatesCollection implements ObserverInterface { /** * Join custom table to the rates collection to obtain the `shippingnew` attribute anywhere in the code. * * @param Observer $observer * @return void */ public function execute(Observer $observer) { /** @var \MageWorx\ShippingRules\Model\ResourceModel\Rate\Collection $collection */ $collection = $observer->getEvent()->getData('collection'); if (!$collection instanceof \MageWorx\ShippingRules\Model\ResourceModel\Rate\Collection) { return; } if ($collection->isLoaded()) { return; } $joinTable = $collection->getTable('mageworx_shippingrules_rates_shippingnew'); $collection->getSelect() ->joinLeft( $joinTable, '`main_table`.`rate_id` = `' . $joinTable . '`.`rate_id`', ['shippingnew'] ); } }
検証を利用できるようにするために、顧客がチェックアウトまたは配送料の見積もりにアクセスしたときに、カスタム属性を使用してテーブルを通常の料金テーブルに結合しましょう。
> app/code/MageWorx/ShippingRateByProductAttribute/Observer/FilterRatesCollectionByShippingNewAttribute.php <?php /** * Copyright MageWorx. All rights reserved. * See LICENSE.txt for license details. */ namespace MageWorx\ShippingRateByProductAttribute\Observer; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; /** * Class FilterRatesCollectionByShippingNewAttribute * * Filter rates collection before we load it by custom attribute: shippingnew. * * For more details * * @see \MageWorx\ShippingRules\Model\Carrier\Artificial::getSuitableRatesAccordingRequest() * */ class FilterRatesCollectionByShippingNewAttribute implements ObserverInterface { /** * @param Observer $observer * @return void */ public function execute(Observer $observer) { /** @var \MageWorx\ShippingRules\Model\ResourceModel\Rate\Collection $collection */ $collection = $observer->getEvent()->getData('rates_collection'); if (!$collection instanceof \MageWorx\ShippingRules\Model\ResourceModel\Rate\Collection) { return; } /** @var \Magento\Quote\Model\Quote\Address\RateRequest $request */ $request = $observer->getEvent()->getData('request'); if (!$request instanceof \Magento\Quote\Model\Quote\Address\RateRequest) { return; } /** @var \Magento\Quote\Model\Quote\Item[] $items */ $items = $request->getAllItems() ?? []; $shippingCategories = []; foreach ($items as $item) { $value = $item->getProduct()->getData('shippingnew'); if ($value !== null) { $shippingCategories[] = $value; } } $shippingCategories = array_unique($shippingCategories); $joinTable = $collection->getTable('mageworx_shippingrules_rates_shippingnew'); $collection->getSelect() ->joinLeft( ['sn' => $joinTable], '`main_table`.`rate_id` = `sn`.`rate_id`', ['shippingnew'] ); $collection->getSelect()->where( "`sn`.`shippingnew` IN (?)", $shippingCategories ); } }
これは、スタック内で最も複雑なオブザーバーです。 これは、顧客のカートからすべての属性値 (`$shippingCategories`) を収集するように設計されており、属性値をフィルターとして通常の料金コレクションに追加します (テーブルは既に結合されています)。 シンプルにするために、「フィルター」と名付けました。 作業が完了すると、現在のカート項目の実際の配送料が顧客に表示されます。
別の 3 つのイベント オブザーバーは、配送料のエクスポートおよびインポート中にカスタム データを追加および受信するように設計されています。 ブログ投稿ではそのコードをスキップしますが、ソース コードと共にリポジトリで利用できるようになります。
ステップ 3. ユーザー インターフェイス
属性をグリッドと配送料のフォームに追加します。
形
> app/code/MageWorx/ShippingRateByProductAttribute/view/adminhtml/ui_component/mageworx_shippingrules_rate_form.xml <?xml version="1.0" encoding="UTF-8"?> <!-- /** * Copyright MageWorx. All rights reserved. * See LICENSE.txt for license details. */ --> <form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> <fieldset name="conditions"> <field name="shippingnew"> <argument name="data" xsi:type="array"> <item name="options" xsi:type="object">MageWorx\ShippingRateByProductAttribute\Model\Config\Source\ShippingCategory</item> <item name="config" xsi:type="array"> <item name="label" xsi:type="string" translate="true">Shipping Category</item> <item name="dataType" xsi:type="string">int</item> <item name="formElement" xsi:type="string">select</item> <item name="dataScope" xsi:type="string">shippingnew</item> <item name="source" xsi:type="string">mageworx_shippingrules_rate_form.custom_attributes</item> </item> </argument> </field> </fieldset> </form>
グリッド
> app/code/MageWorx/ShippingRateByProductAttribute/view/adminhtml/ui_component/mageworx_shippingrules_rates_regular_listing.xml <?xml version="1.0"?> <!-- Copyright MageWorx. All rights reserved. See LICENSE.txt for license details. --> <listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Ui/etc/ui_configuration.xsd"> <columns name="mageworx_shippingrules_rates_columns"> <column name="shippingnew"> <argument name="data" xsi:type="array"> <item name="options" xsi:type="object">MageWorx\ShippingRateByProductAttribute\Model\Config\Source\ShippingCategory</item> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">select</item> <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item> <item name="dataType" xsi:type="string">select</item> <item name="label" xsi:type="string" translate="true">Shipping Category</item> <item name="visible" xsi:type="boolean">true</item> <item name="sortOrder" xsi:type="number">40</item> <item name="editor" xsi:type="string">select</item> </item> </argument> </column> </columns> </listing>
ご覧のとおり、これらのファイルではカスタム ソース モデルを使用しています。 作成しましょう。 対応する属性 (`shippingnew`) をロードし、利用可能なすべての値を提供します。
> app/code/MageWorx/ShippingRateByProductAttribute/Model/Config/Source/ShippingCategory.php <?php /** * Copyright MageWorx. All rights reserved. * See LICENSE.txt for license details. */ namespace MageWorx\ShippingRateByProductAttribute\Model\Config\Source; use Magento\Framework\Exception\LocalizedException; /** * Class ShippingCategory * * Obtain options for specified product attribute */ class ShippingCategory extends \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource { /** * @var \Magento\Catalog\Api\ProductAttributeRepositoryInterface */ protected $productAttributeRepository; /** * @var \Psr\Log\LoggerInterface */ protected $logger; /** * ShippingCategory constructor. * * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $productAttributeRepository * @param \Psr\Log\LoggerInterface $logger */ public function __construct( \Magento\Catalog\Api\ProductAttributeRepositoryInterface $productAttributeRepository, \Psr\Log\LoggerInterface $logger ) { $this->productAttributeRepository = $productAttributeRepository; $this->logger = $logger; } /** * @inheritDoc */ public function getAllOptions() { if (empty($this->_options)) { try { /** @var \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute */ $attribute = $this->productAttributeRepository->get('shippingnew'); $this->_options = $attribute->usesSource() ? $attribute->getSource()->getAllOptions() : []; } catch (LocalizedException $localizedException) { $this->logger->critical($localizedException->getLogMessage()); } } return $this->_options; } }
コードの部分は非常に単純です。 属性リポジトリを使用して属性をロードし、そこからすべてのオプション (値) を取得します。 「shippingnew」コードを持つ属性は管理パネルで作成する必要があり、事前定義されたオプション (値) を持つドロップダウン入力タイプが必要であることに注意してください。 これは、[ストア] > [属性] > [製品] メニューから実行できます。 商品に使用している属性セットにこの属性を追加することを忘れないでください。
すべてが完了したら、モジュールを有効にして「setup:upgrade」を実行するだけです。 キャッシュは自動的にクリアされます。
送料グリッド (「ストア > 配送料」) に移動すると、新しい列が表示されます。
その条件は料金フォーム内で利用できます。
対応する配送方法で「複数料金の価格計算」設定を「最大料金で料金を使用」に設定した場合、配送料の計算時に最も高い料金の料金が使用されます。
スクリーンショットの形式でどのように機能するかの小さな例を次に示します。
- 製品を設定する
- 料金を設定する
- 価格計算アルゴリズムを設定する (配送方法フォームで)
- カート(フロントエンド)で選択した商品に対応する方法の送料を確認してください。
これは、Shipping Suite モジュールの機能のすべてではありません。 希望する結果が得られるように、自由に設定を変更してください。
ご不明な点がございましたら、喜んでお答えいたします。 したがって、下の専用フィールドにコメントを残してください。