Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Bazar Laravel Package

conedevelopment/bazar

Bazar is a Laravel e-commerce package that gives you products, categories, carts, orders, payments, and coupons on a flexible database-driven foundation, making it easier to build and customize online stores quickly.

View on GitHub
Deep Wiki
Context7

Getting Started

Bazar is a headless domain layer, not a storefront. It gives you Eloquent models, contracts, a cart manager, and a checkout manager. You bring the routes, controllers, views, and payment integration.

  1. Install and publish
composer require conedevelopment/bazar
php artisan vendor:publish --tag=bazar
php artisan migrate

Publish both the config and migrations (tags may differ slightly by version — run php artisan vendor:publish and pick the Bazar provider). Treat the published migrations as your schema; edit them freely before you have data.

  1. Read these files first, in order

    • config/bazar.php — currency, model bindings, cart/checkout settings. This is the package's control panel.
    • src/Contracts/ — Buyable, Sellable, Cart, Order, etc. These tell you what the package expects from your models.
    • src/Models/ — Product, Variant, Price, Cart, CartItem, Order, OrderItem. This is where the behavior lives.
    • src/Cart/ and src/Checkout/ — the two managers you'll call from your app.
    • database/migrations/ — the actual columns. Read them; they resolve most API questions.
  2. First use case: product → cart → order

use Bazar\Models\Product;
use Bazar\Facades\Cart;
use Bazar\Facades\Checkout;

// 1. Catalog: product with a variant and a price
$product = Product::create([
    'name' => 'T-Shirt',
    'slug' => 't-shirt',
]);

$variant = $product->variants()->create(['name' => 'Large']);

$product->prices()->create(['price' => 2500]); // verify unit in config/migration
// or attach to the variant if your catalog prices at variant level

// 2. Cart (session-backed by default)
Cart::add($variant, 2);
$cart  = Cart::get();
$total = Cart::total();

// 3. Checkout: cart → order
$order = Checkout::create($cart, auth()->user(), [
    'billing_address'  => [...],
    'shipping_address' => [...],
]);

The key mental model: Product is buyable, Variant is sellable, Price is polymorphic. Your cart line points at a purchasable entity, and the order copies that data.

Implementation Patterns

Custom models via config, not forks. The intended extension path is to subclass Bazar\Models\Product (or any model), then point the corresponding key in config/bazar.php at your class. Keep the table names identical unless you also edit migrations. This is how you add media, categories, SEO fields, or custom scopes without fighting the package.

namespace App\Models;

class Product extends \Bazar\Models\Product
{
    public function media() { return $this->morphMany(Media::class, 'mediable'); }
}
// config/bazar.php
'models' => [
    'product' => App\Models\Product::class,
    // ...
],

Catalog modeling. Model variants as the sellable unit whenever you have sizes/colors; use product-level prices only for single-SKU items. Prices are rows, not columns — that's what makes multiple currencies and sale prices possible. Add a compare_at_price or sale_price column to the prices migration if you need strikethrough pricing, and compute it in a presenter rather than mutating price.

Cart lifecycle. The cart is session-scoped by default. The pattern that works well:

  • Guest adds items → cart keyed to session.
  • On login/register, listen for Login/Registered, find the guest cart, merge items into the user's cart, then delete the guest cart. Do this in a queued listener so you don't slow down auth.
  • Never store a computed total column. Read Cart::total() / the cart model's accessors. If you cache totals, invalidate on every item mutation.

Checkout as an orchestrated pipeline. Bazar creates the order and fires events; it does not charge cards. The reliable flow is:

validate cart → Checkout::create(...) → redirect to payment provider
   → webhook hits your controller → verify signature → mark order paid → clear cart

Hook into CheckoutStarted / CheckoutCompleted (and any order events your version exposes) for side effects: inventory decrement, confirmation email, CRM sync, coupon redemption. Queue all of them — these events fire inside the request.

Order status and fulfillment. Keep status transitions in your own service/action class rather than scattering $order->update([...]) in controllers. Listeners should be idempotent, because payment webhooks retry. Store the payment provider's transaction ID on the order (add a column) and guard on it.

Headless integration. Wrap Bazar in thin API controllers returning JSON resources. Bind routes by slug for products, by cart ID for carts, and require auth for orders. If you're building a SPA, the cart endpoints are the only stateful part — everything else is CRUD over your catalog.

Testing. Use RefreshDatabase, create products/variants/prices via factories, and drive Cart/Checkout directly in feature tests. Test the cart merge-on-login listener and the webhook idempotency path explicitly — those are the two places bugs hide.

Gotchas and Tips

  • There is no UI, no routes, no views. Expect to write every controller, request validator, and Blade/Inertia page yourself. Budget for that; Bazar saves you the schema, not the storefront.
  • Confirm the price unit before writing code. Check config/bazar.php and the prices migration: packages in this space differ between integer minor units (cents) and decimals. Getting this wrong silently makes every product 100× too expensive. Centralize formatting in one helper.
  • Single global currency by default. The config defines one currency. True multi-currency means multiple Price rows plus a currency-aware cart/checkout resolver — plan for it early rather than retrofitting.
  • Swapping models requires consistency. If you subclass a model, update the config binding and the corresponding contract binding if the package resolves interfaces. Keep table names; changing them means rewriting every relation.
  • Published migrations are a snapshot. Don't republish after you have production data. Add new migrations for your columns instead of editing the originals post-launch.
  • Polymorphic prices and cart items can orphan. Use soft deletes on products/variants, and never hard-delete anything referenced by a historical order. Order items should snapshot name/price at purchase time — verify that in your version and add snapshots if missing.
  • Events are synchronous. Listeners for CheckoutCompleted run inside the checkout request. Implement ShouldQueue and make them idempotent; a slow mailer can time out a checkout.
  • Cart mutation concurrency. Adding the same item twice should updateOrCreate (or increment) a single line, not create duplicates. If you build custom "add to cart" logic, enforce a unique index on (cart_id, purchasable_type, purchasable_id).
  • Checkout does not clear the cart for you in all versions — explicitly clear/delete the cart after a successful order, and make that part of the same transaction or post-webhook step. Otherwise users see stale items.
  • Taxes and shipping are compute hooks, not magic. Wire them to your own calculator or to the package's tax/shipping models, and test rounding at the order level. Totals recalculated in two places (cart and order) will eventually disagree.
  • Small ecosystem. With ~445 stars, Stack Overflow answers are sparse; the source is the documentation. When in doubt, dd() the cart manager and read src/Cart and src/Checkout — it's a small codebase and very readable.
  • Pin the version. Pre-1.0/early-1.x packages move fast. Commit a specific tag in composer.json and read the changelog before composer update, especially around checkout signatures and event names.
  • SQLite in tests can surprise you. JSON columns and some index definitions behave differently than MySQL/Postgres. Run the full suite against your production database engine in CI.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib