Building Web Apps with Laravel
Laravel project structure, routing and controllers, Eloquent ORM, migrations and seeders, Blade templates, and the service container.
Project Skeleton and Artisan
composer create-project laravel/laravel shop
cd shop && php artisan serve # http://127.0.0.1:8000
# 代码生成器
php artisan make:model Article -mfsc
# -m 迁移 -f 工厂 -s 填充 -c 控制器
php artisan migrate # 执行迁移
php artisan db:seed # 填充测试数据
php artisan route:list # 查看全部路由
Routing and Controllers
<?php
// routes/web.php
use App\Http\Controllers\ArticleController;
Route::get('/articles', [ArticleController::class, 'index'])
->name('articles.index');
Route::get('/articles/{article}', [ArticleController::class, 'show'])
->name('articles.show');
// routes/api.php 自动加 /api 前缀与频率限制
Route::post('/articles', [ArticleController::class, 'store']);
// 控制器
class ArticleController extends Controller {
public function show(Article $article) { // 路由模型绑定
return view('articles.show', compact('article'));
}
}
Eloquent and Migrations
<?php
// database/migrations/xxxx_create_articles_table.php
public function up(): void {
Schema::create('articles', function (Blueprint $t) {
$t->id();
$t->string('title');
$t->text('body')->nullable();
$t->foreignId('user_id')->constrained()->cascadeOnDelete();
$t->timestamps();
});
}
// 模型
class Article extends Model {
protected $fillable = ['title', 'body'];
public function user(): BelongsTo { // 关联
return $this->belongsTo(User::class);
}
public function scopePublished($q) { // 查询作用域
return $q->where('published', true);
}
}
// 使用:避免 N+1 用 with() 预加载
$articles = Article::with('user')->published()->latest()->paginate(15);
The N+1 problem is the single most common Eloquent pitfall: enable Model::preventLazyLoading() in development to turn lazy loads into exceptions.
Blade and the Service Container
<?php
{{-- resources/views/articles/show.blade.php --}}
@extends('layouts.app')
@section('title', $article->title)
@section('content')
<h1>{{ $article->title }}</h1>
<p>by {{ $article->user->name }}</p>
@foreach ($article->comments as $c)
<p>{{ $c->body }}</p>
@endforeach
@endsection
<?php
// 服务容器:绑定接口到实现,控制器只声明类型即可
$this->app->bind(PaymentGateway::class, StripeGateway::class);
// 控制器中自动注入
public function store(Request $request, PaymentGateway $gateway) {
$data = $request->validate(['title' => 'required|max:200']);
$gateway->charge(100);
}项目骨架与 Artisan
composer create-project laravel/laravel shop
cd shop && php artisan serve # http://127.0.0.1:8000
# 代码生成器
php artisan make:model Article -mfsc
# -m 迁移 -f 工厂 -s 填充 -c 控制器
php artisan migrate # 执行迁移
php artisan db:seed # 填充测试数据
php artisan route:list # 查看全部路由
路由与控制器
<?php
// routes/web.php
use App\Http\Controllers\ArticleController;
Route::get('/articles', [ArticleController::class, 'index'])
->name('articles.index');
Route::get('/articles/{article}', [ArticleController::class, 'show'])
->name('articles.show');
// routes/api.php 自动加 /api 前缀与频率限制
Route::post('/articles', [ArticleController::class, 'store']);
// 控制器
class ArticleController extends Controller {
public function show(Article $article) { // 路由模型绑定
return view('articles.show', compact('article'));
}
}
Eloquent 与迁移
<?php
// database/migrations/xxxx_create_articles_table.php
public function up(): void {
Schema::create('articles', function (Blueprint $t) {
$t->id();
$t->string('title');
$t->text('body')->nullable();
$t->foreignId('user_id')->constrained()->cascadeOnDelete();
$t->timestamps();
});
}
// 模型
class Article extends Model {
protected $fillable = ['title', 'body'];
public function user(): BelongsTo { // 关联
return $this->belongsTo(User::class);
}
public function scopePublished($q) { // 查询作用域
return $q->where('published', true);
}
}
// 使用:避免 N+1 用 with() 预加载
$articles = Article::with('user')->published()->latest()->paginate(15);
N+1 是 Eloquent 最常见的坑:在开发环境开启 Model::preventLazyLoading(),把懒加载直接变成异常。
Blade 与服务容器
<?php
{{-- resources/views/articles/show.blade.php --}}
@extends('layouts.app')
@section('title', $article->title)
@section('content')
<h1>{{ $article->title }}</h1>
<p>by {{ $article->user->name }}</p>
@foreach ($article->comments as $c)
<p>{{ $c->body }}</p>
@endforeach
@endsection
<?php
// 服务容器:绑定接口到实现,控制器只声明类型即可
$this->app->bind(PaymentGateway::class, StripeGateway::class);
// 控制器中自动注入
public function store(Request $request, PaymentGateway $gateway) {
$data = $request->validate(['title' => 'required|max:200']);
$gateway->charge(100);
}