keirontw/relay-point-bundle
A PHP bundle for adding relay point and pickup location support to your application. It provides configuration and service integration for shipping, checkout, and delivery workflows, making it easier to manage selectable parcel pickup points.
This is a pure Symfony bundle, not a Laravel package. There is no RelayPointServiceProvider, no auto-discovered config, no facade, and no Blade/Twig views shipped in a Laravel-compatible layout. Before installing, decide which of these you actually want:
symfony/http-foundation and can pull symfony/http-client, but you own the glue.Minimal steps for the adapter path:
composer require keirontw/relay-point-bundle
RelayPointProviderInterface — that interface is the only stable extension contract you should build against.config/relay-point.php yourself. Laravel will not import the Symfony Configuration class, so you are transcribing keys by hand.AppServiceProvider::register(), bind a RelayPointProviderInterface instance per carrier and tag them, then construct the bundle's selector/registry service with that tagged iterator.Illuminate\Session\Store is not a SessionInterface. Either wrap it in an adapter or override the storage service with your own implementation of whatever the bundle type-hints.Config translation, not config publishing. Keep a single config/relay-point.php that mirrors the bundle's tree 1:1, and add a config('relay-point.*') read in your provider. Write one test that asserts every key you expect exists, because Symfony's config-tree validation will not run for you.
Carrier registration via tags. Auto-discovery of RelayPointProviderInterface is a Symfony compiler-pass feature. In Laravel, do it explicitly:
$this->app->tag([MondialRelayProvider::class, ChronopostProvider::class], 'relay_point.providers');
$this->app->singleton(RelayPointRegistry::class, fn ($app) => new RelayPointRegistry(...$app->tagged('relay_point.providers')));
Only register carriers you have credentials and contracts for — instantiating a dozen providers at boot is wasted work.
Geocoder as a swappable binding. The pluggable geocoding is the most portable part of the package. Bind one geocoder interface implementation and switch by env: Addok/Nominatim for self-hosted or dev, Google Maps for production accuracy, Photon as a middle ground. Wrap the chosen geocoder in a caching decorator (Laravel Cache::remember keyed on normalized address) — geocoding is the slowest and most rate-limited call in the flow.
Selection storage as a first-class abstraction. Do not scatter session()->get('relay_point') through controllers. Introduce RelayPointSelection (a small value object: carrier, id, label, address, lat/lng) and one SelectionStore that owns read/write/forget. When/if the bundle's session format changes, you patch one class.
Checkout listener. The optional checkout_listener.enabled + event_class hook is a Symfony event-dispatcher pattern. In Laravel, skip it and use a native listener on your own OrderPlacing event that reads SelectionStore and writes the relay point onto the order. You get the same outcome with less indirection and normal Laravel event fakes in tests.
Widget rendering. The Stimulus widget is the genuinely reusable asset — Stimulus works fine under Laravel Vite. The Twig templates are not. Two options: install twigbridge and render the bundle's Twig blocks from Blade, or (usually better) treat the Twig markup as a reference implementation and rebuild the ~3 templates in Blade with the same data-* controller attributes. The CSS variable theming contract (--relay-point-*) survives either way — keep those variable names in your own stylesheet so theming stays aligned with docs.
AssetMapper registration is a no-op for you. Guard your boot code so the absence of symfony/asset-mapper doesn't throw; expect to import the JS via vite.config.js instead.
Testing. Use Symfony's MockHttpClient for carrier and geocoder responses — Laravel's Http::fake() only intercepts the Laravel HTTP client and will not touch Symfony HttpClient calls made inside the bundle. Assert on your SelectionStore and on RelayPointSelection hydration rather than on bundle internals.
keirontw/relay-point-bundle while the README lives under BastienMesnil/relay-point-bundle. Confirm on Packagist which namespace is the maintained one before pinning; a mismatch here is a supply-chain question, not a typo.extra.laravel block is absent, so nothing registers itself. Every service the bundle expects must be bound by you, and the failure mode is a container "unresolvable dependency" deep in a carrier provider rather than a clear "package not installed" error.checkout_listener.enable is a silently disabled feature. Add an assertion test.carrier + postcode + radius.1.0.0 with a hand-written integration layer, use "keirontw/relay-point-bundle": "1.0.*" and re-run your container/config assertions on every bump — you have no Symfony deprecation layer to warn you.RelayPointProviderInterface (add a carrier the bundle doesn't cover, or stub one in tests), the geocoder interface (inject a cached/fake implementation), and Twig block overrides only if you go the twigbridge route. Everything else — config tree, asset registration, event listener — is Symfony-shaped and should be replaced with idiomatic Laravel in the adapter.How can I help you explore Laravel packages today?