Weave Code
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

Reservable Laravel Package

yanselmask/reservable

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require yanselmask/reservable
    php artisan migrate
    
    • Verify the reservations and reservable tables exist in your database.
  2. Basic Model Setup

    • For reservable models (e.g., Book, Room):
      use Yanselmask\Reservable\Interfaces\ReservableInterface;
      use Yanselmask\Reservable\Traits\Reservable;
      
      class Book extends Model implements ReservableInterface
      {
          use Reservable;
      }
      
    • For customer models (e.g., User):
      use Yanselmask\Reservable\Interfaces\CustomerInterface;
      use Yanselmask\Reservable\Traits\Customer;
      
      class User extends Authenticatable implements CustomerInterface
      {
          use Customer;
      }
      
  3. First Use Case: Reserve a Model

    $book = Book::find(1);
    $user = auth()->user();
    
    $reservation = $book->reserve($user, now()->addDays(3));
    // Returns a `Reserve` model instance.
    

Implementation Patterns

Core Workflows

  1. Reserving a Model

    // Basic reservation (auto-assigns to authenticated user if `Customer` trait is used)
    $reservation = $model->reserve($user, $endAt);
    
    // Force reservation (bypasses availability checks)
    $reservation = $model->forceReserve($user, $endAt);
    
  2. Checking Availability

    // Check if a model is available for a given period
    $isAvailable = $model->isAvailable($startAt, $endAt);
    
    // Get overlapping reservations
    $overlaps = $model->getOverlappingReservations($startAt, $endAt);
    
  3. Managing Reservations

    // Cancel a reservation
    $reservation->cancel();
    
    // Extend a reservation
    $reservation->extend($newEndAt);
    
    // List all reservations for a model
    $model->reservations; // MorphMany relationship
    
  4. Customer-Specific Reservations

    // Get all reservations for a customer
    $user->reservations; // MorphMany relationship
    
    // Check if a customer has an active reservation
    $hasActiveReservation = $user->hasActiveReservation($model);
    

Integration Tips

  1. Customizing Reservation Logic Override the isAvailable() method in your model to enforce business rules:

    public function isAvailable($startAt, $endAt)
    {
        // Custom logic (e.g., blackout dates, capacity limits)
        return parent::isAvailable($startAt, $endAt) && $this->customRule();
    }
    
  2. Scopes for Querying Add scopes to your models for common queries:

    public function scopeAvailable($query, $startAt, $endAt)
    {
        return $query->whereDoesntHave('reservations', function ($q) use ($startAt, $endAt) {
            $q->where(function ($query) use ($startAt, $endAt) {
                $query->where('start_at', '<', $endAt)
                      ->where('end_at', '>', $startAt);
            });
        });
    }
    
  3. Events and Observers Listen for reservation events (e.g., Reserved, Cancelled) to trigger notifications or updates:

    // In EventServiceProvider
    protected $listen = [
        \Yanselmask\Reservable\Events\Reserved::class => [
            \App\Listeners\SendReservationConfirmation::class,
        ],
    ];
    
  4. API Endpoints Example controller methods:

    public function reserve(Request $request, $modelId)
    {
        $model = Model::findOrFail($modelId);
        $reservation = $model->reserve(auth()->user(), $request->end_at);
    
        return response()->json($reservation);
    }
    
    public function checkAvailability(Request $request, $modelId)
    {
        $model = Model::findOrFail($modelId);
        $isAvailable = $model->isAvailable($request->start_at, $request->end_at);
    
        return response()->json(['available' => $isAvailable]);
    }
    

Gotchas and Tips

Pitfalls

  1. Missing Traits/Interfaces

    • Forgetting to implement ReservableInterface or CustomerInterface will cause runtime errors.
    • Fix: Ensure all models adhere to the required contracts.
  2. Time Zone Mismatches

    • Reservations use Carbon instances, which respect the app's timezone. Ensure consistency:
      $reservation = $model->reserve($user, now()->setTimezone('UTC')->addDays(3));
      
  3. Overlapping Reservations

    • The package checks for overlaps, but custom logic (e.g., partial overlaps) may require manual handling.
    • Tip: Extend the isAvailable() method or use the getOverlappingReservations() method for granular control.
  4. Database Constraints

    • The reservations table may lack foreign key constraints by default. Add them if needed:
      Schema::table('reservations', function (Blueprint $table) {
          $table->foreign('reservable_id')->references('id')->on('reservables')->onDelete('cascade');
          $table->foreign('customer_id')->references('id')->on('users')->onDelete('cascade');
      });
      
  5. Soft Deletes

    • If your models use soft deletes, ensure the Reserve model also does:
      use Illuminate\Database\Eloquent\SoftDeletes;
      
      class Reserve extends Model
      {
          use SoftDeletes;
      }
      

Debugging

  1. Reservation Not Saving

    • Check if the reservable_id and customer_id are correctly set in the reservations table.
    • Verify the reservable_type column matches your model's fully qualified class name (e.g., App\Models\Book).
  2. Availability Checks Failing

    • Log the overlapping reservations to debug:
      $overlaps = $model->getOverlappingReservations($startAt, $endAt);
      \Log::info('Overlaps:', $overlaps->toArray());
      
  3. Performance Issues

    • Avoid eager-loading reservations for large datasets. Use lazy loading or query scopes instead:
      $model->load('reservations:start_at,end_at'); // Load only specific columns
      

Extension Points

  1. Custom Reservation Attributes Add fields to the reservations table via migration:

    Schema::table('reservations', function (Blueprint $table) {
        $table->string('status')->default('active');
        $table->json('metadata');
    });
    

    Update the Reserve model to cast these fields.

  2. Validation Rules Extend the package's validation by overriding the reserve() method:

    public function reserve($customer, $endAt)
    {
        if (!$this->validateReservation($endAt)) {
            throw new \Exception('Reservation validation failed.');
        }
        return parent::reserve($customer, $endAt);
    }
    
  3. Custom Reserve Model Replace the default Reserve model by binding it in the service provider:

    public function register()
    {
        $this->app->bind(
            \Yanselmask\Reservable\Models\Reserve::class,
            \App\Models\CustomReserve::class
        );
    }
    
  4. Localization Override language strings (e.g., for validation errors) in your app/config/reservable.php:

    'messages' => [
        'overlap' => 'This item is already reserved for the selected time period.',
    ],
    
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.
besmartand-pro/php-quality-config
sentix/ai-chatbot
codifyo/ts-generator-bundle
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor