Laravel Best Practices: Writing Clean and Maintainable Code

Published on December 08, 2025
Laravel LaravelBestPractices CleanCode PHPCleanCode LaravelArchitecture SOLIDPrinciples LaravelTesting CodeQuality WebDevelopment BackendDevelopment LaravelPerformance

Introduction

In the world of Laravel development, writing code that works is just the beginning. The true mark of a professional Laravel developer is writing code that's clean, maintainable, and scalable. This comprehensive guide covers the essential best practices that will transform your Laravel applications from functional to exceptional.

Section 1: Laravel Application Architecture

1.1 Domain-Driven Design (DDD) in Laravel

Why DDD?

Domain-Driven Design helps you structure your application around business domains, making it more maintainable and scalable.

Implementation:

// Traditional structure (MVC)
app/
├── Http/
│   ├── Controllers/
│   └── Requests/
├── Models/
└── Services/

// DDD structure
app/
├── Domain/
│   ├── Users/
│   │   ├── Actions/
│   │   ├── Models/
│   │   ├── DataTransferObjects/
│   │   ├── Events/
│   │   ├── Listeners/
│   │   ├── Rules/
│   │   └── ValueObjects/
│   └── Products/
│       ├── Actions/
│       └── ...
├── Application/
│   ├── Users/
│   │   ├── Services/
│   │   └── Queries/
│   └── Products/
└── Infrastructure/
    ├── Http/
    ├── Database/
    └── Providers/

Example Domain Structure:

// app/Domain/Users/Models/User.php
namespace App\Domain\Users\Models;

use Illuminate\Database\Eloquent\Model;
use App\Domain\Users\ValueObjects\Email;
use App\Domain\Users\Events\UserCreated;

class User extends Model
{
    protected $dispatchesEvents = [
        'created' => UserCreated::class,
    ];
    
    public function setEmailAttribute($value)
    {
        $this->attributes['email'] = (new Email($value))->value();
    }
}

// app/Domain/Users/ValueObjects/Email.php
namespace App\Domain\Users\ValueObjects;

class Email
{
    private string $value;
    
    public function __construct(string $email)
    {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new \InvalidArgumentException('Invalid email address');
        }
        
        $this->value = strtolower(trim($email));
    }
    
    public function value(): string
    {
        return $this->value;
    }
    
    public function domain(): string
    {
        return explode('@', $this->value)[1];
    }
}

// app/Domain/Users/Actions/CreateUser.php
namespace App\Domain\Users\Actions;

use App\Domain\Users\Models\User;
use App\Domain\Users\DataTransferObjects\UserData;
use Illuminate\Support\Facades\Hash;

class CreateUser
{
    public function execute(UserData $userData): User
    {
        $user = new User([
            'name' => $userData->name,
            'email' => $userData->email,
            'password' => Hash::make($userData->password),
        ]);
        
        $user->save();
        
        return $user;
    }
}

1.2 Repository Pattern Implementation

Why Repositories?

Repositories abstract data access, making your code more testable and flexible.

Implementation:

// app/Domain/Users/Contracts/UserRepositoryInterface.php
namespace App\Domain\Users\Contracts;

use App\Domain\Users\Models\User;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;

interface UserRepositoryInterface
{
    public function find(int $id): ?User;
    public function findByEmail(string $email): ?User;
    public function all(): array;
    public function paginate(int $perPage = 15): LengthAwarePaginator;
    public function create(array $data): User;
    public function update(User $user, array $data): bool;
    public function delete(User $user): bool;
    public function withTrashed(): self;
}

// app/Infrastructure/Database/Repositories/UserRepository.php
namespace App\Infrastructure\Database\Repositories;

use App\Domain\Users\Contracts\UserRepositoryInterface;
use App\Domain\Users\Models\User;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;

class UserRepository implements UserRepositoryInterface
{
    protected $model;
    
    public function __construct(User $user)
    {
        $this->model = $user;
    }
    
    public function find(int $id): ?User
    {
        return $this->model->find($id);
    }
    
    public function findByEmail(string $email): ?User
    {
        return $this->model->where('email', $email)->first();
    }
    
    public function all(): array
    {
        return $this->model->all()->toArray();
    }
    
    public function paginate(int $perPage = 15): LengthAwarePaginator
    {
        return $this->model->paginate($perPage);
    }
    
    public function create(array $data): User
    {
        return $this->model->create($data);
    }
    
    public function update(User $user, array $data): bool
    {
        return $user->update($data);
    }
    
    public function delete(User $user): bool
    {
        return $user->delete();
    }
    
    public function withTrashed(): self
    {
        $this->model = $this->model->withTrashed();
        return $this;
    }
}

// app/Infrastructure/Providers/RepositoryServiceProvider.php
namespace App\Infrastructure\Providers;

use Illuminate\Support\ServiceProvider;
use App\Domain\Users\Contracts\UserRepositoryInterface;
use App\Infrastructure\Database\Repositories\UserRepository;

class RepositoryServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->bind(
            UserRepositoryInterface::class,
            UserRepository::class
        );
    }
}

1.3 Service Layer Implementation

Why Services?

Services encapsulate business logic, keeping controllers thin and focused.

Implementation:

// app/Application/Users/Services/UserRegistrationService.php
namespace App\Application\Users\Services;

use App\Domain\Users\Contracts\UserRepositoryInterface;
use App\Domain\Users\Actions\CreateUser;
use App\Domain\Users\Actions\SendWelcomeEmail;
use App\Domain\Users\DataTransferObjects\UserRegistrationData;

class UserRegistrationService
{
    private $userRepository;
    private $createUser;
    private $sendWelcomeEmail;
    
    public function __construct(
        UserRepositoryInterface $userRepository,
        CreateUser $createUser,
        SendWelcomeEmail $sendWelcomeEmail
    ) {
        $this->userRepository = $userRepository;
        $this->createUser = $createUser;
        $this->sendWelcomeEmail = $sendWelcomeEmail;
    }
    
    public function register(UserRegistrationData $data): array
    {
        // Validate unique email
        if ($this->userRepository->findByEmail($data->email)) {
            throw new \Exception('Email already registered');
        }
        
        // Create user
        $user = $this->createUser->execute($data);
        
        // Send welcome email
        $this->sendWelcomeEmail->execute($user);
        
        // Log registration
        \Log::info('User registered', ['user_id' => $user->id]);
        
        return [
            'user' => $user,
            'token' => $user->createToken('auth-token')->plainTextToken,
        ];
    }
    
    public function registerWithSocial(string $provider, array $userData): User
    {
        // Find or create user from social provider
        $user = $this->userRepository->findByEmail($userData['email']);
        
        if (!$user) {
            $user = $this->userRepository->create([
                'name' => $userData['name'],
                'email' => $userData['email'],
                'provider' => $provider,
                'provider_id' => $userData['id'],
                'email_verified_at' => now(),
            ]);
        }
        
        return $user;
    }
}

Section 2: SOLID Principles in Laravel

2.1 Single Responsibility Principle (SRP)

Bad Practice:

class UserController extends Controller
{
    public function store(Request $request)
    {
        // Validate
        $validated = $request->validate([
            'name' => 'required',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:8',
        ]);
        
        // Create user
        $user = User::create([
            'name' => $validated['name'],
            'email' => $validated['email'],
            'password' => Hash::make($validated['password']),
        ]);
        
        // Send welcome email
        Mail::to($user->email)->send(new WelcomeEmail($user));
        
        // Create user profile
        $user->profile()->create([
            'bio' => 'New user',
            'avatar' => 'default.jpg',
        ]);
        
        // Log activity
        ActivityLog::create([
            'user_id' => $user->id,
            'action' => 'user_registered',
            'details' => 'New user registered',
        ]);
        
        // Return response
        return redirect()->route('users.show', $user)
            ->with('success', 'User created successfully');
    }
}

Good Practice (SRP Applied):

// Controller (Single responsibility: Handle HTTP)
class UserController extends Controller
{
    private $registrationService;
    
    public function __construct(UserRegistrationService $registrationService)
    {
        $this->registrationService = $registrationService;
    }
    
    public function store(UserRegistrationRequest $request)
    {
        $result = $this->registrationService->register(
            UserRegistrationData::fromRequest($request)
        );
        
        return response()->json($result, 201);
    }
}

// Request (Single responsibility: Validate)
class UserRegistrationRequest extends FormRequest
{
    public function rules()
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:8|confirmed',
        ];
    }
}

// Data Transfer Object (Single responsibility: Data structure)
class UserRegistrationData
{
    public string $name;
    public string $email;
    public string $password;
    
    public static function fromRequest(UserRegistrationRequest $request): self
    {
        $data = new self();
        $data->name = $request->input('name');
        $data->email = $request->input('email');
        $data->password = $request->input('password');
        
        return $data;
    }
}

// Service (Single responsibility: Business logic)
class UserRegistrationService
{
    public function register(UserRegistrationData $data): array
    {
        $user = $this->createUser->execute($data);
        $this->sendWelcomeEmail->execute($user);
        $this->createUserProfile->execute($user);
        $this->logRegistration->execute($user);
        
        return [
            'user' => $user,
            'token' => $user->createToken('auth-token')->plainTextToken,
        ];
    }
}

2.2 Open/Closed Principle (OCP)

Implementation:

// app/Domain/Payments/Contracts/PaymentGatewayInterface.php
interface PaymentGatewayInterface
{
    public function charge(float $amount, array $options = []): PaymentResult;
    public function refund(string $transactionId, float $amount): bool;
}

// Concrete implementations
class StripePaymentGateway implements PaymentGatewayInterface
{
    public function charge(float $amount, array $options = []): PaymentResult
    {
        // Stripe-specific implementation
    }
}

class PayPalPaymentGateway implements PaymentGatewayInterface
{
    public function charge(float $amount, array $options = []): PaymentResult
    {
        // PayPal-specific implementation
    }
}

class BankTransferGateway implements PaymentGatewayInterface
{
    public function charge(float $amount, array $options = []): PaymentResult
    {
        // Bank transfer implementation
    }
}

// Payment service (Open for extension, closed for modification)
class PaymentService
{
    private $gateway;
    
    public function __construct(PaymentGatewayInterface $gateway)
    {
        $this->gateway = $gateway;
    }
    
    public function processOrder(Order $order): PaymentResult
    {
        return $this->gateway->charge(
            $order->total,
            ['order_id' => $order->id]
        );
    }
}

// Factory for creating gateways
class PaymentGatewayFactory
{
    public static function create(string $gateway): PaymentGatewayInterface
    {
        return match($gateway) {
            'stripe' => new StripePaymentGateway(),
            'paypal' => new PayPalPaymentGateway(),
            'bank_transfer' => new BankTransferGateway(),
            default => throw new \InvalidArgumentException("Unknown gateway: $gateway"),
        };
    }
}

// Usage
$gateway = PaymentGatewayFactory::create('stripe');
$paymentService = new PaymentService($gateway);
$result = $paymentService->processOrder($order);

// Adding new gateway doesn't require changing existing code
class CryptoPaymentGateway implements PaymentGatewayInterface
{
    public function charge(float $amount, array $options = []): PaymentResult
    {
        // New cryptocurrency implementation
    }
}

2.3 Liskov Substitution Principle (LSP)

Implementation:

// Base interface
interface NotificationInterface
{
    public function send(User $user, string $message): bool;
}

// Implementations
class EmailNotification implements NotificationInterface
{
    public function send(User $user, string $message): bool
    {
        // Send email
        return Mail::to($user->email)
            ->send(new GenericNotification($message));
    }
}

class SMSNotification implements NotificationInterface
{
    public function send(User $user, string $message): bool
    {
        // Send SMS
        return $this->smsService->send($user->phone, $message);
    }
}

class PushNotification implements NotificationInterface
{
    public function send(User $user, string $message): bool
    {
        // Send push notification
        return $user->devices->each->sendPush($message);
    }
}

// Notification service using LSP
class NotificationService
{
    private $notifications;
    
    public function __construct(NotificationInterface ...$notifications)
    {
        $this->notifications = $notifications;
    }
    
    public function notify(User $user, string $message): void
    {
        foreach ($this->notifications as $notification) {
            try {
                $notification->send($user, $message);
            } catch (\Exception $e) {
                \Log::error('Notification failed', [
                    'type' => get_class($notification),
                    'error' => $e->getMessage(),
                ]);
            }
        }
    }
}

// Usage - all implementations are interchangeable
$service = new NotificationService(
    new EmailNotification(),
    new SMSNotification(),
    new PushNotification()
);

$service->notify($user, 'Your order has shipped!');

2.4 Interface Segregation Principle (ISP)

Bad Practice (Fat Interface):

interface UserRepositoryInterface
{
    public function find(int $id);
    public function findByEmail(string $email);
    public function create(array $data);
    public function update(int $id, array $data);
    public function delete(int $id);
    public function restore(int $id);
    public function forceDelete(int $id);
    public function paginate(int $perPage);
    public function withTrashed();
    public function onlyTrashed();
    public function attachRole(int $userId, int $roleId);
    public function detachRole(int $userId, int $roleId);
    // ... 20 more methods
}

Good Practice (Segregated Interfaces):

// Core repository
interface RepositoryInterface
{
    public function find(int $id);
    public function findByEmail(string $email);
    public function create(array $data);
    public function update(int $id, array $data);
    public function delete(int $id);
    public function paginate(int $perPage);
}

// Soft delete operations
interface SoftDeletesRepositoryInterface
{
    public function restore(int $id);
    public function forceDelete(int $id);
    public function withTrashed();
    public function onlyTrashed();
}

// Role operations
interface RoleRepositoryInterface
{
    public function attachRole(int $userId, int $roleId);
    public function detachRole(int $userId, int $roleId);
    public function syncRoles(int $userId, array $roleIds);
}

// User repository implementing multiple interfaces
class UserRepository implements 
    RepositoryInterface,
    SoftDeletesRepositoryInterface,
    RoleRepositoryInterface
{
    // Implement all methods
}

// Service needing only basic operations
class UserLookupService
{
    public function __construct(RepositoryInterface $repository)
    {
        $this->repository = $repository;
    }
    
    public function findUser(int $id)
    {
        return $this->repository->find($id);
    }
}

// Service needing soft delete operations
class UserAdminService
{
    public function __construct(
        RepositoryInterface $repository,
        SoftDeletesRepositoryInterface $softDeleteRepository
    ) {
        $this->repository = $repository;
        $this->softDeleteRepository = $softDeleteRepository;
    }
}

2.5 Dependency Inversion Principle (DIP)

Implementation:

// High-level module
class OrderProcessor
{
    private $paymentGateway;
    private $notificationService;
    private $orderRepository;
    
    public function __construct(
        PaymentGatewayInterface $paymentGateway,
        NotificationInterface $notificationService,
        OrderRepositoryInterface $orderRepository
    ) {
        $this->paymentGateway = $paymentGateway;
        $this->notificationService = $notificationService;
        $this->orderRepository = $orderRepository;
    }
    
    public function process(Order $order): ProcessResult
    {
        // Process payment
        $paymentResult = $this->paymentGateway->charge($order->total);
        
        if ($paymentResult->success) {
            $order->status = 'paid';
            $this->orderRepository->save($order);
            
            // Send notification
            $this->notificationService->send(
                $order->user,
                'Your order has been processed'
            );
            
            return ProcessResult::success($order);
        }
        
        return ProcessResult::failure($paymentResult->error);
    }
}

// Service provider binding abstractions to implementations
class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        // Bind interfaces to implementations
        $this->app->bind(
            PaymentGatewayInterface::class,
            StripePaymentGateway::class
        );
        
        $this->app->bind(
            NotificationInterface::class,
            EmailNotification::class
        );
        
        $this->app->bind(
            OrderRepositoryInterface::class,
            EloquentOrderRepository::class
        );
    }
}

// Testing with different implementations
class TestOrderProcessor extends TestCase
{
    public function test_order_processing()
    {
        // Use mock implementations
        $mockPaymentGateway = $this->createMock(PaymentGatewayInterface::class);
        $mockNotification = $this->createMock(NotificationInterface::class);
        $mockRepository = $this->createMock(OrderRepositoryInterface::class);
        
        $processor = new OrderProcessor(
            $mockPaymentGateway,
            $mockNotification,
            $mockRepository
        );
        
        // Test with mocked dependencies
    }
}

Section 3: Clean Code Practices

3.1 Meaningful Naming Conventions

Bad Naming:

class x {
    public function y($a, $b) {
        $c = $a + $b;
        $d = $c * 10;
        return $d;
    }
}

function proc($d) {
    // What does 'd' mean?
}

$temp = getUser(); // Temporary what?
$flag = true; // Flag for what?

Good Naming:

class ShoppingCart {
    public function calculateTotalWithTax(float $subtotal, float $taxRate): float {
        $taxAmount = $subtotal * $taxRate;
        $totalAmount = $subtotal + $taxAmount;
        return $totalAmount;
    }
}

function processOrder(Order $order): void {
    // Clear intention
}

$user = getAuthenticatedUser();
$isUserActive = $user->isActive();
$hasValidSubscription = $user->subscription()->valid();

Naming Guidelines:

// Classes: Nouns, PascalCase
class OrderProcessor {}
class UserRepository {}
class EmailNotificationService {}

// Methods: Verbs, camelCase
public function calculateTotal() {}
public function sendNotification() {}
public function validateInput() {}

// Variables: Descriptive, camelCase
$orderTotal = 100.50;
$customerEmail = 'john@example.com';
$isPaymentSuccessful = true;

// Constants: UPPER_SNAKE_CASE
const MAX_LOGIN_ATTEMPTS = 5;
const DEFAULT_PAGINATION_SIZE = 25;

// Boolean variables: prefix with is/has/can/should
$isActive = true;
$hasPermission = false;
$canEdit = true;
$shouldNotify = false;

// Collections: plural
$users = User::all();
$orders = $customer->orders;

// Single items: singular
$user = User::find(1);
$order = Order::latest()->first();

3.2 Function/Method Design

Bad Function:

public function process($data) {
    // Too many responsibilities
    if ($data) {
        $user = User::find($data['user_id']);
        if ($user) {
            $order = Order::create([
                'user_id' => $user->id,
                'total' => $data['total'],
                'items' => $data['items'],
            ]);
            
            if ($order) {
                Mail::to($user->email)->send(new OrderConfirmation($order));
                
                $log = ActivityLog::create([
                    'user_id' => $user->id,
                    'action' => 'order_created',
                    'details' => json_encode($order),
                ]);
                
                return ['success' => true, 'order' => $order];
            }
        }
    }
    
    return ['success' => false, 'error' => 'Failed'];
}

Good Function (Extracted Responsibilities):

public function createOrderFromRequest(OrderRequest $request): OrderCreationResult
{
    $user = $this->findUser($request->userId());
    
    $this->validateUserCanOrder($user);
    
    $order = $this->createOrder($user, $request->items(), $request->total());
    
    $this->sendOrderConfirmation($user, $order);
    
    $this->logOrderActivity($user, $order);
    
    return OrderCreationResult::success($order);
}

private function findUser(int $userId): User
{
    $user = User::find($userId);
    
    if (!$user) {
        throw new UserNotFoundException("User {$userId} not found");
    }
    
    return $user;
}

private function validateUserCanOrder(User $user): void
{
    if (!$user->isActive()) {
        throw new UserNotActiveException("User {$user->id} is not active");
    }
    
    if ($user->hasExceededOrderLimit()) {
        throw new OrderLimitExceededException("Order limit exceeded");
    }
}

private function createOrder(User $user, array $items, float $total): Order
{
    return DB::transaction(function () use ($user, $items, $total) {
        $order = Order::create([
            'user_id' => $user->id,
            'total' => $total,
            'status' => 'pending',
        ]);
        
        foreach ($items as $item) {
            $order->items()->create([
                'product_id' => $item['product_id'],
                'quantity' => $item['quantity'],
                'price' => $item['price'],
            ]);
        }
        
        return $order;
    });
}

private function sendOrderConfirmation(User $user, Order $order): void
{
    try {
        Mail::to($user->email)
            ->send(new OrderConfirmation($order));
    } catch (\Exception $e) {
        \Log::warning('Failed to send order confirmation', [
            'order_id' => $order->id,
            'error' => $e->getMessage(),
        ]);
    }
}

private function logOrderActivity(User $user, Order $order): void
{
    ActivityLog::create([
        'user_id' => $user->id,
        'action' => 'order_created',
        'details' => [
            'order_id' => $order->id,
            'total' => $order->total,
            'item_count' => $order->items->count(),
        ],
    ]);
}

3.3 Code Organization & Structure

Project Structure Guidelines:

app/
├── Console/
│   ├── Commands/
│   │   ├── Import/
│   │   │   ├── ImportUsers.php
│   │   │   └── ImportProducts.php
│   │   └── Reports/
│   │       ├── GenerateSalesReport.php
│   │       └── GenerateUserReport.php
│   └── Kernel.php
├── Domain/                          # Business logic layer
│   ├── Users/
│   │   ├── Actions/                 # Single-use actions
│   │   │   ├── CreateUser.php
│   │   │   ├── UpdateUser.php
│   │   │   └── DeleteUser.php
│   │   ├── Collections/             # Custom collections
│   │   │   └── UserCollection.php
│   │   ├── DataTransferObjects/     # DTOs
│   │   │   ├── UserData.php
│   │   │   └── UserRegistrationData.php
│   │   ├── Events/                  # Domain events
│   │   │   ├── UserCreated.php
│   │   │   └── UserUpdated.php
│   │   ├── Exceptions/              # Domain exceptions
│   │   │   ├── UserNotFoundException.php
│   │   │   └── InvalidUserException.php
│   │   ├── Listeners/               # Event listeners
│   │   │   ├── SendWelcomeEmail.php
│   │   │   └── LogUserActivity.php
│   │   ├── Models/                  # Eloquent models
│   │   │   ├── User.php
│   │   │   └── Profile.php
│   │   ├── Observers/               # Model observers
│   │   │   └── UserObserver.php
│   │   ├── Policies/                # Authorization policies
│   │   │   └── UserPolicy.php
│   │   ├── Rules/                   # Validation rules
│   │   │   ├── UniqueUserEmail.php
│   │   │   └── ValidUserRole.php
│   │   ├── Scopes/                  # Query scopes
│   │   │   ├── ActiveScope.php
│   │   │   └── WithRoleScope.php
│   │   └── ValueObjects/            # Value objects
│   │       ├── Email.php
│   │       └── Password.php
│   ├── Products/
│   │   └── ... (similar structure)
│   └── Orders/
│       └── ... (similar structure)
├── Application/                     # Application services layer
│   ├── Users/
│   │   ├── Queries/                 # Query classes
│   │   │   ├── GetActiveUsers.php
│   │   │   └── GetUsersWithOrders.php
│   │   ├── Services/                # Application services
│   │   │   ├── UserRegistrationService.php
│   │   │   └── UserProfileService.php
│   │   └── ViewModels/              # View models
│   │       └── UserProfileViewModel.php
│   └── Orders/
│       └── ... (similar structure)
├── Infrastructure/                  # Framework-specific implementations
│   ├── Http/
│   │   ├── Controllers/
│   │   │   ├── Api/
│   │   │   └── V1/              # API versioning
│   │   │       ├── UserController.php
│   │   │       └── OrderController.php
│   │   └── Web/
│   │       ├── UserController.php
│   │       └── OrderController.php
│   ├── Middleware/
│   │   ├── Api/
│   │   │   ├── Authenticate.php
│   │   │   └── ThrottleRequests.php
│   │   └── Web/
│   │       ├── Authenticate.php
│   │       └── TrimStrings.php
│   ├── Requests/
│   │   ├── Api/
│   │   │   └── V1/
│   │   │       ├── User/
│   │   │       │   ├── StoreRequest.php
│   │   │       │   └── UpdateRequest.php
│   │   │       └── Order/
│   │   │           ├── StoreRequest.php
│   │   │           └── UpdateRequest.php
│   │   └── Web/
│   │       └── ... (similar structure)
│   ├── Resources/
│   │   ├── Api/
│   │   │   └── V1/
│   │   │       ├── UserResource.php
│   │   │       └── OrderResource.php
│   │   └── Web/
│   │       └── ... (similar structure)
│   ├── Database/
│   │   ├── Factories/
│   │   │   ├── UserFactory.php
│   │   │   └── OrderFactory.php
│   │   ├── Migrations/
│   │   │   ├── 2023_01_01_000000_create_users_table.php
│   │   │   └── 2023_01_01_000001_create_orders_table.php
│   │   ├── Seeders/
│   │   │   ├── DatabaseSeeder.php
│   │   │   ├── UserSeeder.php
│   │   │   └── OrderSeeder.php
│   │   └── Repositories/
│   │       ├── UserRepository.php
│   │       └── OrderRepository.php
│   ├── Mail/
│   │   ├── WelcomeEmail.php
│   │   └── OrderConfirmation.php
│   ├── Notifications/
│   │   ├── OrderShipped.php
│   │   └── PasswordReset.php
│   └── Providers/
│       ├── AppServiceProvider.php
│       ├── AuthServiceProvider.php
│       ├── EventServiceProvider.php
│       ├── RepositoryServiceProvider.php
│       └── RouteServiceProvider.php
├── Support/                         # Helper classes
│   ├── Helpers/
│   │   ├── DateHelper.php
│   │   └── StringHelper.php
│   ├── Traits/
│   │   ├── HasUuid.php
│   │   └── LogsActivity.php
│   └── Collections/
│       └── PaginatedCollection.php
└── Exceptions/
    └── Handler.php

3.4 Commenting & Documentation

Bad Comments:

// Get users
$users = User::all(); // Gets all users

// Loop through users
foreach ($users as $user) {
    // Send email
    Mail::send(...); // Sends email
}

Good Comments (Documenting WHY, not WHAT):

/**
 * Fetches active users who have made a purchase in the last 30 days.
 * This is used for the targeted marketing campaign that requires
 * recently active customers to maximize conversion rates.
 *
 * @param int $minimumPurchase Minimum purchase amount in cents
 * @param DateTimeInterface $since Only include purchases after this date
 * @return UserCollection Collection of active purchasing users
 * @throws DatabaseConnectionException If database connection fails
 */
public function getActivePurchasingUsers(
    int $minimumPurchase = 1000,
    DateTimeInterface $since = null
): UserCollection {
    $since = $since ?? now()->subDays(30);
    
    return User::query()
        ->where('active', true)
        ->whereHas('orders', function ($query) use ($minimumPurchase, $since) {
            $query->where('total', '>=', $minimumPurchase)
                  ->where('created_at', '>=', $since);
        })
        ->with(['orders' => function ($query) use ($since) {
            $query->where('created_at', '>=', $since)
                  ->orderBy('created_at', 'desc');
        }])
        ->get();
}

// Exception for complex business logic
if ($user->subscription->isTrial() && $user->loginCount > 5) {
    // Trial users with more than 5 logins are likely engaged
    // and should see premium features to encourage conversion
    $showPremiumFeatures = true;
}

// TODO comments for future improvements
// TODO: Implement caching for this query as it's called frequently
// TODO: Add support for multiple currency conversions
// TODO: Consider moving this to a queued job for better performance

// FIXME comments for known issues
// FIXME: This workaround handles timezone issues until the API is fixed
// FIXME: Remove this temporary fix after v2.1 release

// NOTE comments for important information
// NOTE: This method assumes the user's email is verified
// NOTE: Cache invalidation happens automatically via model events

PHPDoc Standards:

/**
 * Processes an order and handles payment, inventory, and notifications.
 *
 * @param Order $order The order to process
 * @param PaymentMethod $paymentMethod Payment method to use
 * @param bool $sendNotifications Whether to send customer notifications
 * @return ProcessResult Result containing success status and any errors
 * @throws PaymentFailedException If payment processing fails
 * @throws InsufficientInventoryException If items are out of stock
 * @throws OrderAlreadyProcessedException If order was already processed
 *
 * @example
 * $result = $orderProcessor->process($order, $paymentMethod);
 * if ($result->success) {
 *     echo "Order processed successfully!";
 * }
 */
public function process(
    Order $order,
    PaymentMethod $paymentMethod,
    bool $sendNotifications = true
): ProcessResult {
    // Implementation
}

Section 4: Database & Eloquent Best Practices

4.1 Eloquent Model Organization

Well-structured Model:

namespace App\Domain\Users\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use App\Domain\Users\Collections\UserCollection;
use App\Domain\Users\Scopes\ActiveScope;
use App\Domain\Users\Traits\HasPermissions;
use App\Domain\Users\Events\UserCreated;
use App\Domain\Users\Events\UserUpdated;

class User extends Model
{
    use HasPermissions;
    
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
        'active',
        'email_verified_at',
    ];
    
    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
        'two_factor_secret',
        'two_factor_recovery_codes',
    ];
    
    /**
     * The attributes that should be cast.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
        'active' => 'boolean',
        'settings' => 'array',
        'last_login_at' => 'datetime',
        'metadata' => 'object',
    ];
    
    /**
     * The event map for the model.
     *
     * @var array
     */
    protected $dispatchesEvents = [
        'created' => UserCreated::class,
        'updated' => UserUpdated::class,
    ];
    
    /**
     * The relationships that should always be loaded.
     *
     * @var array
     */
    protected $with = [
        'profile',
    ];
    
    /**
     * Create a new Eloquent Collection instance.
     *
     * @param  array  $models
     * @return UserCollection
     */
    public function newCollection(array $models = []): UserCollection
    {
        return new UserCollection($models);
    }
    
    /**
     * Boot the model.
     *
     * @return void
     */
    protected static function booted(): void
    {
        static::addGlobalScope(new ActiveScope);
        
        static::creating(function ($user) {
            $user->uuid = (string) \Str::uuid();
        });
        
        static::updating(function ($user) {
            if ($user->isDirty('email')) {
                $user->email_verified_at = null;
            }
        });
    }
    
    /**
     * Scope a query to only include popular users.
     *
     * @param Builder $query
     * @param int $minFollowers
     * @return Builder
     */
    public function scopePopular(Builder $query, int $minFollowers = 1000): Builder
    {
        return $query->where('followers_count', '>=', $minFollowers);
    }
    
    /**
     * Scope a query to only include users with a specific role.
     *
     * @param Builder $query
     * @param string $role
     * @return Builder
     */
    public function scopeWithRole(Builder $query, string $role): Builder
    {
        return $query->whereHas('roles', function ($q) use ($role) {
            $q->where('name', $role);
        });
    }
    
    /**
     * Get the user's full name.
     *
     * @return string
     */
    public function getFullNameAttribute(): string
    {
        return "{$this->first_name} {$this->last_name}";
    }
    
    /**
     * Set the user's password.
     *
     * @param string $value
     * @return void
     */
    public function setPasswordAttribute(string $value): void
    {
        $this->attributes['password'] = bcrypt($value);
    }
    
    /**
     * Determine if the user is an administrator.
     *
     * @return bool
     */
    public function isAdministrator(): bool
    {
        return $this->hasRole('administrator');
    }
    
    /**
     * Determine if the user has verified their email.
     *
     * @return bool
     */
    public function hasVerifiedEmail(): bool
    {
        return !is_null($this->email_verified_at);
    }
    
    /**
     * Get the user's profile.
     *
     * @return HasOne
     */
    public function profile(): HasOne
    {
        return $this->hasOne(Profile::class);
    }
    
    /**
     * Get the user's orders.
     *
     * @return HasMany
     */
    public function orders(): HasMany
    {
        return $this->hasMany(Order::class);
    }
    
    /**
     * Get the user's roles.
     *
     * @return BelongsToMany
     */
    public function roles(): BelongsToMany
    {
        return $this->belongsToMany(Role::class)
            ->withTimestamps()
            ->withPivot('assigned_by');
    }
}

4.2 Efficient Database Queries

Common N+1 Problem & Solutions:

// ❌ BAD: N+1 Query Problem
$users = User::all();
foreach ($users as $user) {
    echo $user->profile->bio; // Executes query for each user
}

// ✅ GOOD: Eager Loading
$users = User::with('profile')->get();
foreach ($users as $user) {
    echo $user->profile->bio; // No additional queries
}

// ✅ BETTER: Eager load with constraints
$users = User::with([
    'profile',
    'orders' => function ($query) {
        $query->where('status', 'completed')
              ->orderBy('created_at', 'desc')
              ->limit(5);
    },
    'orders.items',
])->get();

// ✅ BEST: Lazy eager loading when needed
$users = User::all();
$users->load(['profile', 'orders']);

// Advanced eager loading patterns
class UserController extends Controller
{
    public function index()
    {
        // Load counts without loading relationships
        $users = User::withCount(['orders', 'posts'])
            ->with(['profile' => function ($query) {
                $query->select('id', 'user_id', 'bio');
            }])
            ->paginate(25);
            
        return UserResource::collection($users);
    }
    
    public function show(User $user)
    {
        // Load nested relationships
        $user->load([
            'profile',
            'orders.items.product',
            'posts.comments.user',
            'notifications' => function ($query) {
                $query->where('read', false)
                      ->orderBy('created_at', 'desc');
            },
        ]);
        
        return new UserResource($user);
    }
}

// Using subqueries for optimization
$users = User::addSelect([
    'last_order_date' => Order::select('created_at')
        ->whereColumn('user_id', 'users.id')
        ->latest()
        ->limit(1),
        
    'total_spent' => Order::selectRaw('SUM(total)')
        ->whereColumn('user_id', 'users.id')
        ->where('status', 'completed'),
])->get();

4.3 Database Migration Best Practices

Comprehensive Migration Example:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up(): void
    {
        Schema::create('users', function (Blueprint $table) {
            // Primary key
            $table->id();
            $table->uuid('uuid')->unique();
            
            // Basic information
            $table->string('first_name', 100);
            $table->string('last_name', 100);
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            
            // Profile information
            $table->string('phone', 20)->nullable();
            $table->date('date_of_birth')->nullable();
            $table->enum('gender', ['male', 'female', 'other'])->nullable();
            
            // Status flags
            $table->boolean('active')->default(true);
            $table->boolean('is_suspended')->default(false);
            $table->timestamp('suspended_until')->nullable();
            
            // Preferences
            $table->string('timezone', 50)->default('UTC');
            $table->string('locale', 10)->default('en');
            $table->json('preferences')->nullable();
            $table->json('metadata')->nullable();
            
            // Statistics
            $table->integer('login_count')->default(0);
            $table->timestamp('last_login_at')->nullable();
            $table->ipAddress('last_login_ip')->nullable();
            
            // Indexes
            $table->index(['active', 'created_at']);
            $table->index('email_verified_at');
            $table->index('last_login_at');
            $table->index(['first_name', 'last_name']);
            
            // Full-text search
            $table->fullText(['first_name', 'last_name', 'email']);
            
            // Timestamps
            $table->timestamps();
            $table->softDeletes();
            
            // Additional comments
            $table->comment('Stores user account information');
        });
        
        // Create profile table with foreign key
        Schema::create('profiles', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')
                ->constrained()
                ->onUpdate('cascade')
                ->onDelete('cascade');
                
            $table->text('bio')->nullable();
            $table->string('avatar_url')->nullable();
            $table->string('website')->nullable();
            $table->json('social_links')->nullable();
            
            $table->timestamps();
            
            // Unique constraint
            $table->unique('user_id');
        });
        
        // Create many-to-many pivot table
        Schema::create('role_user', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')
                ->constrained()
                ->onUpdate('cascade')
                ->onDelete('cascade');
                
            $table->foreignId('role_id')
                ->constrained()
                ->onUpdate('cascade')
                ->onDelete('cascade');
                
            $table->foreignId('assigned_by')
                ->nullable()
                ->constrained('users')
                ->nullOnDelete();
                
            $table->timestamp('expires_at')->nullable();
            $table->text('notes')->nullable();
            
            $table->timestamps();
            
            // Composite unique with additional condition
            $table->unique(['user_id', 'role_id', 'expires_at']);
            
            // Partial index
            $table->index(['expires_at'], 'idx_active_roles')
                ->where('expires_at', '>', DB::raw('NOW()'));
        });
        
        // Add column with default value
        Schema::table('users', function (Blueprint $table) {
            $table->string('display_name')
                ->virtualAs("CONCAT(first_name, ' ', last_name)")
                ->after('last_name');
        });
        
        // Create stored procedure for complex operations
        DB::unprepared('
            CREATE PROCEDURE CalculateUserStats(IN userId INT)
            BEGIN
                SELECT 
                    COUNT(DISTINCT o.id) as order_count,
                    SUM(o.total) as total_spent,
                    AVG(o.total) as avg_order_value,
                    MAX(o.created_at) as last_order_date
                FROM orders o
                WHERE o.user_id = userId
                AND o.status = "completed";
            END
        ');
    }
    
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down(): void
    {
        // Drop in reverse order
        DB::unprepared('DROP PROCEDURE IF EXISTS CalculateUserStats');
        
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('display_name');
        });
        
        Schema::dropIfExists('role_user');
        Schema::dropIfExists('profiles');
        Schema::dropIfExists('users');
    }
};

Section 5: Testing Best Practices

5.1 Comprehensive Test Structure

Test Organization:

tests/
├── Unit/                           # Unit tests (isolated)
│   ├── Domain/
│   │   ├── Users/
│   │   │   ├── Actions/
│   │   │   │   ├── CreateUserTest.php
│   │   │   │   └── UpdateUserTest.php
│   │   │   ├── Models/
│   │   │   │   └── UserTest.php
│   │   │   ├── ValueObjects/
│   │   │   │   └── EmailTest.php
│   │   │   └── Rules/
│   │   │       └── UniqueUserEmailTest.php
│   │   └── Products/
│   │       └── ...
│   └── Support/
│       └── HelpersTest.php
├── Feature/                        # Feature tests (with framework)
│   ├── Api/
│   │   └── V1/
│   │       ├── AuthenticationTest.php
│   │       ├── UserTest.php
│   │       └── OrderTest.php
│   ├── Web/
│   │   ├── AuthenticationTest.php
│   │   ├── UserTest.php
│   │   └── OrderTest.php
│   └── Console/
│       ├── ImportUsersTest.php
│       └── GenerateReportTest.php
├── Integration/                    # Integration tests
│   ├── Database/
│   │   ├── UserRepositoryTest.php
│   │   └── OrderRepositoryTest.php
│   └── Services/
│       ├── PaymentServiceTest.php
│       └── NotificationServiceTest.php
├── Browser/                        # Dusk tests
│   ├── AuthenticationTest.php
│   └── CheckoutTest.php
└── TestCase.php

5.2 Comprehensive Test Examples

Unit Test Example:

<?php

namespace Tests\Unit\Domain\Users\Actions;

use Tests\TestCase;
use App\Domain\Users\Actions\CreateUser;
use App\Domain\Users\DataTransferObjects\UserData;
use App\Domain\Users\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Hash;

class CreateUserTest extends TestCase
{
    use RefreshDatabase;
    
    /** @test */
    public function it_creates_a_user_with_valid_data(): void
    {
        // Arrange
        $userData = new UserData(
            name: 'John Doe',
            email: 'john@example.com',
            password: 'Secret123!'
        );
        
        $action = new CreateUser();
        
        // Act
        $user = $action->execute($userData);
        
        // Assert
        $this->assertInstanceOf(User::class, $user);
        $this->assertDatabaseHas('users', [
            'email' => 'john@example.com',
            'name' => 'John Doe',
        ]);
        $this->assertTrue(Hash::check('Secret123!', $user->password));
        $this->assertNotNull($user->email_verified_at);
    }
    
    /** @test */
    public function it_throws_exception_for_duplicate_email(): void
    {
        // Arrange
        User::factory()->create(['email' => 'existing@example.com']);
        
        $userData = new UserData(
            name: 'John Doe',
            email: 'existing@example.com',
            password: 'Secret123!'
        );
        
        $action = new CreateUser();
        
        // Assert
        $this->expectException(\Exception::class);
        $this->expectExceptionMessage('Email already registered');
        
        // Act
        $action->execute($userData);
    }
    
    /** @test */
    public function it_generates_uuid_for_new_user(): void
    {
        // Arrange
        $userData = new UserData(
            name: 'John Doe',
            email: 'john@example.com',
            password: 'Secret123!'
        );
        
        $action = new CreateUser();
        
        // Act
        $user = $action->execute($userData);
        
        // Assert
        $this->assertNotNull($user->uuid);
        $this->assertMatchesRegularExpression(
            '/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/',
            $user->uuid
        );
    }
    
    /** @test */
    public function it_creates_user_profile_along_with_user(): void
    {
        // Arrange
        $userData = new UserData(
            name: 'John Doe',
            email: 'john@example.com',
            password: 'Secret123!'
        );
        
        $action = new CreateUser();
        
        // Act
        $user = $action->execute($userData);
        
        // Assert
        $this->assertNotNull($user->profile);
        $this->assertEquals('New user', $user->profile->bio);
    }
    
    /** @test */
    public function it_hashes_password_before_storing(): void
    {
        // Arrange
        $userData = new UserData(
            name: 'John Doe',
            email: 'john@example.com',
            password: 'plaintext-password'
        );
        
        $action = new CreateUser();
        
        // Act
        $user = $action->execute($userData);
        
        // Assert
        $this->assertNotEquals('plaintext-password', $user->password);
        $this->assertTrue(Hash::check('plaintext-password', $user->password));
    }
}

Feature Test Example:

<?php

namespace Tests\Feature\Api\V1;

use Tests\TestCase;
use App\Domain\Users\Models\User;
use Laravel\Sanctum\Sanctum;
use Illuminate\Foundation\Testing\RefreshDatabase;

class UserTest extends TestCase
{
    use RefreshDatabase;
    
    /** @test */
    public function authenticated_user_can_get_their_profile(): void
    {
        // Arrange
        $user = User::factory()->create();
        Sanctum::actingAs($user, ['*']);
        
        // Act
        $response = $this->getJson('/api/v1/user');
        
        // Assert
        $response->assertOk()
            ->assertJsonStructure([
                'data' => [
                    'id',
                    'name',
                    'email',
                    'created_at',
                    'updated_at',
                ]
            ])
            ->assertJson([
                'data' => [
                    'email' => $user->email,
                    'name' => $user->name,
                ]
            ]);
    }
    
    /** @test */
    public function unauthenticated_user_cannot_access_profile(): void
    {
        // Act
        $response = $this->getJson('/api/v1/user');
        
        // Assert
        $response->assertUnauthorized();
    }
    
    /** @test */
    public function user_can_update_their_profile(): void
    {
        // Arrange
        $user = User::factory()->create();
        Sanctum::actingAs($user, ['*']);
        
        $updateData = [
            'name' => 'Updated Name',
            'email' => 'updated@example.com',
        ];
        
        // Act
        $response = $this->putJson('/api/v1/user', $updateData);
        
        // Assert
        $response->assertOk()
            ->assertJson([
                'data' => [
                    'name' => 'Updated Name',
                    'email' => 'updated@example.com',
                ]
            ]);
            
        $this->assertDatabaseHas('users', [
            'id' => $user->id,
            'name' => 'Updated Name',
            'email' => 'updated@example.com',
        ]);
    }
    
    /** @test */
    public function user_cannot_update_to_existing_email(): void
    {
        // Arrange
        $user1 = User::factory()->create(['email' => 'user1@example.com']);
        $user2 = User::factory()->create(['email' => 'user2@example.com']);
        
        Sanctum::actingAs($user1, ['*']);
        
        $updateData = [
            'email' => 'user2@example.com', // Already taken
        ];
        
        // Act
        $response = $this->putJson('/api/v1/user', $updateData);
        
        // Assert
        $response->assertUnprocessable()
            ->assertJsonValidationErrors(['email']);
    }
    
    /** @test */
    public function admin_can_view_all_users(): void
    {
        // Arrange
        $admin = User::factory()->admin()->create();
        $users = User::factory()->count(5)->create();
        
        Sanctum::actingAs($admin, ['*']);
        
        // Act
        $response = $this->getJson('/api/v1/users');
        
        // Assert
        $response->assertOk()
            ->assertJsonStructure([
                'data' => [
                    '*' => ['id', 'name', 'email', 'created_at']
                ],
                'links',
                'meta'
            ])
            ->assertJsonCount(6, 'data'); // Admin + 5 users
    }
    
    /** @test */
    public function non_admin_cannot_view_all_users(): void
    {
        // Arrange
        $user = User::factory()->create();
        Sanctum::actingAs($user, ['*']);
        
        // Act
        $response = $this->getJson('/api/v1/users');
        
        // Assert
        $response->assertForbidden();
    }
    
    /** @test */
    public function it_validates_user_update_data(): void
    {
        // Arrange
        $user = User::factory()->create();
        Sanctum::actingAs($user, ['*']);
        
        $invalidData = [
            'name' => '', // Empty name
            'email' => 'invalid-email', // Invalid email
        ];
        
        // Act
        $response = $this->putJson('/api/v1/user', $invalidData);
        
        // Assert
        $response->assertUnprocessable()
            ->assertJsonValidationErrors(['name', 'email']);
    }
    
    /** @test */
    public function user_can_delete_their_account(): void
    {
        // Arrange
        $user = User::factory()->create();
        Sanctum::actingAs($user, ['*']);
        
        // Act
        $response = $this->deleteJson('/api/v1/user');
        
        // Assert
        $response->assertNoContent();
        $this->assertSoftDeleted('users', ['id' => $user->id]);
    }
}

Section 6: Performance Optimization

6.1 Database Query Optimization

Query Optimization Techniques:

// 1. Select only needed columns
// ❌ BAD: Selects all columns
$users = User::all();

// ✅ GOOD: Select only needed columns
$users = User::select(['id', 'name', 'email'])->get();

// 2. Use chunking for large datasets
// ❌ BAD: Loads all records into memory
User::all()->each(function ($user) {
    processUser($user);
});

// ✅ GOOD: Processes in chunks
User::chunk(1000, function ($users) {
    foreach ($users as $user) {
        processUser($user);
    }
});

// ✅ BETTER: Use cursor for very large datasets
foreach (User::cursor() as $user) {
    processUser($user);
}

// 3. Efficient relationship loading
// ❌ BAD: Multiple queries
$posts = Post::all();
foreach ($posts as $post) {
    $comments = $post->comments; // N+1 problem
}

// ✅ GOOD: Eager loading with constraints
$posts = Post::with([
    'comments' => function ($query) {
        $query->select('id', 'post_id', 'content')
              ->where('approved', true)
              ->orderBy('created_at', 'desc')
              ->limit(5);
    },
    'comments.user:id,name,avatar',
])->get();

// 4. Use database indexes effectively
class CreatePostsTable extends Migration
{
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('content');
            $table->foreignId('user_id')->constrained();
            $table->boolean('published')->default(false);
            $table->timestamp('published_at')->nullable();
            $table->timestamps();
            $table->softDeletes();
            
            // Add indexes for common queries
            $table->index(['published', 'published_at']);
            $table->index(['user_id', 'created_at']);
            $table->fullText(['title', 'content']);
            
            // Composite index for specific queries
            $table->index(['published', 'user_id', 'created_at']);
        });
    }
}

// 5. Use subqueries for aggregates
$users = User::addSelect([
    'posts_count' => Post::selectRaw('COUNT(*)')
        ->whereColumn('user_id', 'users.id'),
        
    'last_post_date' => Post::select('created_at')
        ->whereColumn('user_id', 'users.id')
        ->latest()
        ->limit(1),
])->get();

// 6. Cache expensive queries
public function getPopularPosts()
{
    return Cache::remember('popular_posts', 3600, function () {
        return Post::withCount('likes')
            ->with(['user:id,name', 'category:id,name'])
            ->where('published', true)
            ->where('created_at', '>', now()->subMonth())
            ->orderByDesc('likes_count')
            ->limit(10)
            ->get();
    });
}

// 7. Use database views for complex queries
DB::statement('
    CREATE VIEW user_stats AS
    SELECT 
        u.id,
        u.name,
        u.email,
        COUNT(DISTINCT p.id) as post_count,
        COUNT(DISTINCT c.id) as comment_count,
        COALESCE(SUM(p.views), 0) as total_views,
        MAX(p.created_at) as last_post_date
    FROM users u
    LEFT JOIN posts p ON p.user_id = u.id AND p.published = true
    LEFT JOIN comments c ON c.user_id = u.id AND c.approved = true
    GROUP BY u.id, u.name, u.email
');

// Usage
$stats = DB::table('user_stats')->get();

Section 7: Security Best Practices

7.1 Comprehensive Security Implementation

Security Middleware Stack:

<?php

namespace App\Infrastructure\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class SecurityHeaders
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);
        
        // Security headers
        $response->headers->set('X-Content-Type-Options', 'nosniff');
        $response->headers->set('X-Frame-Options', 'SAMEORIGIN');
        $response->headers->set('X-XSS-Protection', '1; mode=block');
        $response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
        $response->headers->set('Permissions-Policy', 'geolocation=(), microphone=()');
        
        // Content Security Policy
        $csp = $this->buildCspHeader();
        $response->headers->set('Content-Security-Policy', $csp);
        
        // Strict Transport Security (HTTPS only)
        if ($request->secure()) {
            $response->headers->set(
                'Strict-Transport-Security',
                'max-age=31536000; includeSubDomains; preload'
            );
        }
        
        return $response;
    }
    
    private function buildCspHeader(): string
    {
        $directives = [
            "default-src 'self'",
            "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://www.google-analytics.com",
            "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net",
            "img-src 'self' data: https: blob:",
            "font-src 'self' https://cdn.jsdelivr.net",
            "connect-src 'self' https://api.example.com wss://ws.example.com",
            "frame-src 'self' https://www.youtube.com",
            "object-src 'none'",
            "media-src 'self'",
            "form-action 'self'",
            "frame-ancestors 'self'",
            "base-uri 'self'",
            "manifest-src 'self'",
        ];
        
        return implode('; ', $directives);
    }
}

Section 8: Deployment & DevOps

8.1 Comprehensive Deployment Pipeline

GitHub Actions Workflow:

name: Laravel CI/CD Pipeline

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  analyze:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup PHP
      uses: shivammathur/setup-php@v2
      with:
        php-version: '8.2'
        extensions: mbstring, xml, ctype, iconv, intl, pdo_mysql, bcmath, redis, gd, zip
        coverage: xdebug
    
    - name: Install Dependencies
      run: |
        composer install --prefer-dist --no-progress --no-interaction
        npm ci
    
    - name: Copy Environment
      run: cp .env.example .env
    
    - name: Generate Key
      run: php artisan key:generate
    
    - name: PHP Syntax Check
      run: find . -type f -name "*.php" -exec php -l {} \;
    
    - name: PHPStan Analysis
      run: vendor/bin/phpstan analyse --memory-limit=2G
    
    - name: Laravel Pint
      run: vendor/bin/pint --test
    
    - name: Security Check
      run: vendor/bin/security-checker security:check
    
    - name: Build Assets
      run: npm run build

  test:
    runs-on: ubuntu-latest
    needs: analyze
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: root
          MYSQL_DATABASE: laravel_test
        options: >-
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=3
        ports:
          - 3306:3306
      
      redis:
        image: redis:alpine
        options: >-
          --health-cmd="redis-cli ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=3
        ports:
          - 6379:6379
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup PHP
      uses: shivammathur/setup-php@v2
      with:
        php-version: '8.2'
        extensions: mbstring, xml, ctype, iconv, intl, pdo_mysql, bcmath, redis, gd, zip
        coverage: xdebug
    
    - name: Install Dependencies
      run: composer install --prefer-dist --no-progress --no-interaction
    
    - name: Prepare Environment
      run: |
        cp .env.example .env.test
        echo "APP_ENV=testing" >> .env.test
        echo "DB_CONNECTION=mysql" >> .env.test
        echo "DB_HOST=127.0.0.1" >> .env.test
        echo "DB_PORT=3306" >> .env.test
        echo "DB_DATABASE=laravel_test" >> .env.test
        echo "DB_USERNAME=root" >> .env.test
        echo "DB_PASSWORD=root" >> .env.test
        echo "REDIS_HOST=127.0.0.1" >> .env.test
        echo "REDIS_PORT=6379" >> .env.test
    
    - name: Generate Key
      run: php artisan key:generate --env=testing
    
    - name: Create Database
      run: |
        mysql -h 127.0.0.1 -u root -proot -e "CREATE DATABASE IF NOT EXISTS laravel_test"
        mysql -h 127.0.0.1 -u root -proot -e "ALTER DATABASE laravel_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
    
    - name: Run Migrations
      run: php artisan migrate --env=testing --force
    
    - name: Run Tests
      run: |
        php artisan test --parallel --reports=dots
      env:
        DB_CONNECTION: mysql
        DB_HOST: 127.0.0.1
        DB_PORT: 3306
        DB_DATABASE: laravel_test
        DB_USERNAME: root
        DB_PASSWORD: root
    
    - name: Upload Coverage
      uses: codecov/codecov-action@v3
      with:
        file: ./coverage.xml
        flags: unittests

  deploy-staging:
    runs-on: ubuntu-latest
    needs: test
    if: github.ref == 'refs/heads/develop'
    environment: staging
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup SSH
      uses: webfactory/ssh-agent@v0.5.3
      with:
        ssh-private-key: ${{ secrets.STAGING_SSH_KEY }}
    
    - name: Deploy to Staging
      run: |
        ssh -o StrictHostKeyChecking=no ${{ secrets.STAGING_USER }}@${{ secrets.STAGING_HOST }} << 'EOF'
          cd /var/www/staging
          git pull origin develop
          composer install --no-dev --optimize-autoloader
          npm ci --only=production
          npm run build
          php artisan migrate --force
          php artisan config:cache
          php artisan route:cache
          php artisan view:cache
          php artisan queue:restart
          sudo systemctl reload php8.2-fpm
        EOF

  deploy-production:
    runs-on: ubuntu-latest
    needs: test
    if: github.ref == 'refs/heads/main'
    environment: production
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup SSH
      uses: webfactory/ssh-agent@v0.5.3
      with:
        ssh-private-key: ${{ secrets.PRODUCTION_SSH_KEY }}
    
    - name: Deploy to Production
      run: |
        ssh -o StrictHostKeyChecking=no ${{ secrets.PRODUCTION_USER }}@${{ secrets.PRODUCTION_HOST }} << 'EOF'
          cd /var/www/production
          
          # Enable maintenance mode
          php artisan down --secret=${{ secrets.MAINTENANCE_SECRET }}
          
          # Backup database
          mysqldump -u${{ secrets.DB_USERNAME }} -p${{ secrets.DB_PASSWORD }} ${{ secrets.DB_DATABASE }} > backup-$(date +%Y%m%d-%H%M%S).sql
          
          # Deploy new code
          git fetch origin main
          git reset --hard origin/main
          
          # Install dependencies
          composer install --no-dev --optimize-autoloader --no-interaction
          npm ci --only=production --silent
          npm run build --silent
          
          # Run migrations
          php artisan migrate --force
          
          # Clear and cache config
          php artisan config:clear
          php artisan config:cache
          php artisan route:cache
          php artisan view:cache
          php artisan event:cache
          
          # Set permissions
          sudo chown -R www-data:www-data storage bootstrap/cache
          sudo chmod -R 775 storage bootstrap/cache
          
          # Restart services
          php artisan queue:restart
          sudo supervisorctl restart all
          sudo systemctl reload php8.2-fpm
          sudo systemctl reload nginx
          
          # Clear opcache
          sudo service php8.2-fpm reload
          
          # Disable maintenance mode
          php artisan up
          
          # Health check
          curl -f https://${{ secrets.PRODUCTION_DOMAIN }}/health || exit 1
        EOF
    
    - name: Notify Deployment
      uses: 8398a7/action-slack@v3
      with:
        status: ${{ job.status }}
        channel: '#deployments'
        author_name: GitHub Actions
      env:
        SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

Section 9: Code Quality Tools

9.1 Static Analysis Setup

PHPStan Configuration:

# phpstan.neon
parameters:
    level: 8
    paths:
        - app
        - database/factories
        - database/seeders
        - tests
    excludePaths:
        - app/Infrastructure/Http/Middleware/TrustProxies.php
        - app/Infrastructure/Exceptions/Handler.php
    fileExtensions:
        - php
    parallel:
        maximumNumberOfProcesses: 4
    memoryLimit: 2G
    
    # Laravel-specific configuration
    checkMissingIterableValueType: false
    
    # Ignore certain error types
    ignoreErrors:
        - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Builder::#'
        - '#Access to an undefined property Illuminate\\Support\\Facades\\Auth::#'
    
    # Custom rules
    rules:
        - PHPStan\Rules\Functions\CallToFunctionStatementWithoutSideEffectsRule
    
    # Bleeding edge for PHP 8.2 features
    featureToggles:
        readonlyClasses: true
        readonlyProperties: true
    
    # Dynamic return type extensions for Laravel
    dynamicConstantNames:
        - config
        - env
    
    # Custom early terminating method calls
    earlyTerminatingMethodCalls:
        App\Domain\Users\Models\User:
            - abortIf
            - abortUnless
    
    # Report unmatched ignored errors
    reportUnmatchedIgnoredErrors: false

includes:
    - vendor/nunomaduro/larastan/extension.neon
    - vendor/phpstan/phpstan-deprecation-rules/rules.neon
    - vendor/phpstan/phpstan-phpunit/rules.neon
    - vendor/phpstan/phpstan-strict-rules/rules.neon

Conclusion

Writing clean and maintainable Laravel code is both an art and a science. By following these best practices, you'll create applications that are:

  • Scalable - Can grow with your business needs
  • Maintainable - Easy to understand and modify
  • Testable - Reliable and confidence-inspiring
  • Performant - Fast and resource-efficient
  • Secure - Protected against common vulnerabilities
  • Deployable - Smooth CI/CD pipelines

Key Takeaways:

  • Architecture Matters: Choose patterns that fit your application's complexity
  • SOLID Principles: These aren't just theory - they're practical tools
  • Clean Code: Readability is more important than cleverness
  • Testing First: Write tests that give you confidence
  • Performance Conscious: Optimize proactively, not reactively
  • Security Minded: Security should be baked in, not bolted on
  • Automate Everything: Let robots do the repetitive work

Continuous Improvement Checklist:

  • Review and refactor code regularly
  • Keep dependencies updated
  • Monitor application performance
  • Gather and act on feedback
  • Stay updated with Laravel releases
  • Share knowledge with your team
  • Document architectural decisions
  • Regularly review security practices

Remember, clean code is not a destination but a journey. Start implementing these practices today, and continually refine your approach as you gain more experience.

Next Steps:

  • Audit your current projects against these practices
  • Create team coding standards
  • Set up automated code quality checks
  • Schedule regular code reviews
  • Invest in developer education
  • Measure and track code quality metrics