# Lecture 9: Dive Deep in Laravel Welcome to Lecture 9! In this lecture, we will build the part of the Booking System for our Expert Platform using Laravel 12. This includes creating the booking model, defining relationships, securing the booking endpoint with authentication, implementing a service layer for business logic, and reusing unified response handling with a trait. By the end of this lecture, you'll: - Create bookings with validations - Implement relationships between user, expert, and booking - Learn service classes and dependency injection in Laravel - Implement search, filters, pagination, and ordering - Use traits for unified API responses --- ## Step 1: Booking Model and Migration ### Generate Booking Model ```bash php artisan make:model Booking -m ``` ### Migration File: `database/migrations/xxxx_xx_xx_create_bookings_table.php` ```php Schema::create('bookings', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained()->onDelete('cascade'); $table->foreignId('expert_id')->constrained()->onDelete('cascade'); $table->dateTime('scheduled_at'); $table->enum('status', ['pending', 'confirmed', 'cancelled'])->default('pending'); $table->text('notes')->nullable(); $table->timestamps(); }); ``` Run the migration: ```bash php artisan migrate ``` --- ## Step 2: Model Relationships ### In `User.php` ```php public function bookings() { return $this->hasMany(Booking::class); } ``` ### In `Expert.php` ```php public function bookings() { return $this->hasMany(Booking::class); } ``` ### In `Booking.php` ```php public function user() { return $this->belongsTo(User::class); } public function expert() { return $this->belongsTo(Expert::class); } ``` --- ## Step 3: Booking Controller ### Generate Controller: ```bash php artisan make:controller Api/BookingController ``` ### Add Routes in `routes/api.php` ```php Route::middleware('auth:sanctum')->group(function () { Route::post('/bookings', [BookingController::class, 'store']); Route::get('/bookings', [BookingController::class, 'index']); }); ``` --- ## Step 4: Service Layer (Recommended Practice) ### Why Use a Service Layer? To keep your controller clean and move the business logic to a separate class. ### Create BookingService Class ```bash php artisan make:service BookingService ``` ### Implementation: **app/Services/BookingService.php** ```php namespace App\Services; use App\Models\Booking; use Illuminate\Support\Facades\Auth; class BookingService { public function create(array $data): Booking { return Booking::create([ 'user_id' => Auth::id(), 'expert_id' => $data['expert_id'], 'scheduled_at' => $data['scheduled_at'], 'notes' => $data['notes'] ?? null, ]); } public function getFilteredBookings($filters) { return Booking::with(['expert', 'user']) ->when($filters['status'] ?? null, fn($q, $status) => $q->where('status', $status)) ->when($filters['expert_id'] ?? null, fn($q, $id) => $q->where('expert_id', $id)) ->orderBy($filters['order_by'] ?? 'scheduled_at', $filters['order'] ?? 'desc') ->paginate($filters['per_page'] ?? 10); } } ``` ### Inject it in the Controller: **BookingController.php** ```php use App\Services\BookingService; class BookingController extends Controller { public function __construct(protected BookingService $bookingService) {} public function store(Request $request) { $data = $request->validate([ 'expert_id' => 'required|exists:experts,id', 'scheduled_at' => 'required|date|after:now', 'notes' => 'nullable|string' ]); $booking = $this->bookingService->create($data); return $this->successResponse($booking, 'Booking created successfully.'); } public function index(Request $request) { $bookings = $this->bookingService->getFilteredBookings($request->all()); return $this->successResponse($bookings); } } ``` --- ## Step 5: Unified JSON Response Using Traits ### Why Traits? Avoid repeating response formatting logic in every controller. ### Create Trait **app/Traits/ApiResponseTrait.php** ```php namespace App\Traits; trait ApiResponseTrait { public function successResponse($data, $message = null, $code = 200) { return response()->json([ 'success' => true, 'message' => $message, 'data' => $data ], $code); } public function errorResponse($message, $code = 400) { return response()->json([ 'success' => false, 'message' => $message, 'data' => null ], $code); } } ``` ### Use Trait in Controller ```php use App\Traits\ApiResponseTrait; class BookingController extends Controller { use ApiResponseTrait; ... } ``` --- ## Step 6: Final Test With Postman 1. Register and login a user to get token (via Lecture 8) 2. Make authenticated `POST /api/bookings` 3. Make authenticated `GET /api/bookings?status=pending&order_by=scheduled_at&order=asc&per_page=5` ---