Fix: Correct shipment resolving
This commit is contained in:
@@ -192,6 +192,16 @@ export default class extends Controller {
|
||||
detail: { html: data.summaryHtml },
|
||||
}))
|
||||
}
|
||||
|
||||
// bbk-payment is a sibling controller (both sit on
|
||||
// .bbk-checkout-main), not a target of this one — dispatched as an
|
||||
// event rather than reached into directly, same shape as
|
||||
// bbk-cart:changed above.
|
||||
if (data.paymentMethodsHtml != null) {
|
||||
window.dispatchEvent(new CustomEvent('bbk-checkout:payment-methods-changed', {
|
||||
detail: { html: data.paymentMethodsHtml },
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
applyErrors(errors) {
|
||||
|
||||
@@ -17,7 +17,7 @@ const POLL_TIMEOUT = 30000
|
||||
// poll /order-status until the webhook places it
|
||||
// { status:'failed'|'invalid'|'stale', message } -> show inline, re-enable
|
||||
export default class extends Controller {
|
||||
static targets = ['element', 'terms', 'error', 'submit', 'processing', 'processingText']
|
||||
static targets = ['element', 'terms', 'error', 'submit', 'processing', 'processingText', 'methods']
|
||||
|
||||
static values = {
|
||||
selectUrl: String,
|
||||
@@ -56,11 +56,26 @@ export default class extends Controller {
|
||||
}
|
||||
window.addEventListener('bbk-cart:updated', this.onSummaryUpdate)
|
||||
|
||||
// Fired by bbk-checkout-form after a shipping-option change —
|
||||
// getPaymentMethods() filters by fulfillment type, so the offered
|
||||
// methods (and which one, if any, is still validly selected) can
|
||||
// change without this controller's own element ever reconnecting.
|
||||
this.onPaymentMethodsChanged = (event) => {
|
||||
const html = event.detail?.html
|
||||
if (html == null || !this.hasMethodsTarget) return
|
||||
|
||||
this.methodsTarget.innerHTML = html
|
||||
|
||||
if (!this.selectedIsStripe()) this.unmountStripe()
|
||||
}
|
||||
window.addEventListener('bbk-checkout:payment-methods-changed', this.onPaymentMethodsChanged)
|
||||
|
||||
if (this.selectedIsStripe()) this.mountStripe()
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
window.removeEventListener('bbk-cart:updated', this.onSummaryUpdate)
|
||||
window.removeEventListener('bbk-checkout:payment-methods-changed', this.onPaymentMethodsChanged)
|
||||
this.unmountStripe()
|
||||
}
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@
|
||||
<section class="bbk-checkout-section">
|
||||
<h2 class="bbk-checkout-section-heading">{{ __('checkout.page.payment_heading') }}</h2>
|
||||
|
||||
<div id="bbk-payment-methods">
|
||||
<div id="bbk-payment-methods" data-bbk-payment-target="methods">
|
||||
@include('checkout::partials.payment-methods', [
|
||||
'paymentMethods' => $paymentMethods,
|
||||
'cart' => $cart,
|
||||
|
||||
@@ -646,6 +646,18 @@ class CheckoutController extends Controller
|
||||
])->render(),
|
||||
// Composer (Providers\CheckoutModuleServiceProvider) fills $cart / $lines.
|
||||
'summaryHtml' => $options === null ? null : view('checkout::partials.cart-body')->render(),
|
||||
// getPaymentMethods() filters by the cart's CURRENT fulfillment
|
||||
// type (Modules\Core\Checkout\Services\CheckoutService's own
|
||||
// docblock) — a shipping-option change can change that filter's
|
||||
// result (e.g. switching to store pickup should drop
|
||||
// cash-on-delivery and offer "pay in store" instead), so this
|
||||
// needs to be re-rendered every time shipping options are,
|
||||
// otherwise the payment section silently goes stale until a
|
||||
// full page reload.
|
||||
'paymentMethodsHtml' => $options === null ? null : view('checkout::partials.payment-methods', [
|
||||
'paymentMethods' => $this->checkout->getPaymentMethods(),
|
||||
'cart' => $cart,
|
||||
])->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,13 @@ use Modules\Core\Checkout\Exceptions\InvalidShippingOptionException;
|
||||
use Modules\Core\Checkout\Exceptions\NoShippingAddressException;
|
||||
use Modules\Core\Checkout\Exceptions\TermsNotAcceptedException;
|
||||
use Modules\Core\Checkout\Exceptions\UnknownPaymentTypeException;
|
||||
use Lunar\Shipping\Facades\Shipping;
|
||||
use Modules\Core\Payment\Contracts\RequiresFulfillmentType;
|
||||
use Modules\Core\Payment\DTOs\PaymentResult;
|
||||
use Modules\Core\Payment\Models\PaymentMethod;
|
||||
use Modules\Core\Payment\Services\PaymentDriverRegistry;
|
||||
use Modules\Core\Payment\Services\PaymentMethodCache;
|
||||
use Modules\Core\Shipping\Contracts\SupportsCashCollection;
|
||||
use Modules\Core\Shipping\Support\FulfillmentType;
|
||||
|
||||
/**
|
||||
@@ -254,42 +256,59 @@ class CheckoutService
|
||||
* a courier delivery makes no sense (no staff member present at
|
||||
* handoff to take cash), and cash-on-delivery alongside store
|
||||
* pickup is equally meaningless (OfflinePaymentDriver already
|
||||
* covers that in-person moment). A cart with no shipping option
|
||||
* selected yet imposes no constraint here — every method is
|
||||
* offered until a fulfillment type is actually known, the same
|
||||
* leniency setShippingAddress()'s own docblock describes for
|
||||
* required-field enforcement happening at the payment gate, not
|
||||
* mid-checkout.
|
||||
* covers that in-person moment).
|
||||
* 5. for cash-on-delivery specifically, the cart's currently selected
|
||||
* shipping method's own driver implements Modules\Core\Shipping\
|
||||
* Contracts\SupportsCashCollection — "carrier" alone
|
||||
* (RequiresFulfillmentType) isn't precise enough, since an
|
||||
* unattended parcel locker network (Modules\Core\Shipping\
|
||||
* Carriers\BoxNow\BoxNowRateDriver) is a carrier delivery with
|
||||
* nobody there to collect cash, unlike an actual courier
|
||||
* (Modules\Core\Shipping\Carriers\Acs\AcsRateDriver).
|
||||
* Checks 4 and 5 both impose no constraint when the cart has no
|
||||
* shipping option selected yet — every method is offered until a
|
||||
* shipping method is actually known, the same leniency
|
||||
* setShippingAddress()'s own docblock describes for required-field
|
||||
* enforcement happening at the payment gate, not mid-checkout.
|
||||
*
|
||||
* @return Collection<int, PaymentMethod>
|
||||
*/
|
||||
public function getPaymentMethods(): Collection
|
||||
{
|
||||
$fulfillmentType = $this->currentFulfillmentType();
|
||||
$shippingMethod = $this->currentShippingMethod();
|
||||
$fulfillmentType = $shippingMethod ? FulfillmentType::resolve($shippingMethod) : null;
|
||||
|
||||
return $this->paymentMethods->all()
|
||||
->filter(fn (PaymentMethod $method) => $method->enabled && $method->driver_missing_at === null)
|
||||
->filter(function (PaymentMethod $method) use ($fulfillmentType) {
|
||||
->filter(function (PaymentMethod $method) use ($fulfillmentType, $shippingMethod) {
|
||||
$driver = $this->paymentDrivers->resolve($method->driver);
|
||||
|
||||
if (! $driver?->isConfigured()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($fulfillmentType === null || ! $driver instanceof RequiresFulfillmentType) {
|
||||
return true;
|
||||
if ($fulfillmentType !== null && $driver instanceof RequiresFulfillmentType
|
||||
&& $driver->requiredFulfillmentType() !== $fulfillmentType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $driver->requiredFulfillmentType() === $fulfillmentType;
|
||||
// collect(...)->get() rather than Shipping::driver(), which
|
||||
// throws for an unregistered key — a stale ShippingMethod
|
||||
// row (e.g. still pointing at a removed generic driver)
|
||||
// must fail this check quietly, the same as any other
|
||||
// driver this module doesn't recognize.
|
||||
if ($shippingMethod !== null && $method->driver === 'cash-on-delivery') {
|
||||
$shippingDriver = collect(Shipping::getSupportedDrivers())->get($shippingMethod->driver);
|
||||
|
||||
return $shippingDriver instanceof SupportsCashCollection && $shippingDriver->collectsCash();
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
->values();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 'carrier'|'store_pickup'|null null when the cart has no
|
||||
* shipping option selected yet
|
||||
*/
|
||||
private function currentFulfillmentType(): ?string
|
||||
private function currentShippingMethod(): ?ShippingMethod
|
||||
{
|
||||
$identifier = $this->cart->currentOrCreate()->shippingAddress?->shipping_option;
|
||||
|
||||
@@ -297,9 +316,7 @@ class CheckoutService
|
||||
return null;
|
||||
}
|
||||
|
||||
$method = ShippingMethod::where('code', $identifier)->first();
|
||||
|
||||
return $method ? FulfillmentType::resolve($method) : null;
|
||||
return ShippingMethod::where('code', $identifier)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,6 +10,7 @@ use Livewire\Mechanisms\ComponentRegistry;
|
||||
use Lunar\Models\Order;
|
||||
use Lunar\Shipping\Facades\Shipping;
|
||||
use Lunar\Shipping\Filament\Resources\ShippingZoneResource\Pages\ManageShippingRates as VendorManageShippingRates;
|
||||
use Lunar\Shipping\Interfaces\ShippingMethodManagerInterface;
|
||||
use Lunar\Shipping\Models\ShippingMethod;
|
||||
use Modules\Core\Cart\Events\CartCleared;
|
||||
use Modules\Core\Cart\Events\CartLineAdded;
|
||||
@@ -25,9 +26,11 @@ use Modules\Core\Shipping\Carriers\BoxNow\BoxNowFulfillmentService;
|
||||
use Modules\Core\Shipping\Carriers\BoxNow\BoxNowRateDriver;
|
||||
use Modules\Core\Shipping\Contracts\CarrierFulfillmentInterface;
|
||||
use Modules\Core\Shipping\Filament\Pages\ManageShippingRates;
|
||||
use Modules\Core\Shipping\Carriers\StorePickup\StorePickupRateDriver;
|
||||
use Modules\Core\Shipping\Jobs\PollShipmentTrackingJob;
|
||||
use Modules\Core\Shipping\Listeners\InvalidateShippingOptions;
|
||||
use Modules\Core\Shipping\Support\FulfillmentType;
|
||||
use Modules\Core\Shipping\Support\ShippingManager;
|
||||
use Modules\Core\Shipping\Models\Shipment;
|
||||
|
||||
class ShippingServiceProvider extends ServiceProvider
|
||||
@@ -57,6 +60,16 @@ class ShippingServiceProvider extends ServiceProvider
|
||||
// first load, the Livewire registration covers every AJAX
|
||||
// round-trip (form submits, table interactions) afterwards.
|
||||
$this->app->bind(VendorManageShippingRates::class, ManageShippingRates::class);
|
||||
|
||||
// Binds AFTER the vendor's own ShippingServiceProvider — the
|
||||
// last-registered bind() for a given abstract wins in Laravel's
|
||||
// container, and vendor providers register before this one lists
|
||||
// theirs as a dependency implicitly via composer.json's package
|
||||
// discovery order — see Modules\Core\Shipping\Support\
|
||||
// ShippingManager's own docblock for why this override exists at
|
||||
// all (dropping the vendor's generic drivers from
|
||||
// getSupportedDrivers()).
|
||||
$this->app->bind(ShippingMethodManagerInterface::class, fn ($app) => $app->make(ShippingManager::class));
|
||||
}
|
||||
|
||||
public function boot(): void
|
||||
@@ -123,6 +136,7 @@ class ShippingServiceProvider extends ServiceProvider
|
||||
$this->app->booted(function () {
|
||||
Shipping::extend('acs', fn ($app) => $app->make(AcsRateDriver::class));
|
||||
Shipping::extend('box-now', fn ($app) => $app->make(BoxNowRateDriver::class));
|
||||
Shipping::extend('store-pickup', fn ($app) => $app->make(StorePickupRateDriver::class));
|
||||
|
||||
$this->app->make(ConsoleSchedule::class)
|
||||
->job(new WarmAcsAreaCacheJob)
|
||||
|
||||
@@ -9,16 +9,19 @@ use Lunar\Shipping\Interfaces\ShippingRateInterface;
|
||||
use Lunar\Shipping\Models\ShippingRate;
|
||||
use Modules\Core\Shipping\Carriers\Acs\Exceptions\AcsApiException;
|
||||
use Modules\Core\Shipping\Concerns\CachesLivePricing;
|
||||
use Modules\Core\Shipping\Concerns\ExcludesRestrictedProducts;
|
||||
use Modules\Core\Shipping\Concerns\ResolvesFixedPricing;
|
||||
use Modules\Core\Shipping\Contracts\DeclaresFulfillmentType;
|
||||
use Modules\Core\Shipping\Contracts\SupportsCashCollection;
|
||||
use Modules\Core\Shipping\Contracts\SupportsLivePricing;
|
||||
use Modules\Core\Shipping\Support\ShippingMethodName;
|
||||
use Modules\Core\Shipping\Support\WeightCalculator;
|
||||
|
||||
class AcsRateDriver implements ShippingRateInterface, SupportsLivePricing, DeclaresFulfillmentType
|
||||
class AcsRateDriver implements ShippingRateInterface, SupportsLivePricing, DeclaresFulfillmentType, SupportsCashCollection
|
||||
{
|
||||
use ResolvesFixedPricing;
|
||||
use CachesLivePricing;
|
||||
use ExcludesRestrictedProducts;
|
||||
|
||||
public ShippingRate $shippingRate;
|
||||
|
||||
@@ -37,6 +40,11 @@ class AcsRateDriver implements ShippingRateInterface, SupportsLivePricing, Decla
|
||||
return 'carrier';
|
||||
}
|
||||
|
||||
public function collectsCash(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function description(): string
|
||||
{
|
||||
return 'Live rate quote from ACS Courier.';
|
||||
@@ -48,6 +56,10 @@ class AcsRateDriver implements ShippingRateInterface, SupportsLivePricing, Decla
|
||||
$shippingMethod = $shippingRate->shippingMethod;
|
||||
$cart = $shippingOptionRequest->cart;
|
||||
|
||||
if ($this->cartHasExcludedProducts($shippingRate, $cart)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (($shippingMethod->data['charge_by'] ?? 'cart_total') !== 'live') {
|
||||
return $this->resolveFixedPrice($shippingRate, $shippingMethod, $cart);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use Lunar\DataTypes\ShippingOption;
|
||||
use Lunar\Shipping\DataTransferObjects\ShippingOptionRequest;
|
||||
use Lunar\Shipping\Interfaces\ShippingRateInterface;
|
||||
use Lunar\Shipping\Models\ShippingRate;
|
||||
use Modules\Core\Shipping\Concerns\ExcludesRestrictedProducts;
|
||||
use Modules\Core\Shipping\Concerns\ResolvesFixedPricing;
|
||||
use Modules\Core\Shipping\Contracts\DeclaresFulfillmentType;
|
||||
|
||||
@@ -18,6 +19,7 @@ use Modules\Core\Shipping\Contracts\DeclaresFulfillmentType;
|
||||
class BoxNowRateDriver implements ShippingRateInterface, DeclaresFulfillmentType
|
||||
{
|
||||
use ResolvesFixedPricing;
|
||||
use ExcludesRestrictedProducts;
|
||||
|
||||
public ShippingRate $shippingRate;
|
||||
|
||||
@@ -38,6 +40,10 @@ class BoxNowRateDriver implements ShippingRateInterface, DeclaresFulfillmentType
|
||||
|
||||
public function resolve(ShippingOptionRequest $shippingOptionRequest): ?ShippingOption
|
||||
{
|
||||
if ($this->cartHasExcludedProducts($shippingOptionRequest->shippingRate, $shippingOptionRequest->cart)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->resolveFixedPrice(
|
||||
$shippingOptionRequest->shippingRate,
|
||||
$shippingOptionRequest->shippingRate->shippingMethod,
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Core\Shipping\Carriers\StorePickup;
|
||||
|
||||
use Lunar\DataTypes\ShippingOption;
|
||||
use Lunar\Shipping\DataTransferObjects\ShippingOptionRequest;
|
||||
use Lunar\Shipping\Interfaces\ShippingRateInterface;
|
||||
use Lunar\Shipping\Models\ShippingRate;
|
||||
use Modules\Core\Shipping\Concerns\ExcludesRestrictedProducts;
|
||||
use Modules\Core\Shipping\Concerns\ResolvesFixedPricing;
|
||||
use Modules\Core\Shipping\Contracts\DeclaresFulfillmentType;
|
||||
|
||||
/**
|
||||
* First-party replacement for lunarphp/table-rate-shipping's own Collection
|
||||
* driver — same shape as AcsRateDriver/BoxNowRateDriver, unlike the vendor
|
||||
* generic drivers (flat-rate/ship-by/free-shipping/collection), which this
|
||||
* module no longer registers (see ShippingServiceProvider). Two reasons
|
||||
* this exists rather than keeping the vendor driver:
|
||||
*
|
||||
* 1. Collection::resolve() reads $shippingMethod->name directly instead of
|
||||
* through Modules\Core\Shipping\Support\ShippingMethodName::resolve() —
|
||||
* ShippingMethod has no cast for that locale-keyed JSON column (see
|
||||
* ShippingMethodName's own docblock), so the storefront showed the raw
|
||||
* JSON blob as the option's name instead of the translated string.
|
||||
* 2. Unambiguously store pickup, like ACS/Box Now are unambiguously
|
||||
* carrier — implementing DeclaresFulfillmentType means a merchant never
|
||||
* has to separately pick "Collect in store" from the generic
|
||||
* data['fulfillment_type'] field (see that contract's own docblock).
|
||||
*
|
||||
* No live pricing — there's no API for in-person pickup, just the method's
|
||||
* own charge_by + price-break configuration (usually free), the same
|
||||
* mechanism Box Now uses.
|
||||
*/
|
||||
class StorePickupRateDriver implements ShippingRateInterface, DeclaresFulfillmentType
|
||||
{
|
||||
use ResolvesFixedPricing;
|
||||
use ExcludesRestrictedProducts;
|
||||
|
||||
public ShippingRate $shippingRate;
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'Store Pickup';
|
||||
}
|
||||
|
||||
public function fulfillmentType(): string
|
||||
{
|
||||
return 'store_pickup';
|
||||
}
|
||||
|
||||
public function description(): string
|
||||
{
|
||||
return 'Collect your order in store.';
|
||||
}
|
||||
|
||||
public function resolve(ShippingOptionRequest $shippingOptionRequest): ?ShippingOption
|
||||
{
|
||||
if ($this->cartHasExcludedProducts($shippingOptionRequest->shippingRate, $shippingOptionRequest->cart)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->resolveFixedPrice(
|
||||
$shippingOptionRequest->shippingRate,
|
||||
$shippingOptionRequest->shippingRate->shippingMethod,
|
||||
$shippingOptionRequest->cart,
|
||||
);
|
||||
}
|
||||
|
||||
public function on(ShippingRate $shippingRate): self
|
||||
{
|
||||
$this->shippingRate = $shippingRate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Core\Shipping\Concerns;
|
||||
|
||||
use Lunar\Models\Cart;
|
||||
use Lunar\Models\Product;
|
||||
use Lunar\Shipping\Models\ShippingRate;
|
||||
|
||||
/**
|
||||
* A shipping zone can list specific products that can't go through it (see
|
||||
* the Filament "Shipping Exclusions" relation manager on ShippingZoneResource)
|
||||
* — a merchant's way of saying "this item physically can't be handled by
|
||||
* this carrier/method," independent of price. lunarphp/table-rate-shipping's
|
||||
* own generic drivers (FlatRate, ShipBy, FreeShipping, Collection) each
|
||||
* check this before returning an option; every first-party driver in this
|
||||
* module (ACS, Box Now, store pickup) shares it from here instead, so a
|
||||
* merchant-configured exclusion is honored no matter which driver a
|
||||
* shipping method uses — not just the vendor's generic ones. Call this
|
||||
* FIRST in resolve(), before any pricing lookup (live or fixed): an
|
||||
* excluded product should make the option disappear entirely, the same
|
||||
* "return null" a driver already uses for "no rate matched."
|
||||
*/
|
||||
trait ExcludesRestrictedProducts
|
||||
{
|
||||
private function cartHasExcludedProducts(ShippingRate $shippingRate, Cart $cart): bool
|
||||
{
|
||||
$productIds = $cart->lines->load('purchasable')->pluck('purchasable.product_id');
|
||||
|
||||
return $shippingRate->shippingZone->shippingExclusions()
|
||||
->whereHas('exclusions', function ($query) use ($productIds) {
|
||||
$query->wherePurchasableType(Product::morphName())
|
||||
->whereIn('purchasable_id', $productIds);
|
||||
})->exists();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Core\Shipping\Contracts;
|
||||
|
||||
/**
|
||||
* A shipping rate driver implements this to say a person is physically
|
||||
* present at handoff to collect cash — true for a courier like
|
||||
* Modules\Core\Shipping\Carriers\Acs\AcsRateDriver, false for an
|
||||
* unattended parcel locker network like Modules\Core\Shipping\Carriers\
|
||||
* BoxNow\BoxNowRateDriver, where no one is there to take payment. Checked
|
||||
* by Modules\Core\Checkout\Services\CheckoutService::getPaymentMethods()
|
||||
* for cash-on-delivery specifically (Modules\Core\Payment\Drivers\
|
||||
* CashOnDeliveryPaymentDriver) — separate from Modules\Core\Shipping\
|
||||
* Contracts\DeclaresFulfillmentType, which only distinguishes carrier vs.
|
||||
* store_pickup and can't tell ACS and Box Now apart (both 'carrier').
|
||||
*
|
||||
* Not implemented at all means "no" — a driver with no opinion here
|
||||
* (any of table-rate-shipping's own generic drivers, if one were ever
|
||||
* re-added) is treated as not supporting cash collection, the safer
|
||||
* default for a driver this module knows nothing about.
|
||||
*/
|
||||
interface SupportsCashCollection
|
||||
{
|
||||
public function collectsCash(): bool;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Core\Shipping\Support;
|
||||
|
||||
use Lunar\Shipping\Managers\ShippingManager as VendorShippingManager;
|
||||
|
||||
/**
|
||||
* Drops lunarphp/table-rate-shipping's own generic drivers (free-shipping,
|
||||
* flat-rate, ship-by, collection) from getSupportedDrivers() — every
|
||||
* shipping method in this app now uses a first-party driver (ACS, Box
|
||||
* Now, or Modules\Core\Shipping\Carriers\StorePickup\StorePickupRateDriver,
|
||||
* registered via Shipping::extend() in ShippingServiceProvider), each of
|
||||
* which declares its own fulfillment type and correctly decodes
|
||||
* ShippingMethod's translatable `name` column (see StorePickupRateDriver's
|
||||
* own docblock for why the vendor `collection` driver specifically had to
|
||||
* go — it read $shippingMethod->name raw, showing the storefront the raw
|
||||
* JSON blob instead of a translated string). The generic drivers'
|
||||
* create{X}Driver() methods still exist on the parent (harmless — nothing
|
||||
* calls them once their key is gone from getSupportedDrivers()), so this
|
||||
* only needs to override the one method that lists what's offered.
|
||||
*
|
||||
* Bound over the vendor's own ShippingMethodManagerInterface binding in
|
||||
* ShippingServiceProvider — see that class for why (registration order:
|
||||
* this module's provider binds after the vendor's own).
|
||||
*/
|
||||
class ShippingManager extends VendorShippingManager
|
||||
{
|
||||
public function getSupportedDrivers(): \Illuminate\Support\Collection
|
||||
{
|
||||
return collect($this->customCreators)
|
||||
->mapWithKeys(function ($creator, $key) {
|
||||
return [$key => $this->callCustomCreator($key)];
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user