setono/sylius-pickup-point-plugin
Pickup point plugin for Sylius
Maintainers
Package info
github.com/Setono/SyliusPickupPointPlugin
Type:sylius-plugin
pkg:composer/setono/sylius-pickup-point-plugin
Fund package maintenance!
Requires
- php: >=8.2
- doctrine/dbal: ^3.0 || ^4.0
- doctrine/orm: ^3.0
- fakerphp/faker: ^1.21
- sylius/core: ^2.0
- sylius/core-bundle: ^2.0
- sylius/order: ^2.0
- sylius/shipping: ^2.0
- sylius/shipping-bundle: ^2.0
- symfony/config: ^6.4 || ^7.4
- symfony/dependency-injection: ^6.4 || ^7.4
- symfony/form: ^6.4 || ^7.4
- symfony/http-foundation: ^6.4 || ^7.4
- symfony/http-kernel: ^6.4 || ^7.4
- symfony/options-resolver: ^6.4 || ^7.4
- symfony/validator: ^6.4 || ^7.4
- webmozart/assert: ^1.11
Requires (Dev)
- api-platform/core: ~4.2.1
- dama/doctrine-test-bundle: ^8.6
- doctrine/doctrine-bundle: ^2.11
- lexik/jwt-authentication-bundle: ^3.1
- setono/gls-webservice-bundle: ^1.4
- setono/sylius-plugin: ^2.0
- sylius/sylius: ~2.2.5
- symfony/browser-kit: ^6.4 || ^7.4
- symfony/css-selector: ^6.4 || ^7.4
- symfony/debug-bundle: ^6.4 || ^7.4
- symfony/dom-crawler: ^6.4 || ^7.4
- symfony/dotenv: ^6.4 || ^7.4
- symfony/property-info: ^6.4 || ^7.4
- symfony/var-exporter: ^6.4 || ^7.4
- symfony/web-profiler-bundle: ^6.4 || ^7.4
- symfony/webpack-encore-bundle: ^2.2
Suggests
- setono/dao-bundle: Install this bundle to use the DAO provider
- setono/gls-webservice-bundle: Install this bundle to use the GLS provider
- setono/post-nord-bundle: Install this bundle to use the PostNord provider
Provides
None
Conflicts
None
Replaces
None
MIT 779e5234d1b521312615153a2aff2fb45436a95d
This package is auto-updated.
Last update: 2026-06-08 12:50:03 UTC
README
π Latest Version
π Software License
π Build Status
π Code Coverage
Add a pickup-point chooser to your shipping checkout step.
Supported providers
- DAO
- GLS
- PostNord
- Fake provider (for development/playing purposes β not enabled in
prod) - ...or add your own
Compatibility
| Plugin | Sylius | PHP | Symfony |
|---|---|---|---|
| 2.x | ^2.0 |
>=8.2 |
^6.4 || ^7.4 |
| 1.x | ^1.0 |
>=8.1 |
^5.4 || ^6.0 |
Migrating from 1.x to 2.x: see UPGRADE.md.
Screenshots
Shop
This is the shipping method step in the checkout process where you can choose a pickup point. The points are loaded asynchronously after the page renders, the nearest one is pre-selected, and the shopper can expand the list to pick another.
π Screenshot showing checkout select shipping step with pickup points available
Admin
On the order you can see what pickup point the customer has chosen.
π Screenshot showing admin order shipping page with pickup point address
When you edit shipping method you can associate a pickup point provider to that shipping method.
π Screenshot showing admin shipping method with some pickup point providers
Installation
Step 1: Install and enable plugin
composer require setono/sylius-pickup-point-plugin
Add the bundle to your config/bundles.php:
<?php # config/bundles.php return [ // ... Setono\SyliusPickupPointPlugin\SetonoSyliusPickupPointPlugin::class => ['all' => true], // ... ];
Step 2: Import routing
# config/routes/setono_sylius_pickup_point.yaml setono_sylius_pickup_point: resource: "@SetonoSyliusPickupPointPlugin/config/routes.yaml"
If your store has localized URLs disabled,
import @SetonoSyliusPickupPointPlugin/config/routes_no_locale.yaml instead.
Step 3: Customize resources
Shipment resource
<?php // src/Entity/Shipment.php declare(strict_types=1); namespace App\Entity; use Doctrine\ORM\Mapping as ORM; use Setono\SyliusPickupPointPlugin\Model\PickupPointAwareTrait; use Setono\SyliusPickupPointPlugin\Model\ShipmentInterface; use Sylius\Component\Core\Model\Shipment as BaseShipment; #[ORM\Entity] #[ORM\Table(name: 'sylius_shipment')] class Shipment extends BaseShipment implements ShipmentInterface { use PickupPointAwareTrait; }
Shipping method resource
<?php // src/Entity/ShippingMethod.php declare(strict_types=1); namespace App\Entity; use Doctrine\ORM\Mapping as ORM; use Setono\SyliusPickupPointPlugin\Model\PickupPointProviderAwareTrait; use Setono\SyliusPickupPointPlugin\Model\ShippingMethodInterface; use Sylius\Component\Core\Model\ShippingMethod as BaseShippingMethod; #[ORM\Entity] #[ORM\Table(name: 'sylius_shipping_method')] class ShippingMethod extends BaseShippingMethod implements ShippingMethodInterface { use PickupPointProviderAwareTrait; }
You can read about extending resources here.
Update shipping resources config
# config/packages/_sylius.yaml sylius_shipping: resources: shipment: classes: model: App\Entity\Shipment shipping_method: classes: model: App\Entity\ShippingMethod
Step 4: Configure plugin
Enable desired providers
fakerwill not work in the production environment- Each carrier provider requires its corresponding bundle to be installed:
daoβsetono/dao-bundleglsβsetono/gls-webservice-bundlepost_nordβsetono/post-nord-bundle
The carrier bundles are listed in this plugin's suggest section β install only the ones you need.
# config/packages/setono_sylius_pickup_point.yaml setono_sylius_pickup_point: providers: faker: true gls: true post_nord: true dao: true
Step 5: Database
bin/console doctrine:migrations:diff bin/console doctrine:migrations:migrate
Step 6: Validation groups
Add checkout_select_shipping to sylius.form.type.checkout_select_shipping.validation_groups:
# config/packages/_sylius.yaml parameters: sylius.form.type.checkout_select_shipping.validation_groups: ['sylius', 'checkout_select_shipping']
Step 7: Install assets
bin/console assets:install
The plugin's JavaScript and CSS are auto-included on the shop checkout via Twig
hooks (sylius_shop.checkout#javascripts / sylius_shop.checkout#stylesheets).
The chooser is a framework-free ES module (loaded with <script type="module">)
that builds its UI by cloning overridable Twig <template>s, so you can restyle or
extend it without forking β see
docs/customizing-the-chooser.md.
Step 8: Admin shipping method form
Add the pickupPointProvider field to your admin shipping-method form. With
Sylius 2.x's Twig hooks the cleanest path is a project-local hook config that
points at a template containing {{ form_row(form.pickupPointProvider) }},
attached to sylius_admin.shipping_method.update.content.form.options (or a
form section you already render).
Creating a custom provider
A provider returns the pickup points near an order's address and re-resolves a single point by its id. To add
your own carrier, implement Setono\SyliusPickupPointPlugin\Provider\ProviderInterface β or, more simply,
extend the abstract Setono\SyliusPickupPointPlugin\Provider\Provider, which already handles the registered
code (getCode()), so you only implement two methods.
<?php declare(strict_types=1); namespace App\PickupPoint; use Setono\SyliusPickupPointPlugin\Attribute\AsProvider; use Setono\SyliusPickupPointPlugin\DTO\Address; use Setono\SyliusPickupPointPlugin\DTO\PickupPoint; use Setono\SyliusPickupPointPlugin\Provider\Provider; #[AsProvider(code: 'acme', name: 'ACME')] final class AcmeProvider extends Provider { public function __construct( private readonly AcmeClient $client, // your carrier's API client ) { } /** * @return list<PickupPoint> */ public function findPickupPoints(Address $address): array { // Every Address field is nullable (the cart may not have a full address yet) β bail when a needed one is missing. if (null === $address->postalCode || null === $address->countryCode) { return []; } $points = []; foreach ($this->client->search($address->postalCode, $address->countryCode) as $shop) { $points[] = $this->transform($shop); } // Return them ordered by distance from the address: the first one is auto-selected at checkout. return $points; } public function findPickupPoint(string $id, array $metadata = []): ?PickupPoint { // Called when re-resolving a single point by id; $metadata carries the context you stored (see below). $shop = $this->client->get($id, $metadata['country'] ?? null); return null === $shop ? null : $this->transform($shop); } private function transform(object $shop): PickupPoint { $point = new PickupPoint(); $point->provider = $this->getCode(); // always stamp the code the provider is registered under $point->id = (string) $shop->id; // unique within this provider $point->name = $shop->name; $point->address = $shop->street; $point->zipCode = $shop->zip; $point->city = $shop->city; $point->country = $shop->countryCode; $point->latitude = (string) $shop->lat; $point->longitude = (string) $shop->lng; return $point; } }
Register it. With Symfony autoconfiguration on (the default), the #[AsProvider(code, name)] attribute is all
you need β the plugin turns it into the setono_sylius_pickup_point.provider tag and the compiler pass does the
rest. Without autoconfiguration, tag the service yourself:
# config/services.yaml services: App\PickupPoint\AcmeProvider: tags: - { name: 'setono_sylius_pickup_point.provider', code: 'acme', name: 'ACME' }
The code is the machine identifier (registry key, the value stored on the shipping method, and the provider
part of the pickup-point token); name is the carrier's brand name shown to merchants in the admin form.
Use it. A custom provider is just a registered service β it does not go in the setono_sylius_pickup_point.providers
config (that toggle is only for the plugin's bundled optional providers). To put it to work, edit a shipping
method in the admin and set its Pickup point provider to yours (ACME); its points are then fetched live at
that method's checkout.
Good to know:
- Providers are called live and lazily β construction is deferred until the provider is first used, and the
/pickup-pointsendpoint wraps each provider in its owntry/catch, so a slow or throwing carrier degrades gracefully instead of blocking checkout. - The submitted token is the whole
PickupPoint, decoded straight back on submit βfindPickupPoint()is only for re-resolving a point from a bare id/metadata. Put any extra context your API needs to do that intoPickupPoint::$metadata(it round-trips inside the identifier); the well-knowncountryis folded in for you. - Build an
Addressfrom an order withAddress::fromOrder($order)when calling a provider outside checkout.
Play
To see the pickup points list, use the following example address at checkout:
Dannebrogsgade 1
9000 Aalborg
DK
HΓ€meentie 1
00350 Helsinki
FI
Vasterhaninge 1
137 94 Stockholm
SE
Providers have pickup points in the following countries:
- DAO: DK
- PostNord: DK, SE, FI
- GLS: See https://gls-group.eu/EU/en/depot-parcelshop-search
So, to play with all 3 providers at once β use a DK address.
