tourze/order-core-bundle
tourze/order-core-bundle provides foundational order domain structures and services for PHP applications, helping developers model and manage order workflows with reusable bundle components. It integrates into bundle-based projects and offers a starting point for building order management features w...
Start by pulling the bundle in and confirming which framework layer you're actually getting. Despite the -bundle suffix (a Symfony convention), this is consumed in Laravel via a service provider bridge, so the first thing to verify is that the provider registers cleanly and that the package's config is mergeable:
composer require tourze/order-core-bundle
php artisan vendor:publish --tag=order-core-config # if a config tag is exposed
php artisan about # confirm provider + config are loaded
Where to look first, in order:
First use case: create an order with one or two line items, then walk it through a status transition. That single flow exercises the whole core — pricing, persistence, and the state machine — so if it works, you understand 80% of the package:
$order = app(OrderManager::class)->create([
'currency' => 'USD',
'items' => [
['sku' => 'ABC-1', 'qty' => 2, 'unit_price' => 1999],
],
]);
$order->transitionTo(OrderStatus::PendingPayment);
Treat the package as the order domain, not the checkout flow. The bundle gives you the order aggregate and its lifecycle; it does not give you carts, payment gateways, or fulfilment. The healthy pattern is a thin application service in your app that composes the package with your own concerns:
final class CheckoutService
{
public function __construct(private OrderManager $orders) {}
public function checkout(Cart $cart): Order
{
$order = $this->orders->create([
'currency' => $cart->currency,
'items' => $cart->toOrderItems(),
]);
// your payment concern, package's order concern
event(new OrderPlaced($order));
return $order;
}
}
Drive status changes through transitions, never by assignment. The whole value of an order core is the state machine. Use transitionTo() (or whatever the guard method is named in your version) so invalid jumps throw instead of silently corrupting order history:
$order->transitionTo(OrderStatus::Paid);
$order->transitionTo(OrderStatus::Shipped);
// jumping PendingPayment -> Shipped throws
Hook side effects onto transitions via the package's events rather than editing the manager. Emit-your-own is the extension point: listen for status-changed events and fan out to mailers, webhooks, and inventory in queued listeners.
Persist money as integers throughout. The package's pricing fields are minor units. Keep them that way end-to-end — no floats in your DTOs, no $order->total / 100 until the presentation layer — and you sidestep the rounding drift that shows up a month after launch.
Integration tips:
meta column over subclassing the item class — subclassing tends to break persistence assumptions.The Symfony-to-Laravel seam is the main friction point. The bundle was authored in a Symfony idiom. Watch for:
extends Model your way around it.%env(...)% strings) that need translating.Release notes are thin. 1.0.5 lands between 1.0.3 and 1.0.5 with nothing more than a GitHub compare link. Before upgrading, read the actual diff or the CHANGELOG file:
git diff 1.0.3..1.0.5 -- src/ config/
Pay particular attention to anything touching the status enum, transition guards, or pricing arithmetic — those are the changes that will bite silently rather than loudly. Pin the version in composer.json and run your order-flow tests against the upgrade in a branch.
Debugging state transitions. When a transition "does nothing", it's almost always one of three things: the current status isn't what you think (re-read the row from the DB — you may be holding a stale model), a guard is throwing and being swallowed by a broad catch, or the transition isn't in the enabled list in config. Log the from/to/order-id triple in a transition listener during development; it pays for itself.
Money and currency. Don't assume a default currency — set it explicitly on every order, and validate that line-item currencies match the order's. Mixed-currency orders are a data-corruption bug that surfaces only in refunds.
Extension points to prefer over forking: the status-change events, the config-driven table names, and the manager's public methods. If you find yourself needing to override a protected method to add, say, a discount line, that's a signal to compute it in your application service and pass the resulting items in — not to subclass the core.
Test the aggregate, not the framework. Write a small suite that creates an order, runs it through every valid transition, and asserts every invalid transition throws. That suite is your safety net for upgrades like this one, where the release notes won't tell you what changed.
How can I help you explore Laravel packages today?