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.
Table of Contents
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('/
-->