What is Blueprint in Laravel migration?
Blueprint is a class in Laravel that provides a fluent interface to define database table structure. It’s used within migrations to create, modify, or delete database tables and columns.
Key features:
- Provides methods to define various column types (string, integer, boolean, etc.)
- Allows setting column modifiers (nullable, default, unique, etc.)
- Offers methods to create indexes, foreign keys, and constraints
- Works with the Schema facade to create and modify tables
Example:
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
// Blueprint methods to define columns
$table->id();
$table->string('name', 100);
$table->text('description')->nullable();
$table->decimal('price', 8, 2);
$table->integer('stock')->default(0);
$table->boolean('is_active')->default(true);
$table->foreignId('category_id')->constrained()->onDelete('cascade');
$table->timestamps();
$table->softDeletes(); // Add deleted_at column
// Index creation
$table->index('name');
$table->unique(['sku']);
});
}
}
Real-life scenario: When building an e-commerce platform, developers use Blueprint to define product tables with specific data types (like decimals for prices) and relationships (foreign keys to categories), ensuring consistent database schema across all environments.
What is Schema in Laravel migration?
Schema is a Laravel facade that provides a database-agnostic way to create and modify database tables. It acts as the entry point for defining database structure changes in migrations.
Key functionalities:
- Create, modify, rename, and drop tables
- Check if tables or columns exist
- Add or drop indexes, foreign keys, and constraints
- Works across different database systems (MySQL, PostgreSQL, SQLite, SQL Server)
Example:
use Illuminate\Support\Facades\Schema;
// Creating a table
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamps();
});
// Checking if a table exists
if (Schema::hasTable('users')) {
// Do something
}
// Modifying an existing table
Schema::table('users', function (Blueprint $table) {
$table->string('phone')->nullable()->after('email');
});
// Dropping a table
Schema::dropIfExists('temporary_logs');
// Renaming a table
Schema::rename('old_table_name', 'new_table_name');
Real-life scenario: When implementing a feature that requires new data storage, developers create a migration using Schema to define the required tables. Later, if the requirements change, they can create additional migrations to modify the existing schema without affecting the data.
Key Differences Between Schema & Blueprint
While Schema and Blueprint work together in Laravel migrations, they serve different purposes and have distinct roles:
Schema | Blueprint |
---|---|
|
|
Relationship: Schema is the manager that initiates operations, while Blueprint is the builder that defines the structure details. Schema calls Blueprint to do the actual work of defining columns and constraints.
Example showing their relationship:
// Schema (the manager) initiates the operation
Schema::create('orders', function (Blueprint $table) {
// Blueprint (the builder) defines the structure
$table->id();
$table->foreignId('user_id')->constrained();
$table->decimal('total', 10, 2);
$table->string('status');
$table->timestamps();
});
// Schema performing an operation without Blueprint
Schema::dropIfExists('temporary_table');
Real-life scenario: In a team environment, understanding this distinction helps developers correctly structure their migrations. The Schema facade handles the overall operation (e.g., “create this table”), while Blueprint defines the detailed specifications of that table (columns, types, constraints).
How to implement soft delete in Laravel?
Soft deletion in Laravel allows you to “delete” records without actually removing them from the database. Instead, it sets a deleted_at
timestamp, and the query builder automatically excludes these “deleted” records from query results.
Implementation steps:
- Add the SoftDeletes trait to your model
- Add the deleted_at column to your database table
Example:
// 1. Migration to add soft delete column
public function up()
{
Schema::table('products', function (Blueprint $table) {
$table->softDeletes(); // Adds deleted_at column
});
}
// 2. Model implementation
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Product extends Model
{
use SoftDeletes; // This trait enables soft deletion
// Rest of your model code
}
Working with soft deleted records:
// Normal deletion (sets deleted_at)
$product->delete();
// Force deletion (actually removes from database)
$product->forceDelete();
// Restore a soft-deleted record
$product = Product::withTrashed()->find(1);
$product->restore();
// Query including soft-deleted records
$allProducts = Product::withTrashed()->get();
// Query only soft-deleted records
$trashedProducts = Product::onlyTrashed()->get();
Real-life scenario: In a content management system, when users “delete” articles, they’re actually soft deleted, allowing administrators to restore them if needed. This prevents accidental permanent data loss while keeping the user interface clean by hiding deleted content.
Why Use ORM?
Object-Relational Mapping (ORM) is a programming technique that converts data between incompatible type systems in object-oriented programming languages. In Laravel, Eloquent is the ORM that provides an elegant, simple ActiveRecord implementation for working with your database.
Key benefits of using ORM:
- Abstraction of database logic: Write PHP code instead of SQL queries
- Database agnosticism: Switch between MySQL, PostgreSQL, SQLite without changing code
- Security: Built-in protection against SQL injection
- Relationships: Easy handling of database relationships (one-to-one, one-to-many, etc.)
- Code organization: Better code structure with models representing database tables
- Productivity: Faster development with less boilerplate code
- Maintainability: Easier to read, understand, and modify
Example comparing raw SQL vs. Eloquent ORM:
// Raw SQL approach
$pdo = DB::connection()->getPdo();
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND active = 1');
$stmt->bindValue(':email', $email);
$stmt->execute();
$user = $stmt->fetch(PDO::FETCH_OBJ);
// Eloquent ORM approach
$user = User::where('email', $email)->where('active', 1)->first();
Real-life scenario: In a web application with complex data relationships (e.g., users, orders, products, categories), Eloquent ORM significantly simplifies data management. For example, retrieving all orders with their items and customer details is as simple as Order::with(['items', 'customer'])->get()
, which would require complex joins and manual data structuring with raw SQL.
Query Scopes
Query scopes in Laravel allow you to encapsulate common query logic into reusable methods, making your code cleaner, more maintainable, and easier to read. Laravel supports both local and global scopes.
Types of query scopes:
- Local Scopes: Methods defined in a model that can be chained onto query builder methods
- Global Scopes: Constraints that are automatically applied to all queries for a given model
Local Scope Example:
// Defining a local scope in a model
class Product extends Model
{
// Scope for active products
public function scopeActive($query)
{
return $query->where('is_active', true);
}
// Scope with parameters
public function scopePriceRange($query, $min, $max)
{
return $query->whereBetween('price', [$min, $max]);
}
}
// Using local scopes
$activeProducts = Product::active()->get();
$affordableProducts = Product::priceRange(10, 100)->get();
$featuredAffordable = Product::active()->priceRange(10, 100)->featured()->get();
Global Scope Example:
// Creating a global scope class
namespace App\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class ActiveScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
$builder->where('is_active', true);
}
}
// Applying global scope in a model
namespace App\Models;
use App\Scopes\ActiveScope;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected static function booted()
{
static::addGlobalScope(new ActiveScope);
}
}
// Now all queries will only return active products
$products = Product::all(); // Only active products
// Removing global scope for a specific query
$allProducts = Product::withoutGlobalScope(ActiveScope::class)->get(); // All products
Real-life scenario: In an e-commerce application, you might want to show only published products to regular users. By implementing a scopePublished
scope, you can easily filter product listings without repeating the condition in controllers. Similarly, with a global scope for current tenant in a multi-tenant application, you ensure data isolation without having to add tenant filtering to every query.
Relationships in Eloquent ORM
Eloquent ORM in Laravel provides elegant ways to work with relationships between database tables. These relationships are defined as methods in your model classes.
Common relationship types in Laravel:
- One-to-One: A record in one table is associated with exactly one record in another table
- One-to-Many: A record in one table is associated with multiple records in another table
- Many-to-Many: Multiple records in one table are associated with multiple records in another table
- Has-One-Through: Access distant relations via an intermediate relation
- Has-Many-Through: Similar to has-one-through but for multiple records
- Polymorphic Relationships: A model can belong to more than one type of model
Examples:
// One-to-One
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
}
}
class Profile extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
// One-to-Many
class Post extends Model
{
public function comments()
{
return $this->hasMany(Comment::class);
}
}
class Comment extends Model
{
public function post()
{
return $this->belongsTo(Post::class);
}
}
// Many-to-Many
class User extends Model
{
public function roles()
{
return $this->belongsToMany(Role::class);
}
}
class Role extends Model
{
public function users()
{
return $this->belongsToMany(User::class);
}
}
// Has-Many-Through
class Country extends Model
{
public function posts()
{
return $this->hasManyThrough(Post::class, User::class);
}
}
// Polymorphic Relationship
class Image extends Model
{
public function imageable()
{
return $this->morphTo();
}
}
class User extends Model
{
public function image()
{
return $this->morphOne(Image::class, 'imageable');
}
}
Using relationships:
// Eager loading to prevent N+1 problem
$posts = Post::with('comments')->get();
// Accessing related data
$user = User::find(1);
$profile = $user->profile;
// Creating related records
$post = Post::find(1);
$post->comments()->create([
'content' => 'Great post!'
]);
// Querying relationships
$postsWithComments = Post::has('comments')->get();
$usersWithRoles = User::whereHas('roles', function($query) {
$query->where('name', 'admin');
})->get();
Real-life scenario: In a blog application, you might have User, Post, and Comment models with relationships. When displaying a post page, you can efficiently load the post with its author and comments in a single query: Post::with(['user', 'comments.user'])->find($id)
. This retrieves the post, its author, all comments, and the user who wrote each comment—all in one optimized database query.
What is reverse Routing in Laravel?
Reverse routing in Laravel refers to the practice of generating URLs based on route names rather than hardcoding URLs in your application. This approach makes your application more maintainable, as changing the URL structure only requires updating the route definition, not every reference to that URL throughout your codebase.
Key benefits:
- Makes your application more maintainable
- Prevents broken links if URL patterns change
- Creates cleaner, more readable code
- Automatically handles URL encoding
Example:
// Define a named route in routes/web.php
Route::get('/user/{id}', [UserController::class, 'show'])->name('user.profile');
// Generate URL from route name in PHP
$url = route('user.profile', ['id' => 1]); // Returns /user/1
// Including query parameters
$url = route('user.profile', ['id' => 1, 'tab' => 'settings']); // Returns /user/1?tab=settings
// In Blade templates
<a href="{{ route('user.profile', ['id' => $user->id]) }}">View Profile</a>
// Generating redirects in controllers
return redirect()->route('user.profile', ['id' => $user->id]);
Real-life scenario: In a large application with complex URL structures, you might decide to change the URL pattern from /user/{id}
to /profile/{id}
for SEO purposes. With reverse routing, you only need to update the route definition, and all route() calls will automatically generate the new URL pattern, preventing broken links without having to search and replace URLs throughout your codebase.
What is Dependency Injection in Laravel?
Dependency Injection (DI) is a design pattern where a class receives its dependencies from external sources rather than creating them itself. Laravel’s IoC (Inversion of Control) container automatically resolves dependencies for your controllers, event listeners, middleware, and other components.
Key benefits:
- Reduces coupling between classes
- Makes code more testable by allowing mock dependencies
- Increases code flexibility and reusability
- Promotes SOLID principles, especially Dependency Inversion
Types of dependency injection in Laravel:
- Constructor Injection: Dependencies are injected through the constructor
- Method Injection: Dependencies are injected through method parameters
- Property Injection: Dependencies are injected into class properties (less common in Laravel)
Example – Constructor Injection:
// Define a service class
class UserService
{
protected $repository;
public function __construct(UserRepository $repository)
{
$this->repository = $repository;
}
public function getUser($id)
{
return $this->repository->find($id);
}
}
// In a controller, Laravel automatically injects the dependency
class UserController extends Controller
{
protected $userService;
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function show($id)
{
$user = $this->userService->getUser($id);
return view('users.show', compact('user'));
}
}
Example – Method Injection:
class ProductController extends Controller
{
public function store(Request $request, ProductService $productService)
{
$product = $productService->create($request->all());
return redirect()->route('products.show', $product->id);
}
}
Real-life scenario: In an e-commerce application, you might inject a PaymentGatewayInterface into your OrderController. This allows you to switch between different payment providers (e.g., Stripe, PayPal) by changing the binding in a service provider, without modifying the controller code. During testing, you can inject a MockPaymentGateway to test order processing without making actual API calls.
How to use skip() and take() in Laravel Query?
The skip()
and take()
methods in Laravel’s query builder allow you to limit the number of results and implement pagination manually. skip()
determines how many records to skip, while take()
specifies how many records to retrieve.
Note: For most pagination needs, Laravel’s built-in paginate()
method is preferred as it handles all the pagination logic for you.
Basic usage:
// Retrieve 10 records starting from the 21st record
$users = DB::table('users')
->skip(20) // Skip first 20 records
->take(10) // Take 10 records
->get();
// With Eloquent models
$posts = Post::skip(20)->take(10)->get();
// Order matters - first skip, then take
$recentComments = Comment::latest()
->skip(5)
->take(10)
->get();
// Implementing manual pagination
$page = request()->get('page', 1);
$perPage = 15;
$skip = ($page - 1) * $perPage;
$users = User::skip($skip)->take($perPage)->get();
$totalUsers = User::count();
Real-life scenario: When implementing an infinite scroll feature in a social media feed, you might use skip() and take() to load posts in batches. As the user scrolls down, your JavaScript would make AJAX requests like /api/posts?skip=20&take=10
to continuously load the next batch of posts without refreshing the page.
What is the Repository pattern in Laravel?
The Repository pattern is a design pattern that separates the logic that retrieves data from the underlying storage (like a database) from the rest of the application. In Laravel, repositories act as an abstraction layer between models and controllers.
Key benefits:
- Separates concerns and improves code organization
- Makes code more testable by allowing mock repositories
- Centralizes data access logic
- Reduces duplication of query logic
- Makes it easier to change the data source without affecting application logic
Implementation example:
// 1. Create an interface
namespace App\Repositories\Interfaces;
interface UserRepositoryInterface
{
public function all();
public function find($id);
public function create(array $data);
public function update($id, array $data);
public function delete($id);
}
// 2. Implement the repository
namespace App\Repositories;
use App\Models\User;
use App\Repositories\Interfaces\UserRepositoryInterface;
class UserRepository implements UserRepositoryInterface
{
protected $model;
public function __construct(User $user)
{
$this->model = $user;
}
public function all()
{
return $this->model->all();
}
public function find($id)
{
return $this->model->findOrFail($id);
}
public function create(array $data)
{
return $this->model->create($data);
}
public function update($id, array $data)
{
$record = $this->find($id);
$record->update($data);
return $record;
}
public function delete($id)
{
return $this->model->destroy($id);
}
}
// 3. Register in service provider
namespace App\Providers;
use App\Repositories\Interfaces\UserRepositoryInterface;
use App\Repositories\UserRepository;
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(
UserRepositoryInterface::class,
UserRepository::class
);
}
}
// 4. Use in controller
namespace App\Http\Controllers;
use App\Repositories\Interfaces\UserRepositoryInterface;
class UserController extends Controller
{
protected $userRepository;
public function __construct(UserRepositoryInterface $userRepository)
{
$this->userRepository = $userRepository;
}
public function index()
{
$users = $this->userRepository->all();
return view('users.index', compact('users'));
}
}
Real-life scenario: In a complex application with various data sources (MySQL database, Redis cache, external APIs), you could implement repositories for each entity. Controllers would always interact with repository interfaces, not directly with models. This way, you could change storage implementations (e.g., moving from MySQL to MongoDB) without changing your controllers or business logic.
What is the Singleton design pattern in Laravel?
The Singleton pattern is a creational design pattern that ensures a class has only one instance while providing a global access point to this instance. In Laravel, the Service Container uses the singleton binding to create a single instance of a class that is reused throughout the application’s lifecycle.
Key characteristics:
- Only one instance of the class exists throughout the application
- Provides global access to that instance
- Initializes only when first requested
- Useful for services that need to maintain state or are resource-intensive
Examples in Laravel:
// Registering a singleton in a service provider
namespace App\Providers;
use App\Services\PaymentGateway;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
// Method 1: Using singleton method
$this->app->singleton(PaymentGateway::class, function ($app) {
return new PaymentGateway($app->make('config'));
});
// Method 2: Using instance method with an existing object
$paymentGateway = new PaymentGateway($this->app->make('config'));
$this->app->instance(PaymentGateway::class, $paymentGateway);
}
}
// Using a singleton in your code
class OrderController extends Controller
{
public function process(Request $request, PaymentGateway $gateway)
{
// This is the same instance throughout the app
$gateway->processPayment($request->amount);
}
}
// Resolving manually from the container
$gateway1 = app(PaymentGateway::class);
$gateway2 = app(PaymentGateway::class);
// $gateway1 and $gateway2 are the same instance
Laravel core services that use the Singleton pattern:
- Cache Manager
- Config Repository
- Database Connection
- Event Dispatcher
- File System
- Validation Factory
Real-life scenario: A payment processing service that needs to maintain connection state and configuration would be registered as a singleton. This ensures that expensive connection setup happens only once, and the same configured instance is used for all payment processing throughout the request lifecycle, improving performance and ensuring consistency.
What are the advantages of Queue?
Queues in software development allow applications to handle time-consuming tasks asynchronously by deferring their execution to a later time. Laravel’s queue system provides a unified API for various queue backends like Redis, Amazon SQS, and database.
Key advantages:
- Improved response times: Users don’t have to wait for time-consuming tasks
- Increased scalability: System can handle more concurrent users
- Better resource utilization: Distribute heavy processing across time
- Increased reliability: Failed jobs can be retried automatically
- Background processing: Handle tasks that don’t need immediate execution
- Scheduling flexibility: Process jobs during off-peak hours
- Graceful degradation: System remains responsive under high load
Real-life scenario: In an e-commerce platform, when a user places an order, multiple time-consuming tasks need to happen: payment processing, inventory updates, email notifications, shipping label generation, etc. By queuing these tasks, the user receives confirmation immediately while the system processes these tasks in the background, providing a much better user experience.
What are queues in Laravel?
Laravel Queues provide a unified API across a variety of different queue backends to handle time-consuming tasks asynchronously. By pushing tasks to a queue, your application can respond to requests faster while processing tasks in the background.
Supported queue drivers:
- Database
- Redis
- Amazon SQS
- Beanstalkd
- Sync (for local development)
Implementation in Laravel:
// 1. Configure queue driver in .env
QUEUE_CONNECTION=redis
// 2. Create a job class
php artisan make:job ProcessPodcast
// 3. Define the job
namespace App\Jobs;
use App\Models\Podcast;
use App\Services\AudioProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $podcast;
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast;
}
public function handle(AudioProcessor $processor)
{
// Process the podcast
$processor->process($this->podcast);
}
}
// 4. Dispatch the job
// Method 1: Static dispatch
ProcessPodcast::dispatch($podcast);
// Method 2: Using Bus facade
use Illuminate\Support\Facades\Bus;
Bus::dispatch(new ProcessPodcast($podcast));
// With delay
ProcessPodcast::dispatch($podcast)->delay(now()->addMinutes(10));
// Specifying queue
ProcessPodcast::dispatch($podcast)->onQueue('processing');
// 5. Run the queue worker
php artisan queue:work
// Run specific queue
php artisan queue:work --queue=processing
// Keep running after failure
php artisan queue:work --tries=3
Handling failed jobs:
// Create failed jobs table
php artisan queue:failed-table
php artisan migrate
// Add failure handling to job
class ProcessPodcast implements ShouldQueue
{
// ...
public function failed(\Throwable $exception)
{
// Log the failure or notify someone
Log::error('Podcast processing failed: ' . $exception->getMessage());
}
}
Real-life scenario: When users upload large video files to a video sharing platform, the videos need to be transcoded into different formats and resolutions. By using Laravel queues, the platform can accept uploads immediately, show them as “processing” to users, and handle the resource-intensive transcoding in the background using multiple queue workers distributed across servers.
Define accessors and mutators.
Accessors and mutators in Laravel’s Eloquent ORM allow you to transform attributes when retrieving or setting model values. They provide a powerful way to format data consistently throughout your application.
Accessors: Transform the data when it’s retrieved from the database
Mutators: Transform the data before it’s saved to the database
In Laravel 9+, there are two ways to define accessors and mutators:
- Using dedicated methods
- Using the Attribute class with a fluent API (recommended in newer Laravel versions)
Example using methods:
// Accessor (get)
class User extends Model
{
// Accessor - transforms 'first_name' when accessed
public function getFirstNameAttribute($value)
{
return ucfirst($value);
}
// Mutator - transforms 'email' before saving
public function setEmailAttribute($value)
{
$this->attributes['email'] = strtolower($value);
}
// Creating custom non-database attributes
public function getFullNameAttribute()
{
return "{$this->first_name} {$this->last_name}";
}
}
// Usage
$user = User::find(1);
echo $user->first_name; // Will be capitalized
echo $user->full_name; // Will return concatenated first and last name
$user->email = 'JOHN@EXAMPLE.COM';
$user->save(); // Will be saved as 'john@example.com'
Example using Attribute class (Laravel 9+):
use Illuminate\Database\Eloquent\Casts\Attribute;
class User extends Model
{
// Combined accessor/mutator using Attribute class
protected function firstName(): Attribute
{
return Attribute::make(
get: fn ($value) => ucfirst($value),
set: fn ($value) => strtolower($value)
);
}
// Accessor only
protected function fullName(): Attribute
{
return Attribute::get(
fn () => "{$this->first_name} {$this->last_name}"
);
}
// Mutator only
protected function email(): Attribute
{
return Attribute::set(
fn ($value) => strtolower($value)
);
}
}
Real-life scenario: In a customer management system, you might store phone numbers without formatting in the database (e.g., “1234567890”) for consistent searching and validation. Using a mutator, you can strip all non-numeric characters when phone numbers are saved. With a corresponding accessor, you can format them for display (e.g., “(123) 456-7890”) whenever they’re retrieved, ensuring consistent formatting throughout the application without duplicating formatting logic.
What is throttling and how to implement it in Laravel?
Throttling in Laravel refers to rate limiting, which restricts how many requests a user can make to your application within a specified time period. It’s crucial for preventing abuse, brute force attacks, and ensuring fair resource usage.
Key use cases:
- Protecting login endpoints from brute force attacks
- Limiting API calls to prevent abuse
- Ensuring fair resource usage in high-traffic applications
- Protecting against DoS (Denial of Service) attacks
Implementation in Laravel:
// 1. Using middleware in routes/web.php or routes/api.php
Route::middleware('throttle:5,1')->group(function () {
Route::post('/login', [AuthController::class, 'login']);
});
// The example above limits to 5 requests per minute
// 2. Specifying different limits for guests and authenticated users
Route::middleware('throttle:60,1')->group(function () {
// Routes for all users (60 requests per minute)
});
Route::middleware('auth:api', 'throttle:120,1')->group(function () {
// Routes for authenticated users (120 requests per minute)
});
// 3. Dynamic rate limits based on user
// First, define the rate limiter in App\Providers\RouteServiceProvider
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
protected function configureRateLimiting()
{
RateLimiter::for('api', function (Request $request) {
return $request->user()?->isPremium()
? Limit::perMinute(100)
: Limit::perMinute(30);
});
RateLimiter::for('login', function (Request $request) {
return Limit::perMinute(5)->by($request->ip());
});
}
// Then use the named limiter in routes
Route::middleware('throttle:login')->group(function () {
Route::post('/login', [AuthController::class, 'login']);
});
// 4. Handling rate limit exceeded
Route::middleware('throttle:api')->group(function () {
// Routes here
});
// In Exception\Handler.php
public function render($request, Throwable $exception)
{
if ($exception instanceof ThrottleRequestsException) {
return response()->json([
'message' => 'Too many requests. Please try again later.',
'retry_after' => $exception->getHeaders()['Retry-After']
], 429);
}
return parent::render($request, $exception);
}
Headers returned when rate limited:
X-RateLimit-Limit
: Maximum requests per periodX-RateLimit-Remaining
: Remaining requests in the current periodRetry-After
: Seconds until requests can resume
Real-life scenario: In a SaaS application with a public API, you might implement tiered rate limiting based on subscription level: free users get 100 requests per hour, while premium users get 1000. You’d also implement stricter limits on endpoints that are resource-intensive or security-sensitive (like authentication endpoints) to prevent abuse and ensure system stability.
What are Requests in Laravel?
In Laravel, Requests refer to HTTP requests that are sent by clients (browsers, API consumers, etc.) to your application. Laravel provides an elegant way to access and handle these requests through the Illuminate\Http\Request
class.
Key features of Laravel Requests:
- Access to all HTTP request information (URL, method, headers, body, etc.)
- Input validation through Form Request classes
- File upload handling
- Cookie and session management
- CSRF protection
Basic Request handling:
// Type-hinting in controller methods for automatic injection
use Illuminate\Http\Request;
class UserController extends Controller
{
public function store(Request $request)
{
// Access input data
$name = $request->input('name');
// Alternative syntax
$name = $request->name;
// Get all input data
$all = $request->all();
// Get only specific fields
$credentials = $request->only(['email', 'password']);
// Get all except specific fields
$postData = $request->except(['_token']);
// Check if input exists
if ($request->has('name')) {
// Do something
}
// Get with default value if missing
$page = $request->input('page', 1);
// Check request method
if ($request->isMethod('post')) {
// Handle POST request
}
// Check if request is AJAX/XML-HTTP
if ($request->ajax()) {
// Return JSON response
}
// Get request path and URL
$path = $request->path();
$url = $request->url();
$fullUrl = $request->fullUrl();
}
}
Handling file uploads:
public function upload(Request $request)
{
if ($request->hasFile('photo')) {
// Validate the file
$validated = $request->validate([
'photo' => 'required|file|image|max:2048',
]);
// Store the file
$path = $request->file('photo')->store('photos');
// Or specify a disk
$path = $request->file('photo')->store('photos', 's3');
// Or use storeAs to specify the filename
$path = $request->file('photo')->storeAs(
'photos', $request->user()->id . '.jpg', 's3'
);
return $path;
}
}
Real-life scenario: In a social media application, you might have a controller method that handles post creation. The Request object helps you extract and validate the post content, attached images, and metadata before saving the post to the database. By using Laravel’s Request features, you can ensure that all user input is properly sanitized and validated before processing.
How to do request validation in Laravel?
Laravel provides several approaches to validate incoming HTTP request data. Validation helps ensure that user input meets your application’s requirements before processing it.
Main validation approaches in Laravel:
- Validating in the controller using the validate() method
- Using dedicated Form Request classes
- Manual validation using the Validator facade
Validation in controller:
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'body' => 'required|string',
'published_at' => 'nullable|date',
'category_id' => 'required|exists:categories,id',
'tags' => 'nullable|array',
'tags.*' => 'exists:tags,id',
'image' => 'nullable|image|max:2048',
]);
// If validation fails, it automatically redirects back with errors
// If validation passes, it continues here with the validated data
$post = Post::create($validated);
return redirect()->route('posts.show', $post);
}
Using Form Request classes:
// Generate a form request
php artisan make:request StorePostRequest
// app/Http/Requests/StorePostRequest.php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePostRequest extends FormRequest
{
public function authorize()
{
// Add authorization logic here
return true; // Allow all users for now
}
public function rules()
{
return [
'title' => 'required|string|max:255',
'body' => 'required|string',
'published_at' => 'nullable|date',
'category_id' => 'required|exists:categories,id',
'tags' => 'nullable|array',
'tags.*' => 'exists:tags,id',
'image' => 'nullable|image|max:2048',
];
}
// Custom error messages
public function messages()
{
return [
'title.required' => 'A post title is required',
'category_id.exists' => 'The selected category does not exist',
];
}
// Custom attributes
public function attributes()
{
return [
'category_id' => 'category',
];
}
// Prepare the data for validation
protected function prepareForValidation()
{
$this->merge([
'slug' => Str::slug($this->title),
]);
}
}
// Use in controller
public function store(StorePostRequest $request)
{
// Validation already happened
$post = Post::create($request->validated());
return redirect()->route('posts.show', $post);
}
Manual validation:
use Illuminate\Support\Facades\Validator;
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'title' => 'required|string|max:255',
'body' => 'required|string',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
// Validation passed
$validated = $validator->validated();
// Or continue with custom logic
if ($request->has('draft')) {
// Handle draft logic
}
$post = Post::create($validated);
return redirect()->route('posts.show', $post);
}
Display validation errors in Blade:
<form method="POST" action="/posts">
@csrf
<div class="form-group">
<label for="title">Title</label>
<input type="text" class="form-control @error('title') is-invalid @enderror" id="title" name="title" value="{{ old('title') }}">
@error('title')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<!-- Other fields -->
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Real-life scenario: In a user registration system, you would validate various fields: ensuring email addresses are valid and unique, passwords meet complexity requirements, usernames contain only allowed characters, etc. By using Form Request classes, you keep the validation logic separate from your controllers, making the code more maintainable and testable as the application grows.
What is the register and boot method in the Service Provider class?
Service Providers in Laravel are the central place to configure your application. They are responsible for bootstrapping most of Laravel’s components like database, queue, validation, routing, etc. The register
and boot
methods play crucial roles in a service provider’s lifecycle.
The register() method:
- Used to bind things into the service container
- Should only be used for binding things, not for using any services
- Runs during the registration phase, before all providers have been loaded
- Should be kept lightweight and simple
The boot() method:
- Runs after all service providers have been registered
- Allows you to use any services that have been registered
- Used for operations that depend on other service providers
- Can type-hint dependencies that will be automatically resolved
Example of a Service Provider:
namespace App\Providers;
use App\Contracts\PaymentGatewayInterface;
use App\Services\StripePaymentGateway;
use App\Services\PaymentProcessor;
use Illuminate\Support\ServiceProvider;
class PaymentServiceProvider extends ServiceProvider
{
/**
* Register bindings in the container.
*/
public function register()
{
// Simple binding
$this->app->bind(PaymentGatewayInterface::class, StripePaymentGateway::class);
// Binding with closure
$this->app->bind(PaymentProcessor::class, function ($app) {
return new PaymentProcessor(
$app->make(PaymentGatewayInterface::class),
$app->make('config')->get('payment.options')
);
});
// Singleton binding
$this->app->singleton('payment.manager', function ($app) {
return new PaymentManager($app);
});
}
}
// Using a singleton in your code
class OrderController extends Controller
{
public function process(Request $request, PaymentGateway $gateway)
{
// This is the same instance throughout the app
$gateway->processPayment($request->amount);
}
}
// Resolving manually from the container
$gateway1 = app(PaymentGateway::class);
$gateway2 = app(PaymentGateway::class);
// $gateway1 and $gateway2 are the same instance
Execution order:
- All service providers have their
register()
methods called - After all providers are registered, each provider’s
boot()
method is called
Real-life scenario: If you’re building a payment integration package for Laravel, your service provider would bind your payment service interfaces to concrete implementations in the register()
method. Then in the boot()
method, you might register routes for payment webhooks, publish configuration files, register middleware for payment verification, and set up event listeners for payment events—operations that might require services from other providers to be available.
What are route groups?
Route groups in Laravel allow you to share route attributes, such as middleware, prefixes, namespaces, and more, across multiple routes without having to define those attributes on each individual route. This keeps your routes file clean and DRY (Don’t Repeat Yourself).
Key use cases for route groups:
- Applying middleware to a set of routes (auth, throttling, etc.)
- Adding URL prefixes (like /admin/ or /api/)
- Namespacing controller classes
- Subdomain routing
- Route name prefixing
Basic examples:
// Middleware route group
Route::middleware(['auth'])->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
Route::get('/profile', [ProfileController::class, 'show']);
Route::put('/profile', [ProfileController::class, 'update']);
});
// Prefix route group
Route::prefix('admin')->group(function () {
Route::get('/users', [AdminController::class, 'users']);
Route::get('/settings', [AdminController::class, 'settings']);
});
// Name prefixed route group
Route::name('admin.')->group(function () {
Route::get('/admin/dashboard', [AdminController::class, 'dashboard'])->name('dashboard');
// Route name becomes 'admin.dashboard'
});
// Combining multiple attributes
Route::prefix('api')
->middleware(['api', 'throttle:60,1'])
->name('api.')
->group(function () {
Route::get('/users', [ApiController::class, 'users'])->name('users.index');
// Resulting route: /api/users
// Middleware: api, throttle:60,1
// Route name: api.users.index
});
// Nested route groups
Route::prefix('api')->group(function () {
Route::prefix('v1')->group(function () {
Route::apiResource('products', ProductApiController::class);
// Routes will have prefix /api/v1
});
});
// Domain or subdomain routing
Route::domain('{account}.example.com')->group(function () {
Route::get('/', function ($account) {
return 'Account: ' . $account;
});
});
Real-life scenario: In a multi-tenant SaaS application, you might use route groups to organize routes for different user roles and areas of the application. For example, customer-facing routes might be grouped with certain middleware and prefixes, while admin routes would have different middleware for authorization and their own prefix. API routes would be grouped separately with appropriate throttling and authentication middleware. This keeps your routes organized and ensures proper access controls are consistently applied.
How to create a route for resources in Laravel?
Laravel’s resource routing allows you to quickly define a typical “CRUD” (Create, Read, Update, Delete) route structure for a resource with a single line of code. This is based on RESTful principles and corresponds to controller actions for managing that resource.
Basic Resource Route:
// routes/web.php
use App\Http\Controllers\PostController;
Route::resource('posts', PostController::class);
This single line creates the following seven routes:
HTTP Method | URI | Controller Method | Route Name | Purpose |
---|---|---|---|---|
GET | /posts | index | posts.index | Display a list of resources |
GET | /posts/create | create | posts.create | Show form to create a new resource |
POST | /posts | store | posts.store | Store a newly created resource |
GET | /posts/{post} | show | posts.show | Display a specific resource |
GET | /posts/{post}/edit | edit | posts.edit | Show form to edit a resource |
PUT/PATCH | /posts/{post} | update | posts.update | Update a specific resource |
DELETE | /posts/{post} | destroy | posts.destroy | Delete a specific resource |
Customizing Resource Routes:
// Specify only certain actions
Route::resource('photos', PhotoController::class)->only([
'index', 'show'
]);
// Exclude specific actions
Route::resource('comments', CommentController::class)->except([
'create', 'store', 'update', 'destroy'
]);
// API resource routes (excludes create and edit forms)
Route::apiResource('products', ProductController::class);
// Multiple API resources
Route::apiResources([
'products' => ProductController::class,
'categories' => CategoryController::class
]);
// Nested resources
Route::resource('posts.comments', PostCommentController::class);
// Creates routes like: /posts/{post}/comments/{comment}
// Shallow nesting (prevents deeply nested URIs)
Route::resource('posts.comments', PostCommentController::class)->shallow();
// Creates routes like: /posts/{post}/comments for index/create/store
// But simpler /comments/{comment} for show/edit/update/destroy
// Custom resource route parameters
Route::resource('users', UserController::class)->parameters([
'users' => 'admin_user'
]);
// Changes /users/{user} to /users/{admin_user}
// Localizing resource URIs
Route::resource('posts', PostController::class)->names([
'create' => 'posts.build',
'destroy' => 'posts.remove',
]);
Generating a Resource Controller:
// Generate a controller for the resource
php artisan make:controller PostController --resource
// With model generation
php artisan make:controller PostController --resource --model=Post
// API resource controller (no create/edit methods)
php artisan make:controller API/ProductController --api
Real-life scenario: In a content management system, you might have resources like posts, categories, tags, users, and media. With resource routes, you can quickly set up all the necessary endpoints for managing these resources without manually defining each route. For example, the posts resource would automatically have routes for listing all posts, showing a single post, creating new posts, updating existing ones, and deleting them—all following RESTful conventions.
What are contracts?
Contracts in Laravel are a set of interfaces that define the core services provided by the framework. They serve as the API definition for these services and facilitate loose coupling between components.
Key points about Laravel Contracts:
- They are PHP interfaces that define the methods a service must implement
- They provide a clear and concise API for core Laravel services
- They enable dependency injection of framework services
- They allow for easy swapping of implementations without changing dependent code
- They provide documentation about which features are available
Common Laravel Contracts include:
Illuminate\Contracts\Auth\Authenticatable
Illuminate\Contracts\Queue\Queue
Illuminate\Contracts\Mail\Mailer
Illuminate\Contracts\Cache\Repository
Illuminate\Contracts\Routing\Registrar
Using Contracts:
// Example: Using the Cache contract in a service
namespace App\Services;
use Illuminate\Contracts\Cache\Repository as Cache;
class UserAnalytics
{
protected $cache;
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
public function getUserStats($userId)
{
return $this->cache->remember("user.{$userId}.stats", 3600, function () use ($userId) {
// Calculate and return user statistics
return $this->calculateUserStats($userId);
});
}
protected function calculateUserStats($userId)
{
// Expensive calculation...
}
}
Creating and implementing your own contract:
// 1. Define the contract interface
namespace App\Contracts;
interface PaymentGatewayInterface
{
public function charge(array $data);
public function refund($transactionId);
}
// 2. Create implementations
namespace App\Services;
use App\Contracts\PaymentGatewayInterface;
class StripePaymentGateway implements PaymentGatewayInterface
{
public function charge(array $data)
{
// Stripe-specific implementation
}
public function refund($transactionId)
{
// Stripe-specific refund logic
}
}
class PayPalPaymentGateway implements PaymentGatewayInterface
{
public function charge(array $data)
{
// PayPal-specific implementation
}
public function refund($transactionId)
{
// PayPal-specific refund logic
}
}
// 3. Register in a service provider
namespace App\Providers;
use App\Contracts\PaymentGatewayInterface;
use App\Services\StripePaymentGateway;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(PaymentGatewayInterface::class, function ($app) {
// Choose implementation based on config
return $app->make(config('services.payment.driver'));
});
}
}
// 4. Use in controller
namespace App\Http\Controllers;
use App\Repositories\Interfaces\UserRepositoryInterface;
class UserController extends Controller
{
protected $userRepository;
public function __construct(UserRepositoryInterface $userRepository)
{
$this->userRepository = $userRepository;
}
public function index()
{
$users = $this->userRepository->all();
return view('users.index', compact('users'));
}
}
Real-life scenario: In a multi-channel notification system, you might define a NotificationChannelInterface
with methods like send()
and canSend()
. You would then implement this interface for various channels (email, SMS, push notification, etc.) and bind the appropriate implementation based on user preferences or system configuration. This allows you to add new notification channels without changing the code that sends notifications, as it depends on the interface, not specific implementations.
Which is the REPL used in Laravel?
Laravel includes a powerful REPL (Read-Eval-Print Loop) called Tinker, which allows you to interact with your Laravel application from the command line. Tinker is powered by the PsySH package and provides a convenient way to test ideas, experiment with your database, and debug code without building a full UI.
Key features of Tinker:
- Interactive PHP console within your Laravel application context
- Access to all your application’s models, services, and facades
- Auto-completion and documentation assistance
- Support for executing arbitrary PHP code
- Ability to explore relationships and test database queries
Using Tinker:
// Start Tinker
php artisan tinker
// Creating a model instance
$user = new App\Models\User;
$user->name = "John Doe";
$user->email = "john@example.com";
$user->password = Hash::make("password");
$user->save();
// Querying the database
$users = App\Models\User::all();
$userCount = App\Models\User::count();
$admin = App\Models\User::where('email', 'admin@example.com')->first();
// Exploring relationships
$user = App\Models\User::find(1);
$user->posts; // Retrieve related posts
// Using Laravel helpers and facades
now(); // Carbon instance of current time
config('app.name'); // Access configuration
Storage::disk('local')->files(); // List files
// Test events
event(new App\Events\UserRegistered($user));
// Execute artisan commands
\Artisan::call('cache:clear');
// Exit Tinker
exit;
Customizing Tinker:
php artisan vendor:publish --provider="Laravel\Tinker\TinkerServiceProvider"
This creates a config/tinker.php
file where you can configure:
- Commands to add to Tinker
- Alias classes for shorter reference
- Classes that should not be aliased
- Whitelist of classes that can be modified
Real-life scenario: During development of a payment processing module, you could use Tinker to test the payment gateway integration without building a UI. You might create test orders, run them through your payment service, test error handling paths, and even trigger events like payment confirmations to ensure the entire workflow functions correctly. Tinker lets you interactively debug and test these components in isolation, saving development time.
What is SOLID?
SOLID is a set of five object-oriented design principles that help developers create more maintainable, understandable, and flexible software. The term was coined by Robert C. Martin (Uncle Bob) and stands for:
- S – Single Responsibility Principle
- O – Open-Closed Principle
- L – Liskov Substitution Principle
- I – Interface Segregation Principle
- D – Dependency Inversion Principle
These principles are fundamental to good software design and are widely applied in Laravel’s architecture.
Single Responsibility Principle (SRP):
A class should have only one reason to change, meaning it should have only one job or responsibility.
Open-Closed Principle (OCP):
Software entities should be open for extension but closed for modification. This means you should be able to add new functionality without changing existing, already tested and deployed code.
Liskov Substitution Principle (LSP):
Derived classes must be substitutable for their base classes without altering the correctness of the program. In other words, if S is a subtype of T, objects of type T may be replaced with objects of type S without affecting the functionality of the program.
Interface Segregation Principle (ISP):
No client should be forced to depend on methods it does not use. This suggests creating specific interfaces rather than large, general-purpose ones.
Dependency Inversion Principle (DIP):
High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.
How Laravel implements SOLID:
- SRP: Laravel’s MVC pattern separates concerns (Models for data, Controllers for logic, Views for presentation)
- OCP: Laravel’s service providers allow extending functionality without modifying core code
- LSP: Laravel’s interfaces ensure that implementations can be swapped (e.g., different cache drivers)
- ISP: Laravel’s contracts are focused and specific (e.g., separate contracts for each cache operation)
- DIP: Laravel’s IOC container and dependency injection promote loose coupling
Real-life scenario: In a Laravel application, SOLID principles guide your design decisions. For example, rather than creating large controllers that handle many actions, you might apply SRP by using single-action controllers or form request classes to separate validation logic. Instead of hardcoding notification logic, you might apply DIP by creating a NotificationService that depends on a NotificationInterface, allowing for different implementations (email, SMS, etc.) to be swapped without changing the service itself.
Single-responsibility principle
The Single Responsibility Principle (SRP) states that a class should have only one reason to change, meaning it should have only one job or responsibility. This principle is one of the five SOLID principles and is fundamental to creating maintainable software.
Key benefits of applying SRP:
- Easier to understand and maintain classes
- Improved testability of individual components
- Reduced risk when making changes
- Better organization and clearer code structure
- Easier collaboration for development teams
Example without SRP:
class User
{
protected $name;
protected $email;
protected $database;
protected $logger;
protected $emailService;
public function __construct($database, $logger, $emailService)
{
$this->database = $database;
$this->logger = $logger;
$this->emailService = $emailService;
}
public function save()
{
// Save user to database
$query = "INSERT INTO users (name, email) VALUES ('{$this->name}', '{$this->email}')";
$this->database->execute($query);
// Log the action
$this->logger->log("User {$this->email} was saved");
// Send welcome email
$this->emailService->send($this->email, 'Welcome', 'Welcome to our platform!');
}
}
Example with SRP applied:
// User entity - only responsible for representing user data
class User
{
protected $name;
protected $email;
public function getName()
{
return $this->name;
}
public function getEmail()
{
return $this->email;
}
}
// UserRepository - responsible for database operations
class UserRepository
{
protected $database;
public function __construct($database)
{
$this->database = $database;
}
public function save(User $user)
{
$query = "INSERT INTO users (name, email) VALUES ('{$user->getName()}', '{$user->getEmail()}')";
$this->database->execute($query);
}
}
// Logger - responsible for logging
class UserLogger
{
protected $logger;
public function __construct($logger)
{
$this->logger = $logger;
}
public function logUserCreation(User $user)
{
$this->logger->log("User {$user->getEmail()} was saved");
}
}
// UserNotifier - responsible for sending notifications
class UserNotifier
{
protected $emailService;
public function __construct($emailService)
{
$this->emailService = $emailService;
}
public function sendWelcomeEmail(User $user)
{
$this->emailService->send($user->getEmail(), 'Welcome', 'Welcome to our platform!');
}
}
// UserService - orchestrates the process using the specialized classes
class UserService
{
protected $repository;
protected $logger;
protected $notifier;
public function __construct(UserRepository $repository, UserLogger $logger, UserNotifier $notifier)
{
$this->repository = $repository;
$this->logger = $logger;
$this->notifier = $notifier;
}
public function register(User $user)
{
$this->repository->save($user);
$this->logger->logUserCreation($user);
$this->notifier->sendWelcomeEmail($user);
}
}
Applying SRP in Laravel:
- Form Request classes: Separate validation logic from controllers
- Jobs and Queues: Move time-consuming tasks outside of request lifecycle
- Service classes: Extract complex business logic from controllers
- Policies: Separate authorization logic from controllers
- Repository pattern: Separate database access logic from models
Real-life scenario: In an e-commerce application, you might have resources like posts, categories, tags, users, and media. With resource routes, you can quickly set up all the necessary endpoints for managing these resources without manually defining each route. For example, the posts resource would automatically have routes for listing all posts, showing a single post, creating new posts, updating existing ones, and deleting them—all following RESTful conventions.