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

Versandarten-Kalkulation: eigene Rate-Result-Objekte bauen

Versandarten-Kalkulation: eigene Rate-Result-Objekte bauen

~6 Min. Lesezeit Zuletzt aktualisiert am 9. August 2026

Kapitel 67 hat Method und Result bereits benutzt, ohne beide im Detail zu erklären. Dieses Kapitel holt das nach - insbesondere den Unterschied zwischen price und cost, und was sich ändert, sobald ein Carrier mehr als eine Methode gleichzeitig anbieten soll.

Zwei Fabriken, zwei Objekte

MethodFactory erzeugt genau ein Magento\Quote\Model\Quote\Address\RateResult\Method-Objekt - eine einzelne, dem Kunden angebotene Versandoption. ResultFactory erzeugt den umschließenden Container, Magento\Shipping\Model\Rate\Result, der beliebig viele Method-Objekte per append() sammelt. FreeShippingByPoints::collectRates() ruft append() genau einmal auf - nichts zwingt einen Carrier aber darauf, nur eine einzige Methode anzubieten:

// Illustrative only - FreeShippingByPoints (chapter 67) intentionally offers
// exactly one method. This is how a carrier with several tiers would look.
foreach ($this->getAvailableTiers($customerId) as $tierCode => $tierData) {
    $method = $this->rateMethodFactory->create();
    $method->setCarrier($this->_code);
    $method->setCarrierTitle($this->getConfigData('title'));
    $method->setMethod($tierCode);
    $method->setMethodTitle($tierData['label']);
    $method->setPrice($tierData['price']);
    $method->setCost($tierData['cost']);

    $result->append($method);
}

Tipp: Sowohl MethodFactory als auch ResultFactory werden - wie jede generierte Factory in diesem Modul - per Konstruktor-Injection eingebunden, nie über ObjectManager::create() direkt aufgerufen. Dasselbe Prinzip wie bei PointsLedgerInterfaceFactory (Kapitel 30) oder PointsBalanceWidgets Abhängigkeiten (Kapitel 56).

price vs. cost: zwei verschiedene Zahlen

setPrice() ist der Betrag, den der Kunde im Checkout tatsächlich sieht und zahlt. setCost() ist ein rein interner, buchhalterischer Wert - relevant für Versandkosten-Reporting und manche Versandmodul-Integrationen (z. B. tatsächliche Spediteurskosten bei tabellenbasierten Carriern), taucht aber nie im Frontend auf. FreeShippingByPoints setzt beide bewusst auf 0 - der Versand ist für Magento intern genauso kostenlos wie für den Kunden sichtbar, es gibt keinen tatsächlichen Speditionskosten-Unterschied, der erfasst werden müsste.

false statt eines leeren Result

collectRates() in Kapitel 67 gibt in jedem Ablehnungsfall - inaktiv, Land nicht erlaubt, zu wenig Punkte - konsequent false zurück, nie ein Result-Objekt ohne angehängte Methoden. Magentos Magento\Shipping\Model\Shipping::collectRates() behandelt beide Fälle zwar am Ende ähnlich (keine Methode im Checkout sichtbar), aber nur false spart die Objektinstanziierung komplett und macht in Log-/Debug-Ausgaben sofort klar, dass der Carrier bewusst "nein" gesagt hat, statt technisch "ja, aber leer".

getAllowedMethods() jenseits von collectRates()

getAllowedMethods() aus Kapitel 66/67 wird unabhängig von collectRates() aufgerufen - etwa von der Admin-Konfigurationsseite, um alle theoretisch verfügbaren Methodencodes eines Carriers anzuzeigen, oder von Table-Rate-artigen Vergleichs-Tools. Für FreeShippingByPoints mit nur einer Methode ist das trivial; bei einem Mehrmethoden-Carrier wie im Beispiel oben muss getAllowedMethods() konsequent alle Tarif-Codes zurückgeben, nicht nur die, die für den aktuell eingeloggten Kunden gerade infrage kämen.

Achtung: collectRates() läuft potenziell auf jeder Warenkorb- und Checkout-Seite, bei jeder Adressänderung neu. Ein CustomerRepositoryInterface::getById()-Aufruf pro Request ist für dieses Tutorial-Modul akzeptabel, aber bei sehr hohem Traffic lohnt sich ein Blick auf Magentos eingebautes Objekt-Caching der Customer-Repository-Ergebnisse innerhalb eines Requests - ein zusätzlicher, eigener In-Memory-Cache wie bei CacheLedgerListPlugin (Kapitel 39) wäre der nächste Schritt, sollte Profiling das tatsächlich als Engpass zeigen.

Zahlungsart und Versandart sind jetzt beide vollständig implementiert. Kapitel 69 prüft, ob sie im Checkout auch tatsächlich zusammenspielen.