Plugins & Observers
Interception points, DI preferences, and event observers
Interception points, DI preferences, and event observers
Magento_ConfigurableProduct uses an extensive plugin architecture to integrate with the catalog, cart, checkout, sales, and inventory systems. The module registers 18 plugins and 1 observer to intercept key operations.
These preferences define the default implementations for configurable product service contracts:
| Interface | Implementation |
|---|---|
| ConfigurableProductManagementInterface | Model\ConfigurableProductManagement |
| LinkManagementInterface | Model\LinkManagement |
| OptionRepositoryInterface | Model\OptionRepository |
| Data\OptionInterface | Model\Product\Type\Configurable\Attribute |
| Data\OptionValueInterface | Model\Product\Type\Configurable\OptionValue |
| Data\ConfigurableItemOptionValueInterface | Model\Quote\Item\ConfigurableItemOptionValue |
| Pricing\Price\PriceResolverInterface | Pricing\Price\ConfigurablePriceResolver |
| Pricing\Price\ConfigurableOptionsProviderInterface | Pricing\Price\ConfigurableOptionsProvider |
| Pricing\Price\LowestPriceOptionsProviderInterface | Pricing\Price\LowestPriceOptionsProvider |
| Model\AttributeOptionProviderInterface | Model\AttributeOptionProvider |
| Pricing\Price\ConfigurableOptionsFilterInterface | Pricing\Price\ConfigurableOptionsCompositeFilter |
| Target Class | Plugin Name | Plugin Class | Sort |
|---|---|---|---|
CatalogInventory\Model\Quote\Item\QuantityValidator\Initializer\Option |
configurable_product | Model\Quote\Item\QuantityValidator\Initializer\Option\Plugin\ConfigurableProduct | 50 |
Catalog\Model\Product\CartConfiguration |
configurable_product | Model\Product\CartConfiguration\Plugin\Configurable | 50 |
Quote\Model\Quote\Item\Repository |
configurable | Model\Quote\Item\CartItemProcessor (Proxy) | - |
| Target Class | Plugin Name | Plugin Class | Sort |
|---|---|---|---|
Catalog\Api\ProductRepositoryInterface |
configurableProductSaveOptions | Model\Plugin\ProductRepositorySave | 10 |
Catalog\Model\Product\Type |
configurable_output | Model\Product\Type\Plugin | - |
Catalog\Model\Product\TypeTransitionManager |
configurable_product_transition | Model\Product\TypeTransitionManager\Plugin\Configurable | 50 |
Catalog\Model\Entity\Product\Attribute\Group\AttributeMapperInterface |
configurable_product | Model\Entity\Product\Attribute\Group\AttributeMapper\Plugin | 50 |
Catalog\Model\Product |
product_identities_extender | Model\Plugin\ProductIdentitiesExtender | - |
Catalog\Helper\Product\Configuration |
configurable_product | Helper\Product\Configuration\Plugin | 50 |
ProductVideo\Block\Product\View\Gallery |
product_video_gallery | Block\Plugin\Product\Media\Gallery | - |
| Target Class | Plugin Name | Plugin Class | Sort |
|---|---|---|---|
Catalog\Model\Product\Attribute\Backend\Price |
configurable | Model\Plugin\PriceBackend | 100 |
Eav\Model\Entity\Attribute\Backend\AbstractBackend |
ConfigurableProduct::skipValidation | Plugin\Model\Attribute\Backend\AttributeValidation | - |
Catalog\Model\Product\Pricing\Renderer\SalableResolver |
configurable | Plugin\Catalog\Model\Product\Pricing\Renderer\SalableResolver | - |
| Target Class | Plugin Name | Plugin Class | Sort |
|---|---|---|---|
Sales\Model\Order\Admin\Item |
configurable_product | Model\Order\Admin\Item\Plugin\Configurable | 50 |
SalesRule\Model\Rule\Condition\Product |
apply_rule_on_configurable_children | Plugin\SalesRule\Model\Rule\Condition\Product | - |
Tax\Model\Sales\Total\Quote\CommonTaxCollector |
apply_tax_class_id | Plugin\Tax\Model\Sales\Total\Quote\CommonTaxCollector | - |
| Target Class | Plugin Name | Plugin Class | Sort |
|---|---|---|---|
CatalogInventory\Model\ResourceModel\Stock\Item |
updateStockChangedAuto | Model\Plugin\UpdateStockChangedAuto | - |
Critical plugin that handles saving configurable product options and links:
class ProductRepositorySave
{
/**
* Processes configurable_product_options and configurable_product_links
* extension attributes after product save
*/
public function afterSave(
ProductRepositoryInterface $subject,
ProductInterface $product
) {
if ($product->getTypeId() !== Configurable::TYPE_CODE) {
return $product;
}
$extensionAttributes = $product->getExtensionAttributes();
// Save configurable options (super attributes)
if ($extensionAttributes->getConfigurableProductOptions()) {
$this->saveOptions($product, $extensionAttributes);
}
// Save product links (children)
if ($extensionAttributes->getConfigurableProductLinks()) {
$this->saveLinks($product, $extensionAttributes);
}
return $product;
}
}
Skips EAV backend validation for child attributes on configurable products:
class AttributeValidation
{
/**
* Skip validation for attributes that shouldn't be required on configurable products
* (e.g., price, weight - these come from children)
*/
public function aroundValidate(
AbstractBackend $subject,
callable $proceed,
DataObject $object
) {
if ($this->shouldSkipValidation($object, $subject)) {
return true;
}
return $proceed($object);
}
}
Triggers configurable parent reindex when child stock changes:
class UpdateStockChangedAuto
{
/**
* When child product stock changes, parent configurable needs reindex
*/
public function afterSave(
StockItemResource $subject,
StockItemResource $result,
AbstractModel $stockItem
) {
$productId = $stockItem->getProductId();
$parentIds = $this->configurable->getParentIdsByChild($productId);
foreach ($parentIds as $parentId) {
$this->indexer->reindexRow($parentId);
}
return $result;
}
}
Applies cart price rules to configurable children:
class Product
{
/**
* When evaluating sales rules, check the simple child product's attributes
* not the configurable parent's attributes
*/
public function beforeValidate(
ProductCondition $subject,
AbstractModel $model
) {
$product = $model->getProduct();
if ($product->getTypeId() === Configurable::TYPE_CODE) {
// Get the actual child product for rule evaluation
$childProduct = $this->getChildProduct($model);
$model->setProduct($childProduct);
}
return [$model];
}
}
Magento_ConfigurableProduct registers one observer for admin attribute form handling:
product_attribute_form_build_main_tab
Hides attributes that cannot be used as super attributes (only 'select' type attributes with global scope can be configurable axes).
class HideUnsupportedAttributeTypes implements ObserverInterface
{
private $supportedTypes = ['select'];
public function execute(Observer $observer)
{
$form = $observer->getForm();
$attribute = $observer->getAttribute();
if (!in_array($attribute->getFrontendInput(), $this->supportedTypes)) {
// Hide "Use To Create Configurable Product" option
$configurableField = $form->getElement('is_configurable');
if ($configurableField) {
$configurableField->setDisabled(true);
}
}
}
}
The module defines several virtual types for pricing resolution:
<!-- Price Pool for Configurable Products -->
<virtualType name="Magento\ConfigurableProduct\Pricing\Price\Pool"
type="Magento\Framework\Pricing\Price\Pool">
<arguments>
<argument name="prices">
<item name="regular_price">ConfigurableRegularPrice</item>
<item name="final_price">FinalPrice</item>
</argument>
<argument name="target">Magento\Catalog\Pricing\Price\Pool</argument>
</arguments>
</virtualType>
<!-- Final Price Resolver Chain -->
<virtualType name="ConfigurableFinalPriceResolver"
type="ConfigurablePriceResolver">
<arguments>
<argument name="priceResolver">FinalPriceResolver</argument>
</arguments>
</virtualType>
<!-- Regular Price Resolver Chain -->
<virtualType name="ConfigurableRegularPriceResolver"
type="ConfigurablePriceResolver">
<arguments>
<argument name="priceResolver">RegularPriceResolver</argument>
</arguments>
</virtualType>