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

Booking Product Laravel Package

bagisto/booking-product

Adds bookable product types to Bagisto, letting stores sell appointments, rentals, events, and reservations with configurable time slots, availability, and pricing instead of standard inventory-based items.

View on GitHub
Deep Wiki
Context7

Getting Started

Pin your platform first. This is a Bagisto module, not a standalone library — the only compatibility that matters is the Bagisto core you already run. Before composer require, record in the PR description: exact bagisto/bagisto version, PHP version, and DB engine (MySQL vs Postgres changes locking and DATETIME semantics). If those don't match composer.json, stop; a mismatch is a hard blocker, not a nuisance.

Install and prove it loaded.

composer require bagisto/booking-product
php artisan optimize:clear
php artisan migrate
php artisan vendor:publish --provider="<Provider from src/Providers>"
php artisan storage:link && php artisan optimize

Confirm three visible signals before writing code: the booking product type appears in Admin → Catalog → Products → Create → Type, the booking admin screens render, and a storefront product of that type shows a calendar/date-time picker. Any missing signal means the install is incomplete — fix it before layering your own code on top.

Where to look first, in order. The README is thin, so treat the source as the spec:

  1. composer.json — Bagisto/PHP constraints, autoload namespace, provider discovery.
  2. src/Providers/*ServiceProvider.php — the real map: config merges, routes, event/listener bindings, view namespaces, product-type registration. One file tells you most of the extension surface you'll use daily.
  3. src/Database/Migrations/ — tables, FKs, and indexes on slot/availability lookup columns. Cheapest place to spot a scaling cliff.
  4. src/Models/ — booking, slot, and the product-type model. Note relationships and casts on datetime columns; that's your timezone answer.
  5. routes/ + src/Http/Controllers/ — the storefront availability endpoint is the highest-traffic, highest-risk route.
  6. Cart/checkout integration points — wherever booking metadata attaches to a cart item and copies to the order item.

First use case: one appointment product, capacity 1. Don't start with rentals or multi-slot events. Create a single "Consultation — 30 min" product with one weekly availability window and per-slot capacity 1, then walk the full lifecycle on staging: browse → pick slot → add to cart → checkout → order placed → admin booking visible → cancel → slot available again. Snapshot the relevant rows at each step. That trace becomes your regression baseline for every future upgrade.

Implementation Patterns

Wrap availability in an application service — don't scrape the storefront endpoint. You'll want slot data outside the product page: a "next available" badge on category listings, a JSON endpoint for a mobile client, an ops dashboard. Put one thin service around the package's model/query and never let a second copy of the "is this slot open" predicate drift into your code.

// app/Services/AvailabilityService.php
public function slotsFor(Product $product, CarbonInterface $date): Collection
{
    // delegate to the package's model/query — do not reimplement slot math
}

Ride the cart pipeline, don't go around it. Booking metadata must attach to the cart item and survive to the order item, or admin screens, refunds, and rebooking lose context. Follow the package's existing cart-item handler/observer instead of mutating Cart::add() payloads in your own controllers. If you add custom fields (attendee name, pickup location), extend at the same seam the package uses so a future tag doesn't silently drop your column.

Extend at the outermost layer that works. Precedence for customization: config → event/listener → view override → class override/fork. Published Blade overrides diverge permanently and turn each bump into a manual three-way merge. Cosmetic changes belong in a CSS layer or a wrapper Blade component; behavioural changes belong in a listener on the package's events.

Build a fixture seeder in week one. You'll replay the booking lifecycle dozens of times. One seeder creating the booking product, its schedule, and a handful of pre-existing bookings makes "slot is taken" states reproducible without clicking. It pays for itself by day two.

Two tests the package won't write for you. (1) A lifecycle feature test: book → assert the order item carries slot data → cancel → assert capacity restored. (2) A concurrency test: fire N parallel checkout requests at the last remaining slot and assert exactly one succeeds. Run the concurrency test against your production DB engine — SQLite will lie to you about locking.

// tests/Feature/BookingLifecycleTest.php (shape, not literal API)
public function test_cancelling_a_booking_releases_the_slot(): void
{
    $booking = $this->bookSlot($product, $slot);
    $this->assertDatabaseHas('booking_slots', ['id' => $slot->id, 'available_qty' => 0]);

    $booking->cancel();

    $this->assertDatabaseHas('booking_slots', ['id' => $slot->id, 'available_qty' => 1]);
}

Instrument the availability query early. Log SQL and timing for slot lookups on a product seeded with realistic booking history. Storefront views and cart validation hit this path constantly; if it degrades non-linearly with booking count, you want to know in week one, not month three.

Give ops a runbook, not a tutorial. Admin booking management means operations staff — not engineers — confirm, reschedule, and cancel bookings. Document the exact admin screens and the escalation path before launch. When a customer's slot is wrong, ops needs to know where to click, not who to Slack.

Gotchas and Tips

Double-booking is the risk you must personally disprove. Read the availability code and answer one question: is it check-then-insert, or is there a unique index / pessimistic lock / atomic decrement? If check-then-insert, two concurrent checkouts both succeed and no amount of sequential testing reveals it. This is the highest-severity defect in small booking packages and the one most likely to be present here.

v1.1.0's "product quantity system" change is your upgrade risk. For booking products, quantity-per-slot is the overbooking control. Diff migrations and the quantity-handling service between your installed tag and 1.1.0 before upgrading — check for schema backfills and any change to the decrement/locking strategy. The release notes say "improved"; they don't say what changed. The diff is the changelog — write your own internal upgrade note.

Timezone and DST are where edge cases live. Determine whether slots are stored in UTC or store timezone, and whether there's a per-product override (check the datetime casts on the models). Book a slot on a DST transition day (23- and 25-hour days) in a non-UTC timezone and confirm the customer's view, admin's view, and the stored value agree. Off-by-one-hour bugs here are common, embarrassing, and hard to reproduce after the fact.

Cancellation must release capacity. If the package only models booking creation, availability drifts permanently downward — every cancellation silently removes a sellable slot. Prove this on your first lifecycle walkthrough and re-verify after every bump. If it doesn't release, ship a scheduled reconciliation job before launch, not after.

Never edit a live product's schedule in place. Changing slot duration, availability windows, or capacity on a product with existing bookings can orphan historical bookings or silently redefine "available" for them. There's usually no versioning of slot configuration. Policy: clone the product, or accept that historical bookings get reinterpreted.

Per-slot pricing vs. the discount engine. Per-slot price overrides frequently bypass tier pricing, cart price rules, tax classes, and multi-currency formatting. Test a booked slot with an active cart discount in a non-base currency and confirm the total matches what ops configured. This is also where tax on services (vs. goods) may need its own tax class — decide up front.

Published assets vs. the Bagisto admin build. Admin is Vue-based; published assets must match your installed Bagisto build, not a nominal version. A mismatch shows as blank admin screens or console errors, not a PHP exception. If booking admin screens render empty, open the browser console before the Laravel log.

Debug the DB, not the UI. When a slot shows unavailable or a booking vanishes, query the booking and slot tables directly and compare against what the UI claims. The UI is a projection; the tables are truth. Keep Telescope (or query logging) on in staging so you can see the availability query and its bindings.

Upgrade one variable at a time. Never bump Bagisto core and this package in the same release. Core first, verify the lifecycle, then the package tag, verify again. With a single-maintainer, 14-star dependency and zero dependents, you are the integration test suite — there's no ecosystem catching regressions for you.

Decide the fork threshold before you're inside it. Requirements likely to exceed the package: recurring/subscription bookings, multi-resource allocation (several rooms or staff per booking), deposits and no-show fees tied to cancellation windows, two-way calendar sync. Name the line in advance so crossing it is a scheduled "fork or move to a managed booking engine" conversation, not a mid-sprint surprise.

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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core