Laravel
The most popular full-stack web framework for PHP. Features Eloquent ORM and Blade templating engine with excellent development efficiency.
GitHub Overview
laravel/laravel
Laravel is a web application framework with expressive, elegant syntax. We’ve already laid the foundation for your next big idea — freeing you to create without sweating the small things.
Topics
Star History
Framework
Laravel
Overview
Laravel is a modern PHP web application framework that enables high-quality web development with elegant syntax and rich functionality.
Details
Laravel was developed by Taylor Otwell in 2011 as a PHP web application framework. With the catchphrase "The PHP Framework for Web Artisans," it emphasizes beautiful and expressive syntax. Laravel adopts the Model-View-Controller (MVC) architectural pattern and provides powerful features including Eloquent ORM, Blade template engine, Artisan command-line tool, middleware, and dependency injection container. Standard features include authentication, routing, sessions, caching, database migrations, and unit testing, enabling rapid development. Through Composer package management, you can leverage a rich library ecosystem. Currently adopted by major enterprises like Disney+, Toyota, BMW, and Pfizer, it has become one of the most popular PHP frameworks.
Pros and Cons
Pros
- Elegant Syntax: Beautiful and readable code
- Rich Built-in Features: Many features like authentication, mail, queues built-in
- Eloquent ORM: Intuitive and powerful database operations
- Artisan CLI: Command-line tool that improves development efficiency
- Blade Templates: Simple yet powerful template engine
- Active Community: Large community and abundant learning resources
- Excellent Documentation: Clear and detailed official documentation
Cons
- Learning Curve: Difficult for beginners to master due to many features
- Performance: Slightly heavier compared to other PHP frameworks
- Version Compatibility: Breaking changes in major updates
- Memory Usage: Large memory consumption in large-scale applications
- Over-abstraction: Can become complex even for simple tasks
Key Links
- Laravel Official Site
- Laravel Documentation
- Laravel GitHub Repository
- Laracasts (Learning Resource)
- Laravel News
- Packagist (Composer)
Code Examples
Hello World
// Project creation commands
// composer create-project laravel/laravel hello-laravel
// cd hello-laravel
// php artisan serve
// routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\WelcomeController;
// Basic route
Route::get('/', function () {
return view('welcome', ['message' => 'Hello, Laravel World!']);
});
// Named route
Route::get('/hello', function () {
return response()->json([
'message' => 'Hello from Laravel API!',
'timestamp' => now()->toDateTimeString(),
'version' => app()->version()
]);
})->name('api.hello');
// Route using controller
Route::get('/welcome/{name?}', [WelcomeController::class, 'show'])
->name('welcome.show');
// app/Http/Controllers/WelcomeController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class WelcomeController extends Controller
{
public function show(Request $request, $name = null)
{
$name = $name ?? 'Guest';
return view('welcome.show', [
'name' => $name,
'time' => now()->format('F j, Y g:i A')
]);
}
}
// resources/views/welcome/show.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Laravel Welcome</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100">
<div class="container mx-auto px-4 py-8">
<div class="bg-white rounded-lg shadow-md p-6">
<h1 class="text-3xl font-bold text-gray-800 mb-4">
Welcome, {{ $name }}!
</h1>
<p class="text-gray-600 mb-4">Current time: {{ $time }}</p>
<a href="{{ route('api.hello') }}"
class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
API Endpoint
</a>
</div>
</div>
</body>
</html>
Eloquent ORM
// Model generation commands
// php artisan make:model User --migration --factory --seeder
// php artisan make:model Post --migration --factory
// php artisan make:model Tag --migration --factory
// php artisan make:model PostTag --migration
// app/Models/User.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class User extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'name', 'email', 'password', 'active'
];
protected $hidden = [
'password', 'remember_token'
];
protected $casts = [
'email_verified_at' => 'datetime',
'active' => 'boolean'
];
// Relationships
public function posts()
{
return $this->hasMany(Post::class);
}
public function publishedPosts()
{
return $this->hasMany(Post::class)->where('published', true);
}
// Scopes
public function scopeActive($query)
{
return $query->where('active', true);
}
public function scopeHasPosts($query)
{
return $query->whereHas('posts');
}
// Accessors
public function getFullNameAttribute()
{
return "{$this->first_name} {$this->last_name}";
}
// Mutators
public function setEmailAttribute($value)
{
$this->attributes['email'] = strtolower($value);
}
}
// app/Models/Post.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $fillable = [
'title', 'content', 'published', 'published_at', 'user_id'
];
protected $casts = [
'published' => 'boolean',
'published_at' => 'datetime'
];
// Relationships
public function user()
{
return $this->belongsTo(User::class);
}
public function tags()
{
return $this->belongsToMany(Tag::class, 'post_tags');
}
// Scopes
public function scopePublished($query)
{
return $query->where('published', true);
}
public function scopeRecent($query)
{
return $query->orderBy('created_at', 'desc');
}
public function scopeByTag($query, $tagName)
{
return $query->whereHas('tags', function ($q) use ($tagName) {
$q->where('name', $tagName);
});
}
// Accessors
public function getExcerptAttribute()
{
return substr(strip_tags($this->content), 0, 150) . '...';
}
}
// Eloquent usage examples
use App\Models\User;
use App\Models\Post;
use App\Models\Tag;
// Create data
$user = User::create([
'name' => 'John',
'email' => '[email protected]',
'password' => bcrypt('password'),
'active' => true
]);
$post = $user->posts()->create([
'title' => 'Laravel Guide',
'content' => 'Laravel is an amazing framework.',
'published' => true,
'published_at' => now()
]);
$tag = Tag::create(['name' => 'Laravel']);
$post->tags()->attach($tag);
// Query examples
$users = User::active()->with('posts')->get();
$posts = Post::published()
->with(['user', 'tags'])
->recent()
->paginate(10);
$laravelPosts = Post::byTag('Laravel')
->published()
->get();
// Advanced queries
$popularUsers = User::withCount(['posts' => function ($query) {
$query->where('published', true);
}])
->having('posts_count', '>', 5)
->get();
// Bulk updates
Post::where('created_at', '<', now()->subDays(30))
->update(['archived' => true]);
// Creating relationships
$user = User::find(1);
$user->posts()->create([
'title' => 'New Post',
'content' => 'Content...'
]);
Routing
// routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PostController;
use App\Http\Controllers\AdminController;
use App\Http\Controllers\Auth\LoginController;
// Basic routes
Route::get('/', function () {
return view('home');
})->name('home');
Route::get('/about', function () {
return view('about');
})->name('about');
// Routes with parameters
Route::get('/posts/{id}', function ($id) {
$post = App\Models\Post::findOrFail($id);
return view('posts.show', compact('post'));
})->name('posts.show')->where('id', '[0-9]+');
// Optional parameters
Route::get('/users/{id?}', function ($id = null) {
if ($id) {
$user = App\Models\User::findOrFail($id);
return view('users.show', compact('user'));
}
return view('users.index');
})->name('users.index');
// RESTful resource routes
Route::resource('posts', PostController::class);
// Partial resource routes
Route::resource('comments', CommentController::class)
->only(['index', 'store', 'destroy']);
// Nested resources
Route::resource('posts.comments', CommentController::class)
->shallow();
// Route groups (middleware)
Route::middleware(['auth'])->group(function () {
Route::get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
Route::resource('posts', PostController::class)
->except(['index', 'show']);
});
// Groups with prefix
Route::prefix('admin')->name('admin.')->group(function () {
Route::get('/dashboard', [AdminController::class, 'dashboard'])
->name('dashboard');
Route::resource('users', AdminController::class);
});
// Subdomain routing
Route::domain('{subdomain}.example.com')->group(function () {
Route::get('/', function ($subdomain) {
return "Subdomain: {$subdomain}";
});
});
// routes/api.php
Route::prefix('v1')->name('api.v1.')->group(function () {
// Public APIs
Route::get('/posts', [PostController::class, 'index']);
Route::get('/posts/{post}', [PostController::class, 'show']);
// Protected APIs
Route::middleware('auth:sanctum')->group(function () {
Route::post('/posts', [PostController::class, 'store']);
Route::put('/posts/{post}', [PostController::class, 'update']);
Route::delete('/posts/{post}', [PostController::class, 'destroy']);
});
});
// Route model binding
Route::get('/posts/{post}', function (App\Models\Post $post) {
return $post;
});
// Custom key model binding
Route::get('/posts/{post:slug}', function (App\Models\Post $post) {
return $post;
});
// Route caching
// php artisan route:cache
// php artisan route:clear
Migrations and Seeders
// Migration generation commands
// php artisan make:migration create_posts_table
// php artisan make:migration add_published_to_posts_table --table=posts
// database/migrations/xxx_create_users_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->boolean('active')->default(true);
$table->rememberToken();
$table->timestamps();
$table->softDeletes();
$table->index(['active', 'created_at']);
});
}
public function down()
{
Schema::dropIfExists('users');
}
};
// database/migrations/xxx_create_posts_table.php
<?php
return new class extends Migration
{
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->string('slug')->unique();
$table->boolean('published')->default(false);
$table->timestamp('published_at')->nullable();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->timestamps();
$table->index(['published', 'published_at']);
$table->index('slug');
});
}
public function down()
{
Schema::dropIfExists('posts');
}
};
// Column addition migration
<?php
return new class extends Migration
{
public function up()
{
Schema::table('posts', function (Blueprint $table) {
$table->integer('view_count')->default(0)->after('published_at');
$table->json('meta_data')->nullable()->after('view_count');
$table->index('view_count');
});
}
public function down()
{
Schema::table('posts', function (Blueprint $table) {
$table->dropIndex(['view_count']);
$table->dropColumn(['view_count', 'meta_data']);
});
}
};
// database/seeders/UserSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
class UserSeeder extends Seeder
{
public function run()
{
// Create admin user
User::create([
'name' => 'Administrator',
'email' => '[email protected]',
'password' => Hash::make('password'),
'active' => true
]);
// Generate dummy data using factory
User::factory(50)->create();
}
}
// database/factories/PostFactory.php
<?php
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class PostFactory extends Factory
{
public function definition()
{
$title = $this->faker->sentence();
return [
'title' => $title,
'slug' => Str::slug($title),
'content' => $this->faker->paragraphs(3, true),
'published' => $this->faker->boolean(70),
'published_at' => $this->faker->optional(0.7)->dateTimeBetween('-1 year', 'now'),
'user_id' => User::factory(),
];
}
public function published()
{
return $this->state([
'published' => true,
'published_at' => now()
]);
}
public function draft()
{
return $this->state([
'published' => false,
'published_at' => null
]);
}
}
// Migration execution commands
// php artisan migrate
// php artisan migrate:fresh --seed
// php artisan migrate:rollback
// php artisan migrate:status
// php artisan db:seed
// php artisan db:seed --class=UserSeeder
Form Processing and Validation
// FormRequest generation command
// php artisan make:request StorePostRequest
// app/Http/Requests/StorePostRequest.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePostRequest extends FormRequest
{
public function authorize()
{
return auth()->check();
}
public function rules()
{
return [
'title' => 'required|string|max:255|unique:posts,title',
'content' => 'required|string|min:50',
'published' => 'boolean',
'published_at' => 'nullable|date|after_or_equal:today',
'tags' => 'array',
'tags.*' => 'exists:tags,id',
'featured_image' => 'nullable|image|mimes:jpeg,png,jpg|max:2048'
];
}
public function messages()
{
return [
'title.required' => 'Title is required.',
'title.unique' => 'This title is already taken.',
'content.required' => 'Content is required.',
'content.min' => 'Content must be at least 50 characters.',
'featured_image.image' => 'Please select an image file.',
'featured_image.max' => 'Image size must be under 2MB.'
];
}
public function attributes()
{
return [
'title' => 'Title',
'content' => 'Content',
'published' => 'Published Status',
'published_at' => 'Publish Date',
'tags' => 'Tags',
'featured_image' => 'Featured Image'
];
}
}
// app/Http/Controllers/PostController.php
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StorePostRequest;
use App\Models\Post;
use App\Models\Tag;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;
class PostController extends Controller
{
public function index()
{
$posts = Post::with(['user', 'tags'])
->published()
->latest()
->paginate(10);
return view('posts.index', compact('posts'));
}
public function show(Post $post)
{
$post->load(['user', 'tags']);
return view('posts.show', compact('post'));
}
public function create()
{
$tags = Tag::all();
return view('posts.create', compact('tags'));
}
public function store(StorePostRequest $request)
{
$validated = $request->validated();
$validated['slug'] = Str::slug($validated['title']);
$validated['user_id'] = auth()->id();
// Handle image upload
if ($request->hasFile('featured_image')) {
$validated['featured_image'] = $request->file('featured_image')
->store('posts', 'public');
}
$post = Post::create($validated);
// Attach tags
if (isset($validated['tags'])) {
$post->tags()->sync($validated['tags']);
}
return redirect()
->route('posts.show', $post)
->with('success', 'Post created successfully.');
}
public function edit(Post $post)
{
$this->authorize('update', $post);
$tags = Tag::all();
return view('posts.edit', compact('post', 'tags'));
}
public function update(StorePostRequest $request, Post $post)
{
$this->authorize('update', $post);
$validated = $request->validated();
$validated['slug'] = Str::slug($validated['title']);
// Handle new image upload
if ($request->hasFile('featured_image')) {
// Delete old image
if ($post->featured_image) {
Storage::disk('public')->delete($post->featured_image);
}
$validated['featured_image'] = $request->file('featured_image')
->store('posts', 'public');
}
$post->update($validated);
// Update tag relationships
if (isset($validated['tags'])) {
$post->tags()->sync($validated['tags']);
}
return redirect()
->route('posts.show', $post)
->with('success', 'Post updated successfully.');
}
public function destroy(Post $post)
{
$this->authorize('delete', $post);
// Delete image file
if ($post->featured_image) {
Storage::disk('public')->delete($post->featured_image);
}
$post->delete();
return redirect()
->route('posts.index')
->with('success', 'Post deleted successfully.');
}
}
Artisan Commands
// Custom command generation
// php artisan make:command ProcessPosts
// app/Console/Commands/ProcessPosts.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Post;
use App\Models\User;
use Illuminate\Support\Facades\Mail;
class ProcessPosts extends Command
{
protected $signature = 'posts:process
{--user= : Process only specific user}
{--days=30 : Days to process}
{--dry-run : Show results without actual processing}';
protected $description = 'Process posts for periodic maintenance';
public function handle()
{
$this->info('Starting post processing...');
$days = $this->option('days');
$userId = $this->option('user');
$dryRun = $this->option('dry-run');
// Initialize progress bar
$query = Post::where('created_at', '<=', now()->subDays($days));
if ($userId) {
$query->where('user_id', $userId);
}
$posts = $query->get();
$bar = $this->output->createProgressBar($posts->count());
$processed = 0;
$errors = 0;
foreach ($posts as $post) {
try {
if (!$dryRun) {
// Actual processing
$this->processPost($post);
}
$processed++;
if ($this->output->isVerbose()) {
$this->line("\nProcessed: {$post->title}");
}
} catch (\Exception $e) {
$errors++;
$this->error("\nError: {$post->title} - {$e->getMessage()}");
}
$bar->advance();
}
$bar->finish();
// Display results
$this->newLine(2);
$this->info("Processing complete:");
$this->table(
['Item', 'Count'],
[
['Total', $posts->count()],
['Processed', $processed],
['Errors', $errors],
['Dry Run', $dryRun ? 'Yes' : 'No']
]
);
// User confirmation
if ($errors > 0 && $this->confirm('Show error details?')) {
$this->info('Please check error logs.');
}
return $errors > 0 ? 1 : 0;
}
private function processPost(Post $post)
{
// Post processing logic
if (!$post->published && $post->created_at->diffInDays() > 30) {
$post->delete();
$this->warn("Deleted draft post: {$post->title}");
}
}
}
// Job creation and execution
// php artisan make:job SendNewsletterJob
// app/Jobs/SendNewsletterJob.php
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Models\User;
use App\Mail\Newsletter;
use Illuminate\Support\Facades\Mail;
class SendNewsletterJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $newsletter;
protected $users;
public function __construct($newsletter, $users)
{
$this->newsletter = $newsletter;
$this->users = $users;
}
public function handle()
{
foreach ($this->users as $user) {
Mail::to($user)->queue(new Newsletter($this->newsletter));
}
}
}
// Schedule configuration
// app/Console/Kernel.php
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
protected $commands = [
Commands\ProcessPosts::class,
];
protected function schedule(Schedule $schedule)
{
// Run daily at 2 AM
$schedule->command('posts:process')->dailyAt('02:00');
// Run weekly on Monday
$schedule->command('posts:process --days=7')
->weeklyOn(1, '03:00');
// System maintenance
$schedule->command('model:prune')->daily();
$schedule->command('queue:work --stop-when-empty')->everyMinute();
// Backup
$schedule->command('backup:run')->daily();
}
protected function commands()
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}
// Artisan command execution
// php artisan posts:process
// php artisan posts:process --user=1 --days=7 --dry-run
// php artisan posts:process --help
// php artisan queue:work
// php artisan schedule:run
// php artisan schedule:list