๐๏ธ 26. Namespaces
Namespace = way to group related classes to avoid name collisions
โข
App\Models\User (database model)
โข
App\Http\Resources\User (API resource)
โข Both named "User" but in different namespaces!
1. Full path:
$user = new \App\Models\User();
2. Import with use:
use App\Models\User; $user = new User();
3. Alias:
use App\Models\User as UserModel;
namespace App\Http\Controllers;
use App\Models\User; // Import User model
use Illuminate\Http\Request; // Import Request class
class UserController {
public function index() {
$users = User::all(); // No need for full path
return view('users', compact('users'));
}
}
App\Http\Controllers\UserController lives in app/Http/Controllers/UserController.php
Use: Organize code, avoid conflicts, PSR-4 autoloading, better IDE support
๐ 1. Dependency Injection (DI)
DI = passing dependencies into class instead of creating them inside
Flexibility: Easy to swap implementations (dev vs production)
Loose Coupling: Classes don't depend on concrete implementations
Single Responsibility: Class focuses on its job, not creating dependencies
1. Sees
UserService $userService in constructor
2. Creates or finds UserService instance
3. Injects it when creating the controller
4. Works recursively for nested dependencies!
// Bad: Creating dependencies inside
class UserController {
public function __construct() {
$this->userService = new UserService(); // โ Hard to test
$this->emailService = new EmailService(); // โ Tight coupling
}
}
// Good: Dependencies injected
class UserController {
public function __construct(
UserService $userService, // โ
Laravel injects automatically
EmailService $emailService // โ
Easy to mock in tests
) {
$this->userService = $userService;
$this->emailService = $emailService;
}
}
$mockService = Mockery::mock(UserService::class);
Use: When class depends on other services, for testability, loose coupling
2. ORM vs Raw SQL
ORM (Eloquent) = PHP objects for DB queries
Raw SQL = direct SQL queries
โข Standard CRUD operations
โข Working with relationships
โข Rapid development needed
โข Team has mixed SQL skills
Use Raw SQL when:
โข Complex reporting queries
โข Performance is critical
โข Using database-specific features
โข Bulk operations
$users = User::all(); // 1 query
foreach($users as $user) { echo $user->profile->name; } // N queries
Solution: Eager loading:
User::with('profile')->get();
// ORM - Easy and readable
$users = User::where('active', 1)
->where('created_at', '>', now()->subDays(30))
->with('profile', 'orders')
->orderBy('name')
->get();
// Raw SQL - More control and potentially faster
$users = DB::select("
SELECT u.*, p.avatar, COUNT(o.id) as order_count
FROM users u
LEFT JOIN profiles p ON u.id = p.user_id
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.active = 1 AND u.created_at > ?
GROUP BY u.id
ORDER BY u.name
", [now()->subDays(30)]);
3. Redis Caching
Redis = in-memory fast key-value store with advanced features
Simple Operations: Key-value lookups vs complex SQL queries
No Disk I/O: No reading from/writing to disk
Optimized: Built specifically for caching and speed
Lists: Ordered collections (queues, recent items)
Sets: Unique values (tags, categories)
Hashes: Field-value pairs (user profiles)
Sorted Sets: Ordered by score (leaderboards)
Streams: Event logs and messaging
โข High-traffic applications
โข Real-time features (chat, live updates)
โข Session management across multiple servers
โข Queue processing
โข Complex caching scenarios
Use File/DB cache when:
โข Simple applications
โข Limited server resources
โข Single server setup
// Basic Redis operations in Laravel
Cache::put('user:1', $user, 3600); // Store for 1 hour
$user = Cache::get('user:1'); // Retrieve
// Advanced: Using Redis directly
Redis::set('views:page:1', 100);
Redis::incr('views:page:1'); // Atomic increment
$views = Redis::get('views:page:1');
// Lists for queues
Redis::lpush('queue:emails', json_encode($emailData));
$job = Redis::rpop('queue:emails');
// Sets for unique tracking
Redis::sadd('online:users', $userId);
$onlineUsers = Redis::smembers('online:users');
// Hashes for complex objects
Redis::hmset('user:1', [
'name' => 'John',
'email' => 'john@example.com',
'last_seen' => now()
]);
// Expiration
Redis::expire('temp:data', 300); // Expire in 5 minutes
CACHE_DRIVER=redis and SESSION_DRIVER=redis in .env for optimal performance!
Use: Session storage, queues, real-time data, high-traffic caching, leaderboards
4. JWT vs Passport vs OAuth
JWT = lightweight, stateless tokens
Passport = Laravel's full OAuth2 server implementation
OAuth = authorization protocol for 3rd-party access
2. Storage: Client stores JWT (localStorage, cookie)
3. Requests: Client sends JWT in Authorization header
4. Verification: Server verifies signature without database lookup
Structure: header.payload.signature (Base64 encoded)
โข Building simple APIs or SPAs
โข Need stateless authentication
โข Microservices architecture
โข Mobile app authentication
Choose Passport when:
โข Need OAuth2 server functionality
โข Multiple client applications
โข Need refresh tokens and scopes
โข Want built-in token management
Choose OAuth when:
โข Social media login (Google, Facebook)
โข Third-party API integration
โข Don't want to handle passwords
โข Store securely (httpOnly cookies, not localStorage)
โข Use short expiration times
โข Can't revoke until expiry
โข Payload is readable (don't store sensitive data)
Passport Security:
โข Tokens stored in database (can revoke)
โข Supports refresh tokens
โข Built-in rate limiting
โข Scopes for permission control
// JWT with tymon/jwt-auth
// Login
public function login(Request $request) {
$credentials = $request->only('email', 'password');
if (!$token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return response()->json(['token' => $token]);
}
// Protected route
public function getUser() {
return response()->json(JWTAuth::parseToken()->authenticate());
}
// Laravel Passport
// Install: php artisan passport:install
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
// OAuth client credentials
$client = Http::withHeaders([
'Authorization' => 'Bearer ' . $accessToken,
])->get('https://api.github.com/user');
// Passport scopes
Route::get('/orders', function () {
// Access token has both "check-status" and "place-orders" scopes
})->middleware(['auth:api', 'scope:check-status,place-orders']);
Use: API authentication, SPA auth, mobile apps, third-party integrations
๐ 5. EXPLAIN Command
EXPLAIN = SQL command to analyze query execution plan and performance
Index Usage: Which indexes are being used (or not used)
Join Methods: How tables are being joined together
Row Estimates: How many rows will be examined
Cost Analysis: Relative expense of different operations
Scan Types: Full table scan vs index scan vs unique scan
DB::select('EXPLAIN SELECT * FROM users WHERE email = ?', ['john@example.com'])Query Builder:
DB::table('users')->where('email', 'john@example.com')->explain()Eloquent:
User::where('email', 'john@example.com')->explain()Advanced:
EXPLAIN ANALYZE for actual execution statistics
โข Full Table Scan: Examining every row in a large table
โข High Row Count: Millions of rows being examined
โข No Index Usage: Missing or unused indexes
โข Cartesian Products: Unintended cross joins
โ Good Signs:
โข Index seeks instead of scans
โข Low row counts
โข Efficient join algorithms
โข Constant time lookups
// Laravel EXPLAIN examples
// Basic EXPLAIN
$explain = DB::table('users')
->where('email', 'john@example.com')
->explain();
dd($explain);
// EXPLAIN with Eloquent relationship
$explain = User::with('posts')
->where('active', 1)
->explain();
// Raw EXPLAIN for complex queries
$results = DB::select("
EXPLAIN ANALYZE
SELECT u.name, COUNT(p.id) as post_count
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
WHERE u.created_at > '2023-01-01'
GROUP BY u.id
HAVING post_count > 5
");
// MySQL specific - EXPLAIN FORMAT=JSON
$json_explain = DB::select("
EXPLAIN FORMAT=JSON
SELECT * FROM users
WHERE email = 'john@example.com'
");
// Performance monitoring in Laravel
if (config('app.debug')) {
DB::listen(function ($query) {
if ($query->time > 1000) { // Queries taking > 1 second
Log::warning('Slow query detected', [
'sql' => $query->sql,
'bindings' => $query->bindings,
'time' => $query->time
]);
}
});
}
Use: Query optimization, index tuning, performance debugging, finding bottlenecks
๐ฅ 6. N+1 Problem
N+1 = loading relations in loop causes many queries
โข Laravel Debugbar - shows all queries
โข Telescope - Laravel's debugging assistant
โข
DB::enableQueryLog() - log all queries
โข Look for loops accessing relationships without eager loading
User::with('profile')->get()
2. Lazy Eager Loading:
$users->load('profile')
3. Constraining Eager Loads:
User::with(['posts' => fn($q) => $q->published()])
4. Prevent Lazy Loading:
Model::preventLazyLoading() in AppServiceProvider
// โ N+1 Problem - Makes 1 + N queries
$users = User::all(); // 1 query
foreach($users as $user) {
echo $user->profile->name; // N queries (one per user)
echo $user->posts->count(); // N more queries!
}
// โ
Solution 1: Eager Loading - Makes only 3 queries
$users = User::with(['profile', 'posts'])->get(); // 3 queries total
foreach($users as $user) {
echo $user->profile->name; // No additional query
echo $user->posts->count(); // No additional query
}
// โ
Solution 2: Lazy Eager Loading - Load after fetching
$users = User::all();
$users->load(['profile', 'posts']); // Loads relationships for all users
// โ
Solution 3: Constrained Eager Loading
$users = User::with([
'posts' => function($query) {
$query->where('published', true)->latest()->take(5);
}
])->get();
Model::preventLazyLoading(!app()->isProduction()) in AppServiceProvider to catch N+1 issues during development!
Use: Whenever loading models with relationships, always consider eager loading
๐จ 8. Design Patterns in Laravel
Design Patterns = proven solutions to common programming problems
๐ญ Factory: Create objects (Model Factories, View Factory)
๐ Observer: React to events (Model Events, Listeners)
๐ฆ Repository: Data access abstraction layer
๐ญ Facade: Simple interface to complex subsystems
๐ก๏ธ Strategy: Swap algorithms (Auth Guards, Cache Drivers)
๐ฏ Decorator: Add behavior (Middleware)
๐ Command: Encapsulate requests (Artisan Commands)
๐ Chain of Responsibility: Middleware pipeline
โข You want to separate business logic from data access
โข You need to swap data sources (MySQL โ MongoDB)
โข You want to write testable code with mock data
โข You're working with complex queries
โข You follow Domain-Driven Design (DDD)
Benefits: Testability, maintainability, separation of concerns
Event Listeners: Classes that respond to specific events
Use Cases: Send emails, update caches, log activities, sync data
Benefits: Decoupled code, automatic triggers, clean separation
// Repository Pattern Example
interface UserRepositoryInterface {
public function find($id);
public function create(array $data);
public function getActiveUsers();
}
class EloquentUserRepository implements UserRepositoryInterface {
public function find($id) {
return User::find($id);
}
public function create(array $data) {
return User::create($data);
}
public function getActiveUsers() {
return User::where('active', true)->get();
}
}
// Bind in ServiceProvider
$this->app->bind(UserRepositoryInterface::class, EloquentUserRepository::class);
// Observer Pattern - Model Events
class User extends Model {
protected static function boot() {
parent::boot();
static::creating(function ($user) {
$user->uuid = Str::uuid();
});
static::created(function ($user) {
event(new UserRegistered($user));
});
}
}
// Factory Pattern - Model Factory
class UserFactory extends Factory {
public function definition() {
return [
'name' => $this->faker->name(),
'email' => $this->faker->unique()->safeEmail(),
'password' => Hash::make('password'),
];
}
public function admin() {
return $this->state(['role' => 'admin']);
}
}
// Strategy Pattern - Cache Drivers
// Laravel swaps cache implementations based on config
Cache::put('key', 'value', 60); // Could use Redis, File, Database, etc.
// Facade Pattern - Simple interface
Cache::get('key'); // Instead of app('cache')->get('key')
Mail::send($view, $data); // Instead of app('mailer')->send($view, $data)
Use: Clean architecture, testable code, maintainability, separation of concerns
9. Traits vs Facades
Traits = PHP mechanism to share code between classes, Facades = Laravel's static interface to services
โข You want to share methods between multiple classes
โข You need to add behavior to models (like soft deletes)
โข You want to avoid inheritance issues
โข You're building reusable functionality
Use Facades when:
โข You want clean, expressive syntax
โข You're accessing Laravel services
โข You need static-like interface
โข You want IDE autocompletion
Cache::get('key')
2. __callStatic magic method: Intercepts the call
3. Service resolution: Resolves 'cache' from container
4. Method forwarding: Calls get() on actual cache instance
Benefits: Clean syntax + dependency injection + testability
Traits Cons: Can cause naming conflicts, harder to test
Facade Pros: Clean syntax, testable, IDE support
Facade Cons: Hides dependencies, Laravel-specific
// Traits Example
trait Timestamps {
public function touch() {
$this->updated_at = now();
$this->save();
}
public function formatDate($field) {
return $this->$field->format('Y-m-d H:i:s');
}
}
class User extends Model {
use Timestamps; // Now has touch() and formatDate() methods
}
class Post extends Model {
use Timestamps; // Same methods available here too
}
// Using the trait
$user = User::find(1);
$user->touch(); // Updates updated_at
echo $user->formatDate('created_at');
// Facades Example - Static-like syntax
Cache::put('users', $users, 3600);
$users = Cache::get('users');
Mail::to($user)->send(new WelcomeMail());
DB::table('users')->where('active', 1)->count();
// Behind the scenes, this is happening:
app('cache')->put('users', $users, 3600);
app('cache')->get('users');
app('mailer')->to($user)->send(new WelcomeMail());
// Creating custom Facade
class CustomCache extends Facade {
protected static function getFacadeAccessor() {
return 'custom-cache'; // Service container binding
}
}
// Usage
CustomCache::store('data', $value);
// Trait with multiple inheritance issues
trait A { public function test() { return 'A'; } }
trait B { public function test() { return 'B'; } }
class MyClass {
use A, B {
B::test insteadof A; // Resolve conflict
A::test as testA; // Alias the other
}
}
Cache::shouldReceive('get') to mock Facade calls in tests, or inject real dependencies for more explicit testing!
Use: Code reuse (Traits), clean service access (Facades), API design, testing
โณ 10. Queuing System
Queues = run slow tasks asynchronously in background workers
dispatch(new ProcessOrder($order))2. Queue Storage: Job stored in database, Redis, SQS, or other drivers
3. Queue Worker:
php artisan queue:work continuously processes jobs4. Job Execution: Worker picks up job and runs the
handle() method5. Completion: Job marked as completed or failed
Redis: Fast, feature-rich, supports job priorities and delays
Amazon SQS: Managed service, highly scalable, good for AWS infrastructure
Sync: Runs jobs immediately (for testing/development)
Beanstalkd: Lightweight, good performance, less popular
Recommendation: Redis for most applications, SQS for AWS deployments
$tries property on job classRetry Delay: Use
retryAfter() method for backoff strategyFailed Jobs Table:
php artisan queue:failed-table to track failuresFailed Job Handler: Implement
failed() method for cleanupManual Retry:
php artisan queue:retry all or specific job ID
Queue: A storage mechanism that holds jobs waiting to be processed
Worker: A long-running process that picks up and executes jobs
Queue Name: Logical separation of jobs (emails, reports, critical)
Connection: The queue driver/service being used (Redis, database, SQS)
// Creating a Job
php artisan make:job ProcessOrderPayment
// app/Jobs/ProcessOrderPayment.php
class ProcessOrderPayment implements ShouldQueue {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tries = 3; // Retry 3 times on failure
public $backoff = [10, 30, 60]; // Wait 10s, then 30s, then 60s
public $timeout = 120; // Job timeout after 2 minutes
public function __construct(
public Order $order,
public PaymentMethod $paymentMethod
) {}
public function handle(PaymentService $paymentService) {
try {
$result = $paymentService->charge(
$this->order->total,
$this->paymentMethod
);
$this->order->update(['status' => 'paid', 'payment_id' => $result->id]);
// Dispatch follow-up jobs
dispatch(new SendOrderConfirmation($this->order));
dispatch(new UpdateInventory($this->order));
} catch (PaymentException $e) {
// This will trigger a retry
throw $e;
}
}
public function failed(\Throwable $exception) {
// Cleanup on permanent failure
$this->order->update(['status' => 'payment_failed']);
// Notify admin
dispatch(new NotifyAdminPaymentFailure($this->order, $exception));
}
}
// Dispatching Jobs
// Immediate dispatch
ProcessOrderPayment::dispatch($order, $paymentMethod);
// Delayed dispatch
ProcessOrderPayment::dispatch($order, $paymentMethod)
->delay(now()->addMinutes(5));
// Specific queue
ProcessOrderPayment::dispatch($order, $paymentMethod)
->onQueue('payments');
// Chain jobs (one after another)
Bus::chain([
new ProcessOrderPayment($order, $paymentMethod),
new SendOrderConfirmation($order),
new UpdateInventory($order),
])->dispatch();
// Batch jobs (run in parallel, track completion)
Bus::batch([
new ProcessPayment($order1),
new ProcessPayment($order2),
new ProcessPayment($order3),
])->then(function (Batch $batch) {
// All jobs completed successfully
dispatch(new GenerateDailyReport());
})->catch(function (Batch $batch, \Throwable $e) {
// First batch job failure
})->finally(function (Batch $batch) {
// Always executed
})->dispatch();
// Queue Workers
php artisan queue:work // Process default queue
php artisan queue:work --queue=critical,emails // Specific queues
php artisan queue:work --tries=3 // Override retry attempts
php artisan queue:work --timeout=300 // Job timeout
php artisan queue:work --memory=512 // Memory limit
// Monitoring Commands
php artisan queue:monitor redis:default,redis:emails --max=100
php artisan queue:failed // List failed jobs
php artisan queue:retry all // Retry all failed jobs
php artisan queue:flush // Delete all failed jobs
// Supervisor Configuration (Production)
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /path/to/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=8
redirect_stderr=true
stdout_logfile=/path/to/worker.log
stopwaitsecs=3600
Use: Email sending, file processing, API calls, image resizing, report generation, payment processing
11. Middleware
Middleware = filters that run before and after HTTP requests
โข
auth - Check if user is authenticated
โข
guest - Ensure user is NOT authenticated
โข
throttle - Rate limiting
โข
verified - Email verification required
โข
can - Authorization policies
โข
csrf - CSRF token validation
โข
cors - Cross-Origin Resource Sharing
php artisan make:middleware CheckAge
2. Implement: Add logic in
handle() method
3. Register: Add to
app/Http/Kernel.php
4. Use: Apply to routes or route groups
Before vs After: Run logic before request hits controller or after response is generated
Route Middleware: Applied to specific routes
Group Middleware: Applied to route groups (like admin routes)
Controller Middleware: Applied in controller constructor
// Creating custom middleware
php artisan make:middleware CheckAge
// app/Http/Middleware/CheckAge.php
public function handle($request, Closure $next) {
if ($request->age <= 200) {
return redirect('home');
}
return $next($request); // Continue to next middleware/controller
}
// Register in app/Http/Kernel.php
protected $routeMiddleware = [
'age' => \App\Http\Middleware\CheckAge::class,
];
// Using middleware
Route::get('admin/profile', function () {
//
})->middleware('age');
// Multiple middleware
Route::get('admin/users', function () {
//
})->middleware(['auth', 'verified', 'age']);
// Middleware with parameters
Route::get('admin/settings', function () {
//
})->middleware('throttle:60,1'); // 60 requests per minute
// Group middleware
Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/dashboard', function () {
//
});
Route::get('/profile', function () {
//
});
});
// Controller middleware
class UserController extends Controller {
public function __construct() {
$this->middleware('auth');
$this->middleware('verified')->except('show');
}
}
// Middleware that runs after response
public function handle($request, Closure $next) {
$response = $next($request);
// Log response time after request is processed
Log::info('Response time: ' . (microtime(true) - LARAVEL_START));
return $response;
}
Use: Authentication, authorization, rate limiting, logging, CORS, input validation
๐ 12. Request Lifecycle
Request Lifecycle = complete journey from HTTP request to response
public/index.php2. Autoloader: Composer autoloader loads all classes
3. Application Bootstrap: Creates Laravel application instance
4. HTTP Kernel:
app/Http/Kernel.php handles the request5. Service Providers: Register and boot application services
6. Global Middleware: Run middleware that applies to all routes
7. Route Matching: Find matching route for the request
8. Route Middleware: Execute route-specific middleware
9. Controller/Closure: Execute the route handler
10. Response: Generate and send HTTP response
register() methods calledBooting Phase: All providers'
boot() methods calledDeferred Providers: Only loaded when their services are needed
Order Matters: Providers in
config/app.php array orderKey Providers: EventServiceProvider, RouteServiceProvider, AuthServiceProvider
Middleware Pipeline: Request passes through middleware stack
Route Dispatching: Router finds and executes matching route
Response Generation: Controller returns response object
Middleware Response: Response passes back through middleware
Terminate: Cleanup operations after response sent
Method Matching: Filter routes by HTTP method (GET, POST, etc.)
URI Matching: Match request URI against route patterns
Parameter Binding: Extract route parameters and model binding
Controller Resolution: Instantiate controller with dependencies
Method Injection: Inject dependencies into controller method
// public/index.php - Entry Point
require_once __DIR__.'/../vendor/autoload.php';
$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
$response->send();
$kernel->terminate($request, $response);
// app/Http/Kernel.php - HTTP Kernel
class Kernel extends HttpKernel {
// Global middleware - runs on EVERY request
protected $middleware = [
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
// Route middleware groups
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
}
// Service Provider Example
class CustomServiceProvider extends ServiceProvider {
// Called during registration phase
public function register() {
$this->app->singleton(PaymentService::class, function ($app) {
return new PaymentService($app['config']['payment.key']);
});
}
// Called after all providers registered
public function boot() {
// Database is available here
if ($this->app->environment('production')) {
URL::forceScheme('https');
}
// Custom validation rules
Validator::extend('phone', function ($attribute, $value, $parameters, $validator) {
return preg_match('/^[\+]?[1-9][\d]{0,15}$/', $value);
});
}
}
// Middleware Pipeline Example
class CheckAge {
public function handle($request, Closure $next) {
// Before: Runs before the controller
if ($request->age <= 18) {
return redirect('home');
}
$response = $next($request); // Pass to next middleware/controller
// After: Runs after the controller
$response->headers->set('X-Age-Verified', 'true');
return $response;
}
public function terminate($request, $response) {
// Cleanup after response sent to browser
Log::info('Request completed', [
'url' => $request->url(),
'time' => microtime(true) - LARAVEL_START
]);
}
}
// Route Model Binding
Route::get('/users/{user}', function (User $user) {
// Laravel automatically finds User::find($id)
return $user;
});
// Custom Route Model Binding
public function boot() {
Route::bind('user', function ($value) {
return User::where('slug', $value)->firstOrFail();
});
}
// Controller with Dependency Injection
class UserController extends Controller {
public function __construct(
private UserService $userService,
private LoggerInterface $logger
) {}
public function show(User $user, Request $request) {
// $user automatically injected via route model binding
// $request injected via method injection
$this->logger->info('User profile viewed', ['user_id' => $user->id]);
return view('users.show', compact('user'));
}
}
php artisan route:cache and php artisan config:cache in production to speed up the bootstrap process!
Use: Understanding for debugging, performance optimization, middleware development, custom providers
13. Sanctum vs Passport
Sanctum = simple token auth
Passport = full OAuth2 server
๐ฏ 15. Events & Listeners
Events = things that happen in your app, Listeners = reactions to those events
๐ Scalability: Easy to add new listeners without changing existing code
๐งช Testability: Test each listener independently
โก Performance: Can queue listeners for background processing
๐ Reusability: Same event can trigger multiple listeners
๐ ๏ธ Maintainability: Each listener has a single responsibility
Model Events: Automatically fired by Eloquent (created, updated, deleted)
Examples:
โข Custom: OrderPlaced, PaymentProcessed, UserBanned
โข Model: User created, Post updated, Comment deleted
php artisan make:event UserRegistered2. Create Listener:
php artisan make:listener SendWelcomeEmail --event=UserRegistered3. Register in EventServiceProvider: Map events to listeners
4. Fire Event:
event(new UserRegistered($user))5. Auto-discovery: Laravel can auto-discover listeners
// 1. Create Event
php artisan make:event UserRegistered
// app/Events/UserRegistered.php
class UserRegistered {
public $user;
public function __construct(User $user) {
$this->user = $user;
}
}
// 2. Create Listeners
php artisan make:listener SendWelcomeEmail --event=UserRegistered
php artisan make:listener CreateUserProfile --event=UserRegistered
php artisan make:listener SendAdminNotification --event=UserRegistered
// app/Listeners/SendWelcomeEmail.php
class SendWelcomeEmail {
public function handle(UserRegistered $event) {
Mail::to($event->user->email)->send(new WelcomeMail($event->user));
}
}
// 3. Register in EventServiceProvider
protected $listen = [
UserRegistered::class => [
SendWelcomeEmail::class,
CreateUserProfile::class,
SendAdminNotification::class,
],
];
// 4. Fire the event
class AuthController extends Controller {
public function register(Request $request) {
$user = User::create($request->validated());
// This triggers ALL the listeners above
event(new UserRegistered($user));
return redirect()->home();
}
}
// Queued Listeners (background processing)
class SendWelcomeEmail implements ShouldQueue {
public function handle(UserRegistered $event) {
// This runs in the background via queue
Mail::to($event->user->email)->send(new WelcomeMail($event->user));
}
}
// Model Events (automatic)
class User extends Model {
protected static function boot() {
parent::boot();
static::created(function ($user) {
event(new UserRegistered($user));
});
static::deleting(function ($user) {
event(new UserDeleting($user));
});
}
}
// Event Broadcasting (real-time)
class OrderShipped implements ShouldBroadcast {
public $order;
public function broadcastOn() {
return new PrivateChannel('orders.' . $this->order->user_id);
}
}
ShouldQueue interface on listeners to process them in the background for better user experience!
Use: User registration flows, order processing, notifications, logging, cache invalidation
17. Mass Assignment Protection
Mass Assignment = bulk filling model attributes
class User extends Model {
protected $fillable = ['name', 'email'];
// or
protected $guarded = ['id', 'password'];
}
18. Policies and Gates
Gates = closures for authorization
Policies = classes for model permissions
19. Performance Optimization
Cache config/routes/views
Eager load relationships
Queue slow jobs
Optimize queries
Use Redis
20. Migrations
Migrations = version control for DB schema
php artisan make:migration create_users_table
php artisan migrate
php artisan migrate:rollback
21. Jobs vs Queues
Jobs = define tasks to run
Queues = hold jobs to run asynchronously
22. Bind vs Singleton
bind() = new instance each time
singleton() = same instance always
23. CSRF Protection
CSRF = Cross-Site Request Forgery
<form>
@csrf
<!-- form fields -->
</form>
25. Eloquent Relationships
hasMany = One-to-many (User has many Posts)
belongsTo = Inverse relation (Post belongs to User)
belongsToMany = Many-to-many with pivot table
๐๏ธ 26. SOLID Principles
SOLID = five design principles for maintainable and scalable code
O - Open/Closed: Open for extension, closed for modification (use interfaces, inheritance)
L - Liskov Substitution: Subclasses should replace parent classes seamlessly
I - Interface Segregation: Many small interfaces better than one large interface
D - Dependency Inversion: Depend on abstractions, not concrete classes
โ Good: Separate concerns into Controller (HTTP), FormRequest (validation), Service (business logic), Repository (data access)
Benefits: Easier testing, better maintainability, clearer code organization
Laravel Implementation: Inject interfaces in constructors, bind implementations in service providers
Benefits: Easy to swap implementations, better testing with mocks, loose coupling
Example: Inject
PaymentGatewayInterface instead of StripePaymentGateway
// โ SOLID Violations - Fat Controller
class UserController extends Controller {
public function store(Request $request) {
// Validation in controller (should be in FormRequest)
$rules = ['email' => 'required|email|unique:users'];
$this->validate($request, $rules);
// Business logic in controller (should be in Service)
$user = new User();
$user->email = $request->email;
$user->password = Hash::make($request->password);
$user->email_verification_token = Str::random(60);
$user->save();
// Email sending in controller (should be queued job)
Mail::to($user)->send(new WelcomeEmail($user));
// Multiple responsibilities in one method!
return response()->json(['message' => 'User created']);
}
}
// โ
SOLID Compliant - Single Responsibility
// 1. FormRequest handles validation
class CreateUserRequest extends FormRequest {
public function rules(): array {
return [
'email' => 'required|email|unique:users',
'password' => 'required|min:8|confirmed',
];
}
}
// 2. Service handles business logic
class UserService {
public function __construct(
private UserRepositoryInterface $userRepository,
private EmailServiceInterface $emailService
) {}
public function createUser(array $userData): User {
$user = $this->userRepository->create([
'email' => $userData['email'],
'password' => Hash::make($userData['password']),
'email_verification_token' => Str::random(60),
]);
$this->emailService->sendWelcomeEmail($user);
return $user;
}
}
// 3. Controller only handles HTTP
class UserController extends Controller {
public function __construct(private UserService $userService) {}
public function store(CreateUserRequest $request): JsonResponse {
$user = $this->userService->createUser($request->validated());
return response()->json([
'message' => 'User created successfully',
'user' => new UserResource($user)
], 201);
}
}
// โ
Open/Closed Principle - Extensible without modification
interface PaymentGatewayInterface {
public function charge(float $amount, string $paymentMethod): PaymentResult;
}
class StripePaymentGateway implements PaymentGatewayInterface {
public function charge(float $amount, string $paymentMethod): PaymentResult {
// Stripe-specific implementation
return new PaymentResult($stripeResponse);
}
}
class PayPalPaymentGateway implements PaymentGatewayInterface {
public function charge(float $amount, string $paymentMethod): PaymentResult {
// PayPal-specific implementation
return new PaymentResult($paypalResponse);
}
}
// PaymentService is closed for modification but open for extension
class PaymentService {
public function __construct(
private PaymentGatewayInterface $gateway
) {}
public function processPayment(Order $order): PaymentResult {
return $this->gateway->charge(
$order->total,
$order->payment_method
);
}
}
// โ
Interface Segregation - Small, focused interfaces
interface Readable {
public function read(int $id): ?Model;
}
interface Writable {
public function create(array $data): Model;
public function update(int $id, array $data): bool;
}
interface Deletable {
public function delete(int $id): bool;
}
// Implement only what you need
class ReadOnlyUserRepository implements Readable {
public function read(int $id): ?User {
return User::find($id);
}
}
class FullUserRepository implements Readable, Writable, Deletable {
public function read(int $id): ?User { /* implementation */ }
public function create(array $data): User { /* implementation */ }
public function update(int $id, array $data): bool { /* implementation */ }
public function delete(int $id): bool { /* implementation */ }
}
// โ
Liskov Substitution - Subclasses work as expected
abstract class Animal {
abstract public function makeSound(): string;
abstract public function move(): string;
}
class Dog extends Animal {
public function makeSound(): string { return "Woof!"; }
public function move(): string { return "Running"; }
}
class Fish extends Animal {
public function makeSound(): string { return "Blub"; }
public function move(): string { return "Swimming"; }
}
// Both Dog and Fish can replace Animal without breaking code
function animalShow(Animal $animal): string {
return $animal->makeSound() . " while " . $animal->move();
}
// Service Provider bindings for Dependency Inversion
class AppServiceProvider extends ServiceProvider {
public function register(): void {
// Bind interface to implementation
$this->app->bind(
PaymentGatewayInterface::class,
StripePaymentGateway::class
);
$this->app->bind(
UserRepositoryInterface::class,
EloquentUserRepository::class
);
// Context-based binding
$this->app->when(PaymentController::class)
->needs(PaymentGatewayInterface::class)
->give(StripePaymentGateway::class);
$this->app->when(RefundController::class)
->needs(PaymentGatewayInterface::class)
->give(PayPalPaymentGateway::class);
}
}
Use: Clean architecture, maintainable code, easier testing, team collaboration, scalable applications
๐ญ 27. OOP Concepts
OOP = Encapsulation, Inheritance, Polymorphism, and Abstraction
Public Methods: Controlled way to interact with the object
Getters/Setters: Control how properties are accessed and modified
Laravel Example: Eloquent models encapsulate database operations
Benefits: Data integrity, controlled access, easier maintenance
Method Overriding: Child classes providing specific implementations
Laravel Examples: Mail drivers, Cache drivers, Queue drivers
Dynamic Binding: Service container resolves correct implementation at runtime
Benefits: Code flexibility, easy to extend, cleaner architecture
Interfaces: Only method signatures, no implementation, multiple inheritance
When to use Abstract: Shared code between related classes
When to use Interface: Contract definition, unrelated classes with common behavior
Laravel Example:
ShouldQueue interface, Model abstract class
// โ
Encapsulation Example
class BankAccount {
private float $balance = 0;
private string $accountNumber;
private array $transactionHistory = [];
public function __construct(string $accountNumber) {
$this->accountNumber = $accountNumber;
}
// Controlled access to balance
public function getBalance(): float {
return $this->balance;
}
// Validation and business logic in methods
public function deposit(float $amount): bool {
if ($amount <= 0) {
throw new InvalidArgumentException('Amount must be positive');
}
$this->balance += $amount;
$this->addTransaction('deposit', $amount);
return true;
}
public function withdraw(float $amount): bool {
if ($amount > $this->balance) {
throw new InsufficientFundsException();
}
$this->balance -= $amount;
$this->addTransaction('withdrawal', $amount);
return true;
}
// Private method - internal implementation
private function addTransaction(string $type, float $amount): void {
$this->transactionHistory[] = [
'type' => $type,
'amount' => $amount,
'timestamp' => now(),
'balance_after' => $this->balance
];
}
// Read-only access to history
public function getTransactionHistory(): array {
return $this->transactionHistory;
}
}
// โ
Inheritance and Polymorphism
abstract class Vehicle {
protected string $brand;
protected float $speed = 0;
public function __construct(string $brand) {
$this->brand = $brand;
}
// Concrete method - inherited by all
public function getBrand(): string {
return $this->brand;
}
// Abstract method - must be implemented by children
abstract public function start(): bool;
abstract public function getMaxSpeed(): float;
// Method that can be overridden
public function accelerate(float $increment): void {
$this->speed = min($this->speed + $increment, $this->getMaxSpeed());
}
}
class Car extends Vehicle {
private bool $engineRunning = false;
public function start(): bool {
$this->engineRunning = true;
return true;
}
public function getMaxSpeed(): float {
return 200.0; // km/h
}
// Override parent method with specific behavior
public function accelerate(float $increment): void {
if (!$this->engineRunning) {
throw new Exception('Cannot accelerate - engine not running');
}
parent::accelerate($increment);
}
}
class Bicycle extends Vehicle {
public function start(): bool {
return true; // Always ready to go
}
public function getMaxSpeed(): float {
return 50.0; // km/h
}
}
// โ
Interface Implementation (Polymorphism)
interface Driveable {
public function drive(float $distance): float;
public function getFuelEfficiency(): float;
}
interface Flyable {
public function takeOff(): bool;
public function land(): bool;
public function getAltitude(): float;
}
class Car extends Vehicle implements Driveable {
public function drive(float $distance): float {
$fuelUsed = $distance / $this->getFuelEfficiency();
return $fuelUsed;
}
public function getFuelEfficiency(): float {
return 15.0; // km per liter
}
}
class Airplane extends Vehicle implements Driveable, Flyable {
private float $altitude = 0;
public function drive(float $distance): float {
// Airplane can also taxi on ground
return $distance / 5.0; // Less efficient on ground
}
public function getFuelEfficiency(): float {
return 3.0; // km per liter
}
public function takeOff(): bool {
$this->altitude = 1000;
return true;
}
public function land(): bool {
$this->altitude = 0;
return true;
}
public function getAltitude(): float {
return $this->altitude;
}
}
// Polymorphism in action
function calculateTripCost(Driveable $vehicle, float $distance, float $fuelPrice): float {
$fuelNeeded = $vehicle->drive($distance);
return $fuelNeeded * $fuelPrice;
}
$car = new Car('Toyota');
$airplane = new Airplane('Boeing');
// Same interface, different implementations
$carCost = calculateTripCost($car, 100, 1.5);
$planeCost = calculateTripCost($airplane, 100, 2.0);
// โ
Laravel-specific OOP Examples
// Eloquent Model (Encapsulation + Inheritance)
class User extends Model implements MustVerifyEmail {
// Encapsulated properties
protected $fillable = ['name', 'email', 'password'];
protected $hidden = ['password', 'remember_token'];
protected $casts = ['email_verified_at' => 'datetime'];
// Encapsulated business logic
public function isAdmin(): bool {
return $this->role === 'admin';
}
public function getFullNameAttribute(): string {
return $this->first_name . ' ' . $this->last_name;
}
// Relationships (composition)
public function posts(): HasMany {
return $this->hasMany(Post::class);
}
}
// Service Layer (Composition over Inheritance)
class UserService {
public function __construct(
private UserRepositoryInterface $repository,
private EmailServiceInterface $emailService,
private CacheInterface $cache
) {}
public function createUser(array $data): User {
// Composition: using multiple services
$user = $this->repository->create($data);
$this->emailService->sendWelcomeEmail($user);
$this->cache->forget('users:count');
return $user;
}
}
Use: Clean code architecture, reusable components, maintainable systems, team development
๐ 28. PHP 8+ Features
PHP 8+ = modern features for cleaner, faster, and safer code
Named Arguments: Call functions with parameter names for clarity
Attributes: Replace docblock annotations with native syntax
Constructor Property Promotion: Declare and assign properties in one line
Match Expression: More powerful alternative to switch statements
Nullsafe Operator: Safe navigation through nullable objects (?->)
JIT Compiler: Improved performance for computation-heavy tasks
API Responses: Return different data types based on conditions
Model Attributes: Handle mixed data types in Eloquent models
Better Documentation: Self-documenting code without docblocks
IDE Support: Better autocompletion and error detection
Validation Rules: Attach validation to DTO properties
API Documentation: Generate OpenAPI specs from attributes
Dependency Injection: Mark classes for auto-registration
Better Performance: Native PHP parsing vs regex docblock parsing
PHP 8.2: Readonly classes, DNF types, sensitive parameter redaction
PHP 8.3: Typed class constants, dynamic class constant fetch, JSON validation
Performance: Each version brings 5-10% performance improvements
Laravel Support: Laravel 10+ requires PHP 8.1+, Laravel 11+ requires PHP 8.2+
// โ
PHP 8 Union Types
class PaymentService {
// Accept multiple types
public function processPayment(string|int $amount, User|Guest $customer): PaymentResult {
$normalizedAmount = is_string($amount) ? (float) $amount : $amount;
return match($customer::class) {
User::class => $this->processUserPayment($normalizedAmount, $customer),
Guest::class => $this->processGuestPayment($normalizedAmount, $customer),
};
}
// Return different types based on condition
public function getPaymentDetails(int $paymentId): Payment|null {
return Payment::find($paymentId);
}
}
// โ
Constructor Property Promotion (PHP 8)
// Old way (verbose)
class UserServiceOld {
private UserRepository $userRepository;
private EmailService $emailService;
private LoggerInterface $logger;
public function __construct(
UserRepository $userRepository,
EmailService $emailService,
LoggerInterface $logger
) {
$this->userRepository = $userRepository;
$this->emailService = $emailService;
$this->logger = $logger;
}
}
// New way (concise)
class UserService {
public function __construct(
private UserRepository $userRepository,
private EmailService $emailService,
private LoggerInterface $logger
) {}
}
// โ
Named Arguments (PHP 8)
// Old way - positional arguments
$user = User::create([
'name' => 'John Doe',
'email' => 'john@example.com',
'password' => Hash::make('password'),
'email_verified_at' => now(),
'remember_token' => Str::random(10)
]);
// New way - named arguments for clarity
$response = Cache::remember(
key: "user:{$userId}:profile",
ttl: 3600,
callback: fn() => $this->buildUserProfile($userId)
);
// Named arguments with validation
$request->validate(
rules: [
'email' => 'required|email',
'password' => 'required|min:8'
],
messages: [
'email.required' => 'Email is mandatory',
'password.min' => 'Password too short'
]
);
// โ
Match Expression (PHP 8) - More powerful than switch
class OrderService {
public function calculateShipping(Order $order): float {
return match($order->shipping_method) {
'standard' => 5.99,
'express' => 12.99,
'overnight' => 24.99,
'pickup' => 0.00,
default => throw new InvalidShippingMethodException()
};
}
public function getOrderStatus(Order $order): string {
return match(true) {
$order->isPaid() && $order->isShipped() => 'Delivered',
$order->isPaid() && !$order->isShipped() => 'Processing',
!$order->isPaid() && $order->created_at->lt(now()->subHours(24)) => 'Abandoned',
default => 'Pending'
};
}
}
// โ
Nullsafe Operator (PHP 8) - Safe navigation
class UserController {
public function show(int $userId): JsonResponse {
$user = User::find($userId);
// Old way - multiple null checks
$avatarUrl = null;
if ($user && $user->profile && $user->profile->avatar) {
$avatarUrl = $user->profile->avatar->url;
}
// New way - nullsafe operator
$avatarUrl = $user?->profile?->avatar?->url;
return response()->json([
'user' => $user,
'avatar_url' => $avatarUrl,
'company_name' => $user?->company?->name ?? 'Freelancer'
]);
}
}
// โ
Attributes (PHP 8) - Replace annotations
use App\Attributes\{Route, Validate, Cache, RateLimit};
class UserController extends Controller {
#[Route('GET', '/api/users/{id}')]
#[Cache(ttl: 3600, tags: ['users'])]
#[RateLimit(requests: 60, window: 'minute')]
public function show(
#[Validate('integer|min:1')] int $id
): JsonResponse {
$user = User::findOrFail($id);
return response()->json(new UserResource($user));
}
#[Route('POST', '/api/users')]
#[RateLimit(requests: 10, window: 'minute')]
public function store(
#[Validate(CreateUserRequest::class)] array $data
): JsonResponse {
$user = User::create($data);
return response()->json(new UserResource($user), 201);
}
}
// โ
PHP 8.1 Enums
enum OrderStatus: string {
case PENDING = 'pending';
case PAID = 'paid';
case SHIPPED = 'shipped';
case DELIVERED = 'delivered';
case CANCELLED = 'cancelled';
public function label(): string {
return match($this) {
self::PENDING => 'Awaiting Payment',
self::PAID => 'Payment Received',
self::SHIPPED => 'On the Way',
self::DELIVERED => 'Delivered',
self::CANCELLED => 'Cancelled',
};
}
public function canTransitionTo(OrderStatus $status): bool {
return match($this) {
self::PENDING => in_array($status, [self::PAID, self::CANCELLED]),
self::PAID => in_array($status, [self::SHIPPED, self::CANCELLED]),
self::SHIPPED => $status === self::DELIVERED,
default => false,
};
}
}
// Using enums in Laravel
class Order extends Model {
protected $casts = [
'status' => OrderStatus::class,
];
public function markAsShipped(): void {
if (!$this->status->canTransitionTo(OrderStatus::SHIPPED)) {
throw new InvalidOrderTransitionException();
}
$this->update(['status' => OrderStatus::SHIPPED]);
}
}
// โ
PHP 8.1 Readonly Properties
class PaymentResult {
public function __construct(
public readonly string $transactionId,
public readonly float $amount,
public readonly DateTimeImmutable $processedAt,
public readonly bool $successful
) {}
// Properties cannot be modified after construction
// $result->amount = 100; // Error!
}
// โ
PHP 8.2 Readonly Classes
readonly class UserProfileDTO {
public function __construct(
public string $name,
public string $email,
public ?string $avatar,
public DateTimeImmutable $createdAt
) {}
// All properties are automatically readonly
public static function fromModel(User $user): self {
return new self(
name: $user->name,
email: $user->email,
avatar: $user->avatar?->url,
createdAt: $user->created_at->toDateTimeImmutable()
);
}
}
Use: Modern codebases, better type safety, improved performance, cleaner syntax, enhanced developer experience