Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Plugin Ordering (sortOrder) and Conflicts With Other Modules

Plugin Ordering (sortOrder) and Conflicts With Other Modules

~7 Min. Lesezeit Zuletzt aktualisiert am August 9, 2026

Chapter 38 showed three plugin types on a single target (PointsLedgerRepositoryInterface::getListByCustomerId()) and handed out sortOrder values almost in passing: 10, 20, 30. This chapter explains what those numbers actually do - and what happens once a third-party module joins the same target.

The onion model of execution

Magento sorts every plugin on a target in ascending sortOrder and then wraps them around the original method call like an onion: before methods run in ascending sortOrder order, around methods nest so that the lowest sortOrder forms the outermost layer, and after methods run in descending sortOrder order - mirroring the around nesting so the outermost layer also gets the last word on the return value.

Applied to the three plugins from chapter 38 (ValidateCustomerIdBeforeGetListPlugin sortOrder 10, CacheLedgerListPlugin sortOrder 20, LimitLedgerListResultPlugin sortOrder 30), a single call to getListByCustomerId() runs in exactly this order:

  1. beforeGetListByCustomerId() (sortOrder 10) validates $customerId.
  2. aroundGetListByCustomerId() (sortOrder 20) checks the request cache, calling $proceed() on a miss - which triggers the actual repository method.
  3. afterGetListByCustomerId() (sortOrder 30) caps the result to 500 entries before it goes back to the original caller.

Conflicts with other modules

Chapter 39 plugged the checkout totals collector to directly reduce grand_total. Suppose another, independent (fictional) "cashback" module plugs the same target (TotalsCollector::collectAddressTotals()) to also subtract a discount from grand_total. Without a deliberately set sortOrder on either side, the random, module-load-order-dependent default sorting decides which plugin runs first - harmless in this specific case, since both plugins additively subtract from $result->getGrandTotal() and order makes no mathematical difference. But the moment any involved plugin sets the discount as a fixed target sum instead of subtracting additively, or bases itself on an intermediate value like the subtotal instead of the already-reduced grand_total, execution order turns business critical.

Achtung: Without an explicit sortOrder, every plugin on the same target falls back to the default value 0 - ties then get resolved by an internal order that's not reliably predictable for the module developer. For any plugin that mutates a shared resource like grand_total, an explicit, deliberately chosen sortOrder is mandatory, not optional - and leave gaps between values (10, 20, 30 instead of 1, 2, 3) so a future module can slot in between without having to shift existing values.

Making third-party plugins visible

Before adding your own plugin to a core class that's already heavily used, it's worth checking what's already registered:

bin/cli bin/magento dev:di:info "Magento\\Quote\\Model\\Quote\\TotalsCollector"

The command lists every plugin registered for a class, including sortOrder and originating module - the most reliable source for spotting a conflict before deployment, instead of noticing it only in production through a wrong order total.

Selectively disabling third-party plugins

If a conflict is unavoidable, a third-party plugin can be turned off using the same name string plus disabled="true" in your own di.xml:

<type name="Magento\Quote\Model\Quote\TotalsCollector">
    <plugin name="cashback_apply_cashback_to_totals" disabled="true"/>
</type>

Tipp: The name string must exactly match the one the third-party module uses itself - the most reliable way to find it is via dev:di:info or directly from the other module's di.xml, never by guessing.

Achtung: Disabling a third-party plugin changes behavior the other module's authors never anticipated - in the concrete cashback example, disabling it would silently turn off the entire cashback feature, not just resolve the conflict. Any such disabling belongs thoroughly commented and, ideally, coordinated with the other module's maintainer.

With plugin ordering and coexistence settled, chapter 44 closes out block 5 with one last, smaller question: when a classic helper class is still the right tool despite this project's ViewModel preference.