Public Holidays Coupon Code Code Compiler

Modern Laravel Development A Complete Guide to Laravel 11 and Best Practices 2025


Sep 28, 2025

Laravel 11 represents a significant leap forward in modern PHP development, offering developers powerful tools, improved performance, and streamlined workflows that make building robust web applications more efficient than ever. By following the best practices outlined in this comprehensive guide, you'll be well-equipped to create secure, scalable, and maintainable Laravel applications that meet modern web development standards.

Whether you're a beginner taking your first steps into the Laravel ecosystem or an experienced developer looking to update your skills with the latest features, this guide provides practical insights, real-world examples, and proven techniques that you can implement immediately in your projects.

What's New in Laravel 11: Revolutionary Features

Streamlined Application Architecture

Laravel 11 introduces a dramatically simplified application skeleton that reduces complexity while maintaining all the powerful features that make Laravel the framework of choice for modern web development. The new architecture makes it easier for developers to understand the framework structure and get started with building robust applications faster than ever.

Enhanced PHP 8.2+ Compatibility

Laravel 11 requires PHP 8.2 or higher, ensuring you're working with cutting-edge PHP features that improve performance, security, and developer experience. This includes support for readonly classes, Disjunctive Normal Form (DNF) types, and significant performance improvements that make your applications faster and more efficient.

Improved Query Builder Performance

The enhanced query builder in Laravel 11 provides better performance optimization and more intuitive methods for complex database operations. This improvement significantly reduces query execution time and makes database interactions more developer-friendly and efficient.

Simplified Configuration Management

Laravel 11 reduces configuration complexity by streamlining the number of configuration files while still providing complete customization options when needed. This approach makes application setup and maintenance more straightforward for developers of all skill levels.

Modern Laravel Best Practices for 2025

Professional Naming Conventions

Following proper naming conventions is crucial for maintaining clean, readable code and preventing eloquent-related issues in your Laravel applications. Here's how to implement professional naming standards:


// Models - PascalCase, singular
class User extends Model {}
class BlogPost extends Model {}
class UserProfile extends Model {}

// Controllers - PascalCase with Controller suffix
class UserController extends Controller {}
class BlogPostController extends Controller {}
class DashboardController extends Controller {}

// Database tables - snake_case, plural
users, blog_posts, user_profiles, order_items

// Columns - snake_case
first_name, created_at, user_id, is_published

// Routes - kebab-case for URLs
Route::get('/blog-posts', [BlogPostController::class, 'index']);
Route::get('/user-profiles', [UserProfileController::class, 'show']);

Service-Oriented Architecture Implementation

Implementing a service-oriented architecture keeps your controllers thin and organizes business logic effectively. This approach improves code maintainability, testability, and follows the single responsibility principle:


// app/Services/UserService.php
class UserService
{
    public function __construct(
        private UserRepository $userRepository,
        private NotificationService $notificationService
    ) {}

    public function createUser(array $userData): User
    {
        $user = $this->userRepository->create([
            'name' => $userData['name'],
            'email' => $userData['email'],
            'password' => Hash::make($userData['password']),
            'email_verified_at' => null,
        ]);

        // Send welcome email
        $this->notificationService->sendWelcomeEmail($user);
        
        // Log user registration
        Log::info('New user registered', [
            'user_id' => $user->id,
            'email' => $user->email
        ]);

        event(new UserRegistered($user));
        
        return $user;
    }

    public function updateUserProfile(User $user, array $profileData): User
    {
        $user->update($profileData);
        
        event(new UserProfileUpdated($user));
        
        return $user->fresh();
    }
}

// In your controller - Clean and focused
class UserController extends Controller
{
    public function store(CreateUserRequest $request, UserService $userService)
    {
        try {
            $user = $userService->createUser($request->validated());
            
            return new UserResource($user);
        } catch (Exception $e) {
            Log::error('User creation failed', [
                'error' => $e->getMessage(),
                'data' => $request->validated()
            ]);
            
            return response()->json([
                'message' => 'User creation failed'
            ], 500);
        }
    }
}

Advanced Request Validation

Use Form Request classes for complex validation logic to keep your controllers clean and validation rules organized. This approach also makes validation rules reusable across different parts of your application:


// app/Http/Requests/CreateUserRequest.php
class CreateUserRequest extends FormRequest
{
    public function authorize(): bool
    {
        // Implement authorization logic
        return auth()->check() && auth()->user()->can('create-users');
    }

    public function rules(): array
    {
        return [
            'name' => [
                'required',
                'string',
                'max:255',
                'regex:/^[a-zA-Z\s]+$/'
            ],
            'email' => [
                'required',
                'email:dns',
                'unique:users,email',
                'max:255'
            ],
            'password' => [
                'required',
                'string',
                'min:8',
                'confirmed',
                'regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/'
            ],
            'phone' => [
                'nullable',
                'string',
                'regex:/^([0-9\s\-\+\(\)]*)$/',
                'min:10'
            ],
            'date_of_birth' => [
                'nullable',
                'date',
                'before:18 years ago'
            ]
        ];
    }

    public function messages(): array
    {
        return [
            'email.unique' => 'This email address is already registered in our system.',
            'password.confirmed' => 'Password confirmation does not match.',
            'password.regex' => 'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character.',
            'date_of_birth.before' => 'You must be at least 18 years old to register.'
        ];
    }

    protected function failedValidation(Validator $validator)
    {
        Log::warning('Validation failed for user creation', [
            'errors' => $validator->errors()->toArray(),
            'ip' => request()->ip()
        ]);

        parent::failedValidation($validator);
    }
}

Database Optimization and Best Practices

Proper database design and query optimization are crucial for application performance. Here are modern approaches to database management in Laravel 11:


// Migration with proper indexing and constraints
Schema::create('blog_posts', function (Blueprint $table) {
    $table->id();
    $table->string('title', 255);
    $table->text('content');
    $table->string('slug')->unique();
    $table->text('excerpt')->nullable();
    $table->string('featured_image')->nullable();
    $table->foreignId('user_id')->constrained()->onDelete('cascade');
    $table->foreignId('category_id')->nullable()->constrained()->onDelete('set null');
    $table->boolean('is_published')->default(false);
    $table->boolean('is_featured')->default(false);
    $table->timestamp('published_at')->nullable();
    $table->unsignedInteger('views_count')->default(0);
    $table->timestamps();
    
    // Strategic indexing for performance
    $table->index(['is_published', 'published_at']);
    $table->index(['user_id', 'is_published']);
    $table->index(['category_id', 'is_published']);
    $table->index('is_featured');
    $table->fullText(['title', 'content', 'excerpt']);
});

// Optimized query examples
class BlogPostRepository
{
    public function getPublishedPosts(int $perPage = 15)
    {
        return BlogPost::select([
                'id', 'title', 'slug', 'excerpt', 
                'featured_image', 'published_at', 'views_count'
            ])
            ->with([
                'user:id,name,avatar',
                'category:id,name,slug'
            ])
            ->where('is_published', true)
            ->where('published_at', '<=', now())
            ->orderBy('published_at', 'desc')
            ->paginate($perPage);
    }

    public function getFeaturedPosts(int $limit = 5)
    {
        return Cache::remember('featured-posts', 3600, function () use ($limit) {
            return BlogPost::select(['id', 'title', 'slug', 'excerpt', 'featured_image'])
                ->where('is_published', true)
                ->where('is_featured', true)
                ->orderBy('published_at', 'desc')
                ->limit($limit)
                ->get();
        });
    }

    public function searchPosts(string $query)
    {
        return BlogPost::whereFullText(['title', 'content', 'excerpt'], $query)
            ->where('is_published', true)
            ->with('user:id,name', 'category:id,name')
            ->paginate(10);
    }
}

Performance Optimization Strategies

Advanced Caching Techniques

Implementing comprehensive caching strategies is essential for building high-performance Laravel applications. Here are proven caching techniques that can dramatically improve your application's speed:


// Configuration and route caching for production
// Run these commands during deployment:
php artisan config:cache
php artisan route:cache  
php artisan view:cache
php artisan event:cache

// Advanced application caching with cache tags
class BlogService
{
    public function getPopularPosts(int $limit = 10)
    {
        return Cache::tags(['blog', 'popular'])
            ->remember('popular-posts-' . $limit, 3600, function () use ($limit) {
                return BlogPost::where('is_published', true)
                    ->orderBy('views_count', 'desc')
                    ->limit($limit)
                    ->with('user:id,name', 'category:id,name')
                    ->get();
            });
    }

    public function getCategoryPosts(Category $category, int $perPage = 15)
    {
        $cacheKey = "category-posts-{$category->id}-page-" . request('page', 1);
        
        return Cache::tags(['blog', 'category-' . $category->id])
            ->remember($cacheKey, 1800, function () use ($category, $perPage) {
                return $category->posts()
                    ->published()
                    ->with('user:id,name')
                    ->paginate($perPage);
            });
    }

    public function invalidateBlogCache(?int $categoryId = null)
    {
        Cache::tags(['blog'])->flush();
        
        if ($categoryId) {
            Cache::tags(['category-' . $categoryId])->flush();
        }
    }
}

// Model observer for automatic cache invalidation
class BlogPostObserver
{
    public function __construct(
        private BlogService $blogService
    ) {}

    public function saved(BlogPost $post)
    {
        $this->blogService->invalidateBlogCache($post->category_id);
    }

    public function deleted(BlogPost $post)
    {
        $this->blogService->invalidateBlogCache($post->category_id);
    }
}

Queue Management for Scalability

Use queues to handle time-consuming operations asynchronously, improving user experience and application responsiveness:


// Create and configure a job for heavy operations
php artisan make:job ProcessUserRegistration

// app/Jobs/ProcessUserRegistration.php
class ProcessUserRegistration implements ShouldQueue, ShouldBeUnique
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $timeout = 300; // 5 minutes
    public int $tries = 3;
    public int $backoff = 60; // 1 minute between retries

    public function __construct(
        private User $user,
        private array $additionalData = []
    ) {}

    public function handle(
        UserService $userService,
        NotificationService $notificationService
    ): void {
        try {
            // Send welcome email
            $notificationService->sendWelcomeEmail($this->user);
            
            // Create user profile with default settings
            $userService->createDefaultProfile($this->user, $this->additionalData);
            
            // Subscribe to default newsletter if opted in
            if ($this->additionalData['subscribe_newsletter'] ?? false) {
                $userService->subscribeToNewsletter($this->user);
            }
            
            // Generate and assign referral code
            $userService->generateReferralCode($this->user);
            
            // Log successful processing
            Log::info('User registration processed successfully', [
                'user_id' => $this->user->id,
                'processing_time' => microtime(true) - LARAVEL_START
            ]);
            
        } catch (Exception $e) {
            Log::error('User registration processing failed', [
                'user_id' => $this->user->id,
                'error' => $e->getMessage(),
                'attempt' => $this->attempts()
            ]);
            
            throw $e; // Re-throw to trigger retry mechanism
        }
    }

    public function failed(Throwable $exception): void
    {
        Log::critical('User registration processing permanently failed', [
            'user_id' => $this->user->id,
            'error' => $exception->getMessage(),
            'attempts' => $this->attempts()
        ]);
        
        // Notify administrators about the failure
        Mail::to(config('mail.admin_email'))
            ->queue(new JobFailedNotification($this->user, $exception));
    }

    public function uniqueId(): string
    {
        return 'user-registration-' . $this->user->id;
    }
}

// Dispatching jobs with proper error handling
class AuthController extends Controller
{
    public function register(
        CreateUserRequest $request,
        UserService $userService
    ) {
        DB::transaction(function () use ($request, $userService) {
            $user = $userService->createUser($request->validated());
            
            // Dispatch background processing
            ProcessUserRegistration::dispatch($user, [
                'subscribe_newsletter' => $request->boolean('subscribe_newsletter'),
                'referral_code' => $request->input('referral_code'),
                'source' => $request->input('source', 'web')
            ])->onQueue('user-processing');
            
            return response()->json([
                'message' => 'Registration successful! Welcome email will arrive shortly.',
                'user' => new UserResource($user)
            ], 201);
        });
    }
}

Security Best Practices

Mass Assignment Protection

Implement comprehensive mass assignment protection to prevent unauthorized data manipulation:


// Model with proper mass assignment protection
class User extends Authenticatable
{
    protected $fillable = [
        'name', 'email', 'password', 'phone', 'date_of_birth'
    ];
    
    protected $hidden = [
        'password', 'remember_token', 'two_factor_secret',
        'two_factor_recovery_codes', 'email_verification_token'
    ];
    
    protected $casts = [
        'email_verified_at' => 'datetime',
        'password' => 'hashed', // Laravel 11 automatic hashing
        'date_of_birth' => 'date',
        'is_active' => 'boolean',
        'last_login_at' => 'datetime',
        'two_factor_confirmed_at' => 'datetime'
    ];

    // Prevent certain attributes from being mass assigned
    protected $guarded = [
        'id', 'email_verified_at', 'created_at', 'updated_at',
        'is_admin', 'is_active', 'last_login_at'
    ];

    // Automatically generate UUID on creation
    protected static function boot()
    {
        parent::boot();
        
        static::creating(function ($user) {
            if (empty($user->uuid)) {
                $user->uuid = Str::uuid();
            }
        });
    }
}

// Request validation with security rules
class UpdateProfileRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name' => [
                'required',
                'string',
                'max:255',
                'regex:/^[a-zA-Z\s\-\.\']+$/' // Only letters, spaces, hyphens, dots, apostrophes
            ],
            'email' => [
                'required',
                'email:rfc,dns',
                'unique:users,email,' . $this->user()->id,
                'max:255'
            ],
            'phone' => [
                'nullable',
                'string',
                'regex:/^[\+]?[0-9\s\-\(\)]{10,}$/',
                'max:20'
            ],
            'bio' => [
                'nullable',
                'string',
                'max:1000'
            ]
        ];
    }

    protected function prepareForValidation()
    {
        // Sanitize input data
        $this->merge([
            'name' => trim(strip_tags($this->name ?? '')),
            'bio' => trim(strip_tags($this->bio ?? '', '<p><br><strong><em>')),
            'phone' => preg_replace('/[^0-9\+\-\(\)\s]/', '', $this->phone ?? '')
        ]);
    }
}

Input Sanitization and XSS Protection

Implement comprehensive input sanitization to protect against XSS attacks and ensure data integrity:


// Custom validation rules for enhanced security
class SecurityValidationRule
{
    public static function noScriptTags(): Closure
    {
        return function (string $attribute, mixed $value, Closure $fail) {
            if (preg_match('/)<[^<]*)*<\/script>/i', $value)) {
                $fail("The {$attribute} field contains potentially malicious script tags.");
            }
        };
    }

    public static function safeSql(): Closure
    {
        return function (string $attribute, mixed $value, Closure $fail) {
            $dangerousPatterns = [
                '/union[\s]+select/i',
                '/select[\s]+.*from/i',
                '/insert[\s]+into/i',
                '/delete[\s]+from/i',
                '/update[\s]+.*set/i',
                '/drop[\s]+table/i'
            ];

            foreach ($dangerousPatterns as $pattern) {
                if (preg_match($pattern, $value)) {
                    $fail("The {$attribute} field contains potentially dangerous content.");
                    break;
                }
            }
        };
    }
}

// HTML Purification service
class HtmlPurifierService
{
    private HTMLPurifier $purifier;

    public function __construct()
    {
        $config = HTMLPurifier_Config::createDefault();
        $config->set('Core.Encoding', 'UTF-8');
        $config->set('HTML.Doctype', 'XHTML 1.0 Transitional');
        $config->set('HTML.Allowed', 'p,br,strong,em,u,ol,ul,li,a[href],blockquote,h3,h4,h5');
        $config->set('HTML.AllowedAttributes', 'a.href');
        $config->set('URI.AllowedSchemes', ['http' => true, 'https' => true, 'mailto' => true]);
        
        $this->purifier = new HTMLPurifier($config);
    }

    public function clean(string $html): string
    {
        return $this->purifier->purify($html);
    }

    public function cleanArray(array $data, array $fields): array
    {
        foreach ($fields as $field) {
            if (isset($data[$field]) && is_string($data[$field])) {
                $data[$field] = $this->clean($data[$field]);
            }
        }
        
        return $data;
    }
}

// Usage in service class
class BlogPostService
{
    public function __construct(
        private HtmlPurifierService $purifier
    ) {}

    public function createPost(array $data): BlogPost
    {
        // Sanitize HTML content
        $data = $this->purifier->cleanArray($data, ['content', 'excerpt']);
        
        // Generate SEO-friendly slug
        $data['slug'] = $this->generateUniqueSlug($data['title']);
        
        return BlogPost::create($data);
    }

    private function generateUniqueSlug(string $title): string
    {
        $slug = Str::slug($title);
        $originalSlug = $slug;
        $counter = 1;

        while (BlogPost::where('slug', $slug)->exists()) {
            $slug = $originalSlug . '-' . $counter;
            $counter++;
        }

        return $slug;
    }
}

Testing Strategies

Comprehensive Feature Testing

Implement thorough feature tests to ensure your application works correctly from the user's perspective:


// tests/Feature/BlogPostTest.php
class BlogPostTest extends TestCase
{
    use RefreshDatabase;

    public function test_authenticated_user_can_create_blog_post(): void
    {
        $user = User::factory()->create();
        $category = Category::factory()->create();
        
        $postData = [
            'title' => 'My Amazing Blog Post',
            'content' => 'This is the content of my blog post with lots of interesting information.',
            'excerpt' => 'A brief excerpt of the post',
            'category_id' => $category->id,
            'is_published' => true,
        ];

        $response = $this->actingAs($user)
            ->postJson('/api/blog-posts', $postData);

        $response->assertStatus(201)
            ->assertJsonStructure([
                'data' => [
                    'id', 'title', 'content', 'excerpt', 
                    'slug', 'category', 'user', 'created_at'
                ]
            ])
            ->assertJson([
                'data' => [
                    'title' => 'My Amazing Blog Post',
                    'slug' => 'my-amazing-blog-post',
                    'is_published' => true
                ]
            ]);

        $this->assertDatabaseHas('blog_posts', [
            'title' => 'My Amazing Blog Post',
            'user_id' => $user->id,
            'category_id' => $category->id,
            'is_published' => true
        ]);
    }

    public function test_guest_cannot_create_blog_post(): void
    {
        $postData = [
            'title' => 'Unauthorized Post',
            'content' => 'This should not be created'
        ];

        $response = $this->postJson('/api/blog-posts', $postData);

        $response->assertStatus(401)
            ->assertJson(['message' => 'Unauthenticated.']);

        $this->assertDatabaseMissing('blog_posts', [
            'title' => 'Unauthorized Post'
        ]);
    }

    public function test_published_posts_are_visible_to_guests(): void
    {
        $user = User::factory()->create();
        $publishedPost = BlogPost::factory()->create([
            'user_id' => $user->id,
            'is_published' => true,
            'published_at' => now()->subHour()
        ]);

        $draftPost = BlogPost::factory()->create([
            'user_id' => $user->id,
            'is_published' => false
        ]);

        $response = $this->getJson('/api/blog-posts');

        $response->assertStatus(200)
            ->assertJsonFragment(['id' => $publishedPost->id])
            ->assertJsonMissing(['id' => $draftPost->id]);
    }

    public function test_blog_post_validation_rules(): void
    {
        $user = User::factory()->create();

        $invalidData = [
            'title' => '', // Required field
            'content' => 'Short', // Too short
            'category_id' => 999, // Non-existent category
        ];

        $response = $this->actingAs($user)
            ->postJson('/api/blog-posts', $invalidData);

        $response->assertStatus(422)
            ->assertJsonValidationErrors(['title', 'content', 'category_id']);
    }
}

Unit Testing for Business Logic

Create focused unit tests for your service classes and business logic to ensure individual components work correctly:


// tests/Unit/UserServiceTest.php
class UserServiceTest extends TestCase
{
    use RefreshDatabase;

    private UserService $userService;
    private NotificationService $notificationService;

    protected function setUp(): void
    {
        parent::setUp();
        
        $this->notificationService = Mockery::mock(NotificationService::class);
        $this->app->instance(NotificationService::class, $this->notificationService);
        
        $this->userService = app(UserService::class);
    }

    public function test_create_user_returns_user_instance_with_hashed_password(): void
    {
        $this->notificationService
            ->shouldReceive('sendWelcomeEmail')
            ->once()
            ->andReturn(true);

        $userData = [
            'name' => 'John Doe',
            'email' => 'john@example.com',
            'password' => 'SecurePassword123!',
        ];

        $user = $this->userService->createUser($userData);

        $this->assertInstanceOf(User::class, $user);
        $this->assertEquals('John Doe', $user->name);
        $this->assertEquals('john@example.com', $user->email);
        $this->assertTrue(Hash::check('SecurePassword123!', $user->password));
        $this->assertDatabaseHas('users', [
            'name' => 'John Doe',
            'email' => 'john@example.com'
        ]);
    }

    public function test_create_user_generates_unique_uuid(): void
    {
        $this->notificationService
            ->shouldReceive('sendWelcomeEmail')
            ->twice()
            ->andReturn(true);

        $userData1 = [
            'name' => 'User One',
            'email' => 'user1@example.com',
            'password' => 'password123',
        ];

        $userData2 = [
            'name' => 'User Two', 
            'email' => 'user2@example.com',
            'password' => 'password123',
        ];

        $user1 = $this->userService->createUser($userData1);
        $user2 = $this->userService->createUser($userData2);

        $this->assertNotNull($user1->uuid);
        $this->assertNotNull($user2->uuid);
        $this->assertNotEquals($user1->uuid, $user2->uuid);
    }

    public function test_update_user_profile_triggers_event(): void
    {
        Event::fake();
        
        $user = User::factory()->create();
        $profileData = [
            'name' => 'Updated Name',
            'bio' => 'Updated bio information'
        ];

        $updatedUser = $this->userService->updateUserProfile($user, $profileData);

        $this->assertEquals('Updated Name', $updatedUser->name);
        $this->assertEquals('Updated bio information', $updatedUser->bio);
        
        Event::assertDispatched(UserProfileUpdated::class, function ($event) use ($user) {
            return $event->user->id === $user->id;
        });
    }

    protected function tearDown(): void
    {
        Mockery::close();
        parent::tearDown();
    }
}

// tests/Unit/BlogPostServiceTest.php
class BlogPostServiceTest extends TestCase
{
    use RefreshDatabase;

    private BlogPostService $blogService;
    private HtmlPurifierService $purifierService;

    protected function setUp(): void
    {
        parent::setUp();
        
        $this->purifierService = Mockery::mock(HtmlPurifierService::class);
        $this->app->instance(HtmlPurifierService::class, $this->purifierService);
        
        $this->blogService = app(BlogPostService::class);
    }

    public function test_create_post_sanitizes_html_content(): void
    {
        $user = User::factory()->create();
        $category = Category::factory()->create();

        $unsafeContent = '<script>alert("xss")</script><p>Safe content</p>';
    $safeContent = '<p>Safe content</p>';

    $this->purifierService
        ->shouldReceive('cleanArray')
        ->once()
        ->with(Mockery::type('array'), ['content', 'excerpt'])
        ->andReturn([
            'title' => 'Test Post',
            'content' => $safeContent,
            'excerpt' => 'Safe excerpt',
            'user_id' => $user->id,
            'category_id' => $category->id
        ]);

    $postData = [
        'title' => 'Test Post',
        'content' => $unsafeContent,
        'excerpt' => 'Safe excerpt',
        'user_id' => $user->id,
        'category_id' => $category->id
    ];

    $post = $this->blogService->createPost($postData);

    $this->assertEquals($safeContent, $post->content);
    $this->assertEquals('test-post', $post->slug);
}

public function test_generate_unique_slug_handles_duplicates(): void
{
    $user = User::factory()->create();
    $category = Category::factory()->create();

    // Create existing post with same title
    BlogPost::factory()->create([
        'title' => 'Duplicate Title',
        'slug' => 'duplicate-title'
    ]);

    $this->purifierService
        ->shouldReceive('cleanArray')
        ->once()
        ->andReturnUsing(function ($data) { return $data; });

    $postData = [
        'title' => 'Duplicate Title',
        'content' => 'New content',
        'user_id' => $user->id,
        'category_id' => $category->id
    ];

    $post = $this->blogService->createPost($postData);

    $this->assertEquals('duplicate-title-1', $post->slug);
}
}

Deployment and DevOps

Production Optimization Commands

Prepare your Laravel application for production with these essential optimization commands that significantly improve performance:


// Production deployment script (deploy.sh)
#!/bin/bash

echo "Starting Laravel application deployment..."

# Pull latest changes
git pull origin main

# Install/update Composer dependencies
composer install --optimize-autoloader --no-dev

# Clear and cache configurations
php artisan config:clear
php artisan config:cache

# Clear and cache routes
php artisan route:clear
php artisan route:cache

# Clear and cache views
php artisan view:clear
php artisan view:cache

# Cache events
php artisan event:cache

# Run database migrations (with backup)
php artisan migrate --force

# Clear application cache
php artisan cache:clear

# Restart queue workers
php artisan queue:restart

# Optimize application
php artisan optimize

# Set proper permissions
chown -R www-data:www-data storage bootstrap/cache
chmod -R 775 storage bootstrap/cache

echo "Deployment completed successfully!"

Environment Configuration Best Practices

Proper environment configuration is crucial for security and performance in production environments:


// .env.production example with security considerations
APP_NAME="Your Laravel App"
APP_ENV=production
APP_KEY=base64:your-32-character-secret-key-here
APP_DEBUG=false  # Never true in production
APP_URL=https://yourdomain.com

LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=warning  # Reduce log verbosity in production

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your_production_db
DB_USERNAME=your_db_user
DB_PASSWORD=your-secure-db-password

BROADCAST_DRIVER=redis
CACHE_DRIVER=redis
FILESYSTEM_DISK=s3  # Use cloud storage in production
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis
SESSION_LIFETIME=120

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=your-redis-password
REDIS_PORT=6379

MAIL_MAILER=smtp
MAIL_HOST=your-smtp-host
MAIL_PORT=587
MAIL_USERNAME=your-email@domain.com
MAIL_PASSWORD=your-email-password
MAIL_ENCRYPTION=tls

AWS_ACCESS_KEY_ID=your-aws-access-key
AWS_SECRET_ACCESS_KEY=your-aws-secret-key
AWS_DEFAULT_REGION=us-west-2
AWS_BUCKET=your-s3-bucket

# Security headers and HTTPS enforcement
FORCE_HTTPS=true
SECURE_COOKIES=true

# Rate limiting
THROTTLE_REQUESTS=60
THROTTLE_DECAY_MINUTES=1

// config/session.php - Production session configuration
return [
    'driver' => env('SESSION_DRIVER', 'redis'),
    'lifetime' => env('SESSION_LIFETIME', 120),
    'expire_on_close' => false,
    'encrypt' => true,
    'files' => storage_path('framework/sessions'),
    'connection' => env('SESSION_CONNECTION'),
    'table' => 'sessions',
    'store' => env('SESSION_STORE'),
    'lottery' => [2, 100],
    'cookie' => env(
        'SESSION_COOKIE',
        Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
    ),
    'path' => '/',
    'domain' => env('SESSION_DOMAIN'),
    'secure' => env('SESSION_SECURE_COOKIE', true), // Force HTTPS
    'http_only' => true,
    'same_site' => 'lax',
];

Monitoring and Health Checks

Implement comprehensive monitoring to ensure your application runs smoothly in production:


// app/Http/Controllers/HealthCheckController.php
class HealthCheckController extends Controller
{
    public function check()
    {
        $checks = [
            'database' => $this->checkDatabase(),
            'cache' => $this->checkCache(),
            'storage' => $this->checkStorage(),
            'queue' => $this->checkQueue(),
            'external_apis' => $this->checkExternalApis()
        ];

        $allHealthy = collect($checks)->every(fn($check) => $check['status'] === 'healthy');

        return response()->json([
            'status' => $allHealthy ? 'healthy' : 'unhealthy',
            'timestamp' => now()->toISOString(),
            'checks' => $checks,
            'version' => config('app.version'),
            'environment' => app()->environment()
        ], $allHealthy ? 200 : 503);
    }

    private function checkDatabase(): array
    {
        try {
            DB::select('SELECT 1');
            $latency = $this->measureLatency(fn() => DB::select('SELECT 1'));
            
            return [
                'status' => 'healthy',
                'message' => 'Database connection successful',
                'latency_ms' => $latency
            ];
        } catch (Exception $e) {
            return [
                'status' => 'unhealthy',
                'message' => 'Database connection failed: ' . $e->getMessage()
            ];
        }
    }

    private function checkCache(): array
    {
        try {
            Cache::put('health_check', 'test', 10);
            $value = Cache::get('health_check');
            Cache::forget('health_check');
            
            return [
                'status' => $value === 'test' ? 'healthy' : 'unhealthy',
                'message' => $value === 'test' ? 'Cache working correctly' : 'Cache read/write failed'
            ];
        } catch (Exception $e) {
            return [
                'status' => 'unhealthy',
                'message' => 'Cache error: ' . $e->getMessage()
            ];
        }
    }

    private function checkStorage(): array
    {
        try {
            $testFile = 'health-check-' . Str::random(10) . '.txt';
            Storage::put($testFile, 'health check content');
            $content = Storage::get($testFile);
            Storage::delete($testFile);
            
            return [
                'status' => 'healthy',
                'message' => 'File storage working correctly',
                'disk' => config('filesystems.default')
            ];
        } catch (Exception $e) {
            return [
                'status' => 'unhealthy',
                'message' => 'Storage error: ' . $e->getMessage()
            ];
        }
    }

    private function checkQueue(): array
    {
        try {
            $queueSize = Queue::size();
            $failedJobs = DB::table('failed_jobs')->count();
            
            return [
                'status' => 'healthy',
                'message' => 'Queue system operational',
                'pending_jobs' => $queueSize,
                'failed_jobs' => $failedJobs
            ];
        } catch (Exception $e) {
            return [
                'status' => 'unhealthy',
                'message' => 'Queue error: ' . $e->getMessage()
            ];
        }
    }

    private function checkExternalApis(): array
    {
        // Check critical external services
        $services = [
            'payment_gateway' => config('services.stripe.key') ? $this->checkStripe() : null,
            'email_service' => $this->checkEmailService(),
        ];

        $services = array_filter($services);
        $allHealthy = collect($services)->every(fn($service) => $service['status'] === 'healthy');

        return [
            'status' => $allHealthy ? 'healthy' : 'degraded',
            'services' => $services
        ];
    }

    private function measureLatency(callable $callback): float
    {
        $start = microtime(true);
        $callback();
        return round((microtime(true) - $start) * 1000, 2);
    }
}

// Custom logging for application monitoring
// app/Providers/AppServiceProvider.php
class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        // Log slow queries in production
        if (app()->isProduction()) {
            DB::listen(function ($query) {
                if ($query->time > 1000) { // Log queries taking more than 1 second
                    Log::warning('Slow query detected', [
                        'sql' => $query->sql,
                        'time' => $query->time,
                        'bindings' => $query->bindings
                    ]);
                }
            });
        }

        // Monitor memory usage
        $this->monitorMemoryUsage();
    }

    private function monitorMemoryUsage()
    {
        register_shutdown_function(function () {
            $memoryUsage = memory_get_peak_usage(true);
            $memoryLimit = $this->convertToBytes(ini_get('memory_limit'));
            $memoryPercentage = ($memoryUsage / $memoryLimit) * 100;

            if ($memoryPercentage > 80) {
                Log::warning('High memory usage detected', [
                    'memory_used' => $this->formatBytes($memoryUsage),
                    'memory_limit' => ini_get('memory_limit'),
                    'percentage' => round($memoryPercentage, 2) . '%',
                    'request_uri' => request()->getRequestUri()
                ]);
            }
        });
    }

    private function convertToBytes(string $value): int
    {
        $value = trim($value);
        $unit = strtolower($value[strlen($value) - 1]);
        $numValue = (int) substr($value, 0, -1);

        return match ($unit) {
            'g' => $numValue * 1024 * 1024 * 1024,
            'm' => $numValue * 1024 * 1024,
            'k' => $numValue * 1024,
            default => (int) $value,
        };
    }

    private function formatBytes(int $bytes): string
    {
        $units = ['B', 'KB', 'MB', 'GB'];
        $unitIndex = 0;
        
        while ($bytes >= 1024 && $unitIndex < count($units) - 1) {
            $bytes /= 1024;
            $unitIndex++;
        }
        
        return round($bytes, 2) . ' ' . $units[$unitIndex];
    }
}

Conclusion

Laravel 11 represents a significant leap forward in modern PHP development, offering developers powerful tools, improved performance, and streamlined workflows that make building robust web applications more efficient than ever. By following the best practices outlined in this comprehensive guide, you'll be well-equipped to create secure, scalable, and maintainable Laravel applications that meet modern web development standards.

The key to mastering Laravel lies in consistent practice, staying updated with the latest features and community best practices, and understanding that good code is not just about functionality—it's about creating systems that are secure, performant, and easy to maintain over time. Whether you're building a simple blog, a complex e-commerce platform, or an enterprise-level application, these practices will serve as a solid foundation for your Laravel development journey.

Remember that Laravel's strength comes not just from its features, but from its vibrant community and extensive ecosystem. Don't hesitate to leverage community packages, contribute to open-source projects, and engage with other developers. The Laravel community is incredibly supportive and always willing to help fellow developers grow and succeed.

As you continue your Laravel journey, keep experimenting with new features, stay curious about emerging patterns, and always prioritize writing clean, testable code. With Laravel 11 and these modern development practices, you're well-positioned to build the next generation of web applications.

Copyright 2025. All rights are reserved