Skip to content

Plugins & Observers

Extension points, event subscriptions, and preference declarations

Plugin Declarations

The Quote module declares 5 plugins that intercept catalog and tier price operations to keep quote items synchronized.

Plugin Name Target Purpose
clean_quote_items_after_product_delete Catalog\Model\ResourceModel\Product Remove quote items when product is deleted
update_quote_items_after_product_save Catalog\Model\ResourceModel\Product Mark quotes for recollection on product changes
update_quote_items_after_tier_prices_update Catalog\Api\TierPriceStorageInterface Recollect quotes when tier prices change
quoteProductMassChange Catalog\Model\Product\Action Handle mass product status/attribute changes

Plugin Implementations

RemoveQuoteItems

Model\Product\Plugin\RemoveQuoteItems

afterDelete

When a product is deleted, this plugin removes all corresponding quote items to prevent orphaned cart items.

public function afterDelete(
    ProductResourceModel $subject,
    ProductResourceModel $result,
    AbstractModel $product
): ProductResourceModel {
    $this->quoteItemsCleaner->execute($product);
    return $result;
}

UpdateQuoteItems

Model\Product\Plugin\UpdateQuoteItems

afterSave

Marks quotes containing the updated product for totals recollection on next access.

public function afterSave(
    ProductResourceModel $subject,
    ProductResourceModel $result,
    AbstractModel $product
): ProductResourceModel {
    if ($this->isProductDataChanged($product)) {
        $this->markQuotesRecollect->execute([$product->getId()]);
    }
    return $result;
}

MarkQuotesRecollectMassDisabled

Model\Product\Plugin\MarkQuotesRecollectMassDisabled

afterUpdateAttributes

Handles mass product actions (bulk disable, attribute updates) by marking affected quotes for recollection.

Observer Declarations

The Quote module registers 5 observers across global and frontend scopes.

Observer Event Scope Purpose
CollectTotalsObserver sales_quote_address_collect_totals_before global VAT validation during totals collection
SubmitObserver sales_model_service_quote_submit_success global Post-order placement cleanup
SendInvoiceEmailObserver sales_order_invoice_pay global Trigger invoice email notification

Key Observers

SubmitObserver

Observer\SubmitObserver

CRITICAL

Handles post-order submission tasks including quote cleanup and order notifications.

public function execute(Observer $observer): void
{
    $quote = $observer->getEvent()->getQuote();
    $order = $observer->getEvent()->getOrder();

    // Quote is already marked inactive by QuoteManagement
    // This observer triggers additional order processing
}

Note: This observer fires after successful order creation. If payment fails, this observer is not called.

CollectTotalsObserver (Frontend)

Observer\Frontend\Quote\Address\CollectTotalsObserver

frontend

Validates customer VAT number during frontend totals collection and adjusts customer group if needed.

// Triggered by: sales_quote_address_collect_totals_before
// Validates VAT for EU customers
// May change customer tax group based on VAT validation result

Preference Declarations (41 total)

The Quote module defines 41 interface-to-implementation mappings in di.xml.

Cart Management

  • I CartRepositoryInterface QuoteRepository
  • I CartManagementInterface QuoteManagement
  • I CartItemRepositoryInterface Quote\Item\Repository
  • I CartTotalRepositoryInterface Cart\CartTotalRepository

Data Interfaces

  • I CartInterface Quote
  • I CartItemInterface Quote\Item
  • I AddressInterface Quote\Address
  • I PaymentInterface Quote\Payment

Guest APIs

  • I GuestCartManagementInterface GuestCart\GuestCartManagement
  • I GuestCartRepositoryInterface GuestCart\GuestCartRepository
  • I GuestCouponManagementInterface GuestCart\GuestCouponManagement

ID Resolution

  • I MaskedQuoteIdToQuoteIdInterface MaskedQuoteIdToQuoteId
  • I QuoteIdToMaskedQuoteIdInterface QuoteIdToMaskedQuoteId
  • I QuoteMutexInterface QuoteMutex

Extension Best Practices

DO: Use Totals Collectors

Add custom fees or discounts via sales.xml collectors, not plugins on collectTotals().

DO: Extend Validation Rules

Add custom quote validation via QuoteValidationComposite, not by overriding QuoteValidator.

DON'T: Modify Quote Directly

Never modify quote totals outside the collector system - they will be overwritten on next collectTotals().

DON'T: Skip QuoteMutex

Always use QuoteMutex when placing orders to prevent race condition duplicate orders.