Skip to content

第 6-11 章速查(基础篇精华浓缩)

本文档定位:把基础篇 6 个章节的核心 API、模式、踩坑点压缩成可随手翻的速查表

不是教学,是索引——遇到具体问题时翻这里找代码片段,需要深入理解时去看 docs/13-博客实战完整复盘.md

目标读者:写过 ThinkPHP,已经入门 Laravel,需要"老手快查"的人。


0. 全局速查(一图看懂 6-11 章)

6 章核心抽象的"职责分工"

HTTP 请求 ──▶ 路由 (web.php)

                ├─▶ 中间件链  ──▶ 鉴权 / CSRF / 限流
                │   (第 10 章)


            Controller (第 9 章)

                ├─▶ Form Request ──▶ 验证 + 授权
                │   (第 9 章)

                ├─▶ Policy ──▶ 资源级权限判断
                │   (第 11 章)

                ├─▶ Eloquent Model ──▶ 数据库 (Migration 第 8 章)
                │   (第 7 章)


            View (Blade) (第 10 章)


            HTTP 响应

6 章对应的"核心文件位置"

抽象文件位置创建命令
6路由routes/web.php / routes/api.php(手写)
7Eloquent Modelapp/Models/{Name}.phpphp artisan make:model {Name}
8Migrationdatabase/migrations/{ts}_*.phpphp artisan make:migration create_xxx_table
8Factorydatabase/factories/{Name}Factory.phpphp artisan make:factory {Name}Factory
8Seederdatabase/seeders/{Name}Seeder.phpphp artisan make:seeder {Name}Seeder
9Controllerapp/Http/Controllers/{Name}Controller.phpphp artisan make:controller {Name}Controller --resource
9Form Requestapp/Http/Requests/{Name}Request.phpphp artisan make:request Store{Name}Request
10Blade 视图resources/views/**/*.blade.php(手写)
10Blade 组件resources/views/components/**/*.blade.phpphp artisan make:component {Name}
10中间件app/Http/Middleware/{Name}.phpphp artisan make:middleware {Name}
11Policyapp/Policies/{Name}Policy.phpphp artisan make:policy {Name}Policy --model={Name}

"改一个功能要改哪些文件"对照表

想做的事要改/新增的文件(按依赖顺序)
加新模型 XMigration → Model → Factory → Seeder → Policy → FormRequest(Store/Update)→ Controller → 路由 → 5 个 Blade 视图 → 测试
加新字段Migration(新增)→ Model $fillable → Form Request rules → 视图表单 → 测试
加新业务条件Model 加 scope → Controller 调用 scope → 视图(如有需要)
加新权限规则Policy 加方法 → Controller authorizeResource() 已注册则自动生效,否则 $this->authorize()
加新页面路由 → Controller 方法 → Blade 视图

6. 路由系统速查

6.1 路由定义 7 种写法速查

写法代码
GET 路由Route::get('/posts', [PostController::class, 'index']);
POST 路由Route::post('/posts', [PostController::class, 'store']);
全部 RESTfulRoute::resource('posts', PostController::class);
仅部分 RESTfulRoute::resource('posts', PostController::class)->only(['index', 'show']);
排除部分 RESTfulRoute::resource('posts', PostController::class)->except(['destroy']);
API RESTful(无 create/edit)Route::apiResource('posts', PostController::class);
嵌套资源Route::resource('posts.comments', CommentController::class);

6.2 Route::resource = 7 条路由

php
Route::resource('posts', PostController::class);

等价于:

HTTP 方法URL路由名Controller 方法
GET/postsposts.indexindex()
GET/posts/createposts.createcreate()
POST/postsposts.storestore()
GET/posts/{post}posts.showshow($post)
GET/posts/{post}/editposts.editedit($post)
PUT/PATCH/posts/{post}posts.updateupdate($post)
DELETE/posts/{post}posts.destroydestroy($post)

6.3 路由分组 4 种修饰符

php
Route::middleware('auth')->prefix('admin')->name('admin.')->group(function () {
    Route::get('/dashboard', [AdminController::class, 'index'])->name('dashboard');
    // 实际路由:GET /admin/dashboard,名字 admin.dashboard
});
修饰符作用
middleware('auth')整组套中间件
prefix('admin')URL 加前缀
name('admin.')路由名加前缀(注意结尾的点)
controller(PostController::class)整组共用一个 Controller,方法名直接写

6.4 命名路由 + 反查

php
// 定义
Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');

// 反查(视图 / Controller / 测试通用)
route('posts.show', $post)              // /posts/my-first-post(用 slug)
route('posts.show', ['post' => $post])  // 同上,显式传参
route('posts.show', $post, false)       // 相对路径不带域名

6.5 路由模型绑定

默认(按 id)

php
Route::get('/posts/{post}', [PostController::class, 'show']);
// URL: /posts/1 → 自动 Post::findOrFail(1) 注入到 show(Post $post)

按 slug

在 Model 里:

php
public function getRouteKeyName(): string
{
    return 'slug';
}

→ URL 自动变成 /posts/my-first-post,自动 Post::where('slug', 'my-first-post')->firstOrFail()

显式绑定(多个键)

php
Route::get('/posts/{post:slug}', [PostController::class, 'show']);
// 直接在路由参数指定按 slug 解析

6.6 路由顺序的坑 ⚠️

php
// ❌ 错误顺序
Route::resource('posts', PostController::class);  // 先注册了 /posts/{post}
Route::get('/posts/featured', [PostController::class, 'featured']);  // 永远到不了

// ✓ 正确顺序
Route::get('/posts/featured', [PostController::class, 'featured']);  // 具体路由先
Route::resource('posts', PostController::class);                     // 通配路由后

→ Laravel 按定义顺序匹配路由,第一条匹配就停。具体路由必须放在通配路由前

6.7 自检命令

bash
php artisan route:list                 # 列所有路由
php artisan route:list --path=posts    # 按路径筛选
php artisan route:list --name=posts.   # 按路由名筛选
php artisan route:list --method=POST   # 按方法筛选
php artisan route:list --except-vendor # 排除框架自带路由

→ AI 改完路由后让它跑这条命令自检,比 Boost 的任何工具都直接。

6.8 ThinkPHP vs Laravel 路由对照

ThinkPHP 6Laravel 12
Route::rule('hello', 'Hello/index')Route::get('/hello', [HelloController::class, 'index'])
Route::rule('hello', 'Hello/index', 'POST')Route::post('/hello', [HelloController::class, 'index'])
Route::resource('blog', 'Blog')Route::resource('blog', BlogController::class)
Route::group('admin', function() {...})Route::prefix('admin')->group(function() {...})
Route::get('hello', ...)->middleware('auth')一致

配套实战第 13 章 §3.4 路由设计 含完整的 11 条路由配置示例。


7. Eloquent ORM 速查

7.1 Model 标准模板

php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Post extends Model
{
    use HasFactory;

    protected $fillable = ['title', 'slug', 'body', 'user_id', 'published_at'];

    protected function casts(): array         // ⚠️ Laravel 11+ 用方法签名
    {
        return ['published_at' => 'datetime'];
    }

    public function getRouteKeyName(): string  // 自定义路由键
    {
        return 'slug';
    }

    public function author(): BelongsTo
    {
        return $this->belongsTo(User::class, 'user_id');
    }

    public function scopePublished(Builder $query): Builder
    {
        return $query->whereNotNull('published_at')->where('published_at', '<=', now());
    }
}

7.2 CRUD 操作速查

查(Read)

php
Post::all();                                    // 全部
Post::find(1);                                  // 按 id 找,找不到返 null
Post::findOrFail(1);                            // 找不到抛 404
Post::where('user_id', 1)->get();               // 条件查询
Post::where('user_id', 1)->first();             // 首条
Post::where('user_id', 1)->firstOrFail();       // 首条找不到抛 404
Post::where('user_id', 1)->count();             // 计数
Post::where('user_id', 1)->exists();            // 存在性
Post::where('user_id', 1)->paginate(10);        // 分页
Post::latest()->take(5)->get();                 // 最新 5 条
Post::orderBy('title')->get();                  // 排序
Post::with('author')->get();                    // eager load 防 N+1
Post::with(['author', 'comments'])->get();      // 多个关系
Post::withCount('comments')->get();             // 加 comments_count 字段
Post::published()->get();                       // 调 scope

增(Create)

php
Post::create(['title' => 'X', 'slug' => 'x', 'body' => '...', 'user_id' => 1]);
// 等价于:
$post = new Post();
$post->fill([...])->save();

// 关系链创建(自动设置外键)⭐ 安全做法
$user->posts()->create(['title' => 'X', ...]);  // user_id 自动注入

改(Update)

php
$post->update(['title' => 'New title']);          // 单个
Post::where('user_id', 1)->update(['public' => false]);  // 批量

// 不存在则建
Post::updateOrCreate(
    ['slug' => 'my-post'],     // 查找条件
    ['title' => '...', ...]    // 更新/创建数据
);

删(Delete)

php
$post->delete();                          // 单个
Post::destroy(1);                         // 按 id
Post::destroy([1, 2, 3]);                 // 多个
Post::where('user_id', 1)->delete();      // 条件批量

7.3 关联关系 4 大类型

关系描述Laravel 写法
一对一User 有 1 个 ProfilehasOne(Profile::class)
一对一反向Profile 属于 1 个 UserbelongsTo(User::class)
一对多User 有多个 PosthasMany(Post::class)
一对多反向Post 属于 1 个 UserbelongsTo(User::class)
多对多Post 有多个 TagbelongsToMany(Tag::class)
远跨关系User → Post → CommenthasManyThrough(Comment::class, Post::class)
多态一对多Post / Video 都有 CommentsmorphMany(Comment::class, 'commentable')

命名陷阱 ⚠️

php
// 方法名 = 字段前缀 → 不需要显式传外键
public function user(): BelongsTo
{
    return $this->belongsTo(User::class);   // 默认找 user_id
}

// 方法名 ≠ 字段前缀 → 必须显式传第二个参数
public function author(): BelongsTo
{
    return $this->belongsTo(User::class, 'user_id');
}

7.4 Local Scope(业务条件复用)

定义

php
class Post extends Model
{
    public function scopePublished(Builder $query): Builder
    {
        return $query->whereNotNull('published_at')->where('published_at', '<=', now());
    }

    public function scopeOfAuthor(Builder $query, int $userId): Builder
    {
        return $query->where('user_id', $userId);
    }
}

使用(省略 scope 前缀

php
Post::published()->get();
Post::ofAuthor(1)->published()->get();      // 链式调用
$user->posts()->published()->get();         // 关系上调用

7.5 N+1 查询防御

问题代码

php
$posts = Post::all();
foreach ($posts as $post) {
    echo $post->author->name;   // 每次循环 1 次 SQL → N+1
}

修复

php
$posts = Post::with('author')->get();   // 1 + 1 = 2 次 SQL

检测 N+1(推荐开发期开启)

php
// app/Providers/AppServiceProvider.php
use Illuminate\Database\Eloquent\Model;

public function boot(): void
{
    Model::preventLazyLoading(! $this->app->isProduction());
    // 开发环境下 lazy load 直接抛异常,强制你 eager load
}

7.6 ThinkPHP vs Laravel ORM 对照

ThinkPHP 6Laravel 12
Db::name('posts')->select()Post::all()
Db::name('posts')->where('id', 1)->find()Post::find(1)
Db::name('posts')->where('id', 1)->update(...)Post::where('id', 1)->update(...)$post->update(...)
Post::create([...])一致
$post->user (关联)一致
软删除 trait SoftDeletetrait SoftDeletes + Migration softDeletes()
Db::raw()DB::raw()
全局查询作用域Eloquent Global Scope(更统一)

配套实战第 13 章 §2 数据层 含完整 Post Model + 3 个 scope + 关系示例。


8. Migration + Artisan 速查

8.1 Migration 字段类型速查

php
Schema::create('posts', function (Blueprint $table) {
    $table->id();                                 // bigint primary key
    $table->string('title', 200);                 // VARCHAR(200)
    $table->string('slug')->unique();             // VARCHAR + 唯一索引
    $table->text('summary')->nullable();          // TEXT
    $table->longText('body');                     // LONGTEXT
    $table->integer('views')->default(0);         // INT
    $table->decimal('price', 10, 2);              // DECIMAL(10,2)
    $table->boolean('is_featured')->default(false); // TINYINT
    $table->json('meta');                         // JSON
    $table->date('publish_date');                 // DATE
    $table->dateTime('event_at');                 // DATETIME
    $table->timestamp('published_at')->nullable(); // TIMESTAMP
    $table->softDeletes();                        // deleted_at TIMESTAMP nullable
    $table->timestamps();                         // created_at + updated_at
});

8.2 索引和外键

php
$table->index('user_id');                          // 普通索引
$table->unique('email');                           // 唯一索引
$table->index(['user_id', 'created_at']);          // 复合索引

// 现代外键写法(Laravel 7+)
$table->foreignId('user_id')
    ->constrained()                  // 自动按命名找 users 表
    ->cascadeOnDelete();             // 删除用户时级联删除 posts

// 等价老写法
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');

链式约束修饰符

修饰符作用
nullable()允许 null
default($value)默认值
unique()唯一约束
index()普通索引
unsigned()无符号(数值类型)
comment('xxx')字段注释
after('column')加在某字段之后(仅 MySQL)
change()修改字段(需 doctrine/dbal 包)

8.3 Migration 生命周期命令

bash
# 创建 migration
php artisan make:migration create_posts_table
php artisan make:migration add_views_to_posts_table --table=posts
php artisan make:migration drop_old_field --table=posts

# 跑 migration
php artisan migrate                    # 跑未执行的
php artisan migrate --pretend          # 干跑(仅打印 SQL 不执行)
php artisan migrate:status             # 查看哪些跑过
php artisan migrate:rollback           # 回滚最后一批
php artisan migrate:rollback --step=2  # 回滚 2 批
php artisan migrate:reset              # 回滚全部
php artisan migrate:refresh            # reset + migrate
php artisan migrate:fresh              # drop 所有表 + migrate
php artisan migrate:fresh --seed       # drop + migrate + seed ⭐

8.4 Factory 模板

php
<?php

namespace Database\Factories;

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;

class PostFactory extends Factory
{
    public function definition(): array
    {
        return [
            'title' => fake()->sentence(6),
            'slug' => fake()->unique()->slug(),
            'body' => fake()->paragraphs(5, true),
            'user_id' => User::factory(),                       // 自动建 User
            'published_at' => fake()->dateTimeBetween('-1 year', '-1 hour'),
        ];
    }

    public function draft(): static
    {
        return $this->state(fn () => ['published_at' => null]);
    }

    public function scheduled(): static
    {
        return $this->state(fn () => [
            'published_at' => fake()->dateTimeBetween('+1 hour', '+1 month'),
        ]);
    }
}

8.5 Factory 调用方式

php
Post::factory()->create();                           // 单个,写库
Post::factory()->make();                             // 单个,不写库
Post::factory()->raw();                              // 仅返回属性数组

Post::factory()->count(10)->create();                // 10 个
Post::factory()->count(10)->create(['user_id' => 1]); // 全部覆盖 user_id

Post::factory()->draft()->create();                  // 用 state
Post::factory()->scheduled()->count(3)->create();    // state + count

// 关系链
Post::factory()->for($user, 'author')->create();     // belongsTo 端
User::factory()->has(Post::factory()->count(3))->create();  // hasMany 端

8.6 Seeder 幂等模板(生产可用)

php
<?php

namespace Database\Seeders;

use App\Models\User;
use App\Models\Post;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        $admin = User::firstOrCreate(
            ['email' => 'admin@example.com'],
            User::factory()->raw(['name' => 'Admin', 'email' => 'admin@example.com'])
        );

        if ($admin->wasRecentlyCreated) {                // 仅初次创建时 seed 关联数据
            Post::factory()->count(5)->for($admin, 'author')->create();
        }
    }
}

→ 跑多少次 php artisan db:seed 都不会因 UNIQUE 报错。

8.7 Artisan 命令速查(按使用频率)

高频(每天用)

bash
php artisan serve                # 启动开发服务器
php artisan tinker               # REPL 交互
php artisan test                 # 跑测试
php artisan route:list           # 列路由
php artisan migrate              # 跑 migration

中频

bash
php artisan make:model Post -mfsr    # 一次创建 Model + Migration + Factory + Seeder + Resource Controller
php artisan make:controller PostController --resource
php artisan make:migration create_posts_table
php artisan make:request StorePostRequest
php artisan make:policy PostPolicy --model=Post
php artisan make:component layout
php artisan make:test PostTest

维护

bash
php artisan optimize             # 缓存路由 + 配置 + 视图
php artisan optimize:clear       # 清所有缓存
php artisan config:cache         # 缓存配置(生产)
php artisan config:clear
php artisan storage:link         # 建立 storage/app/public 软链
php artisan db:seed
php artisan db:wipe              # ⚠️ 删所有表

8.8 Tinker 使用速查

bash
php artisan tinker
php
>>> User::count()
=> 6

>>> $user = User::find(1)
>>> $user->posts()->count()

>>> Post::factory()->create()    // 临时造数据

>>> exit                          // 退出

⚠️ Tinker 是长进程——seed 后查不到数据,重启 Tinker 拿新连接。

8.9 ThinkPHP vs Laravel Migration 对照

ThinkPHP 6Laravel 12
php think migrate:createphp artisan make:migration
php think migrate:runphp artisan migrate
php think migrate:rollback一致
没有 Factory 概念php artisan make:factory + Faker
没有 Seeder 标准化php artisan make:seeder + db:seed
php think tinker(不存在)php artisan tinker(必备)

配套实战第 13 章 §2 数据层 含完整 Migration / Factory / 幂等 Seeder 实例。


9. Controller + Form Request 速查

9.1 Resource Controller 标准模板(Laravel 12)

php
<?php

namespace App\Http\Controllers;

use App\Http\Requests\StorePostRequest;
use App\Http\Requests\UpdatePostRequest;
use App\Models\Post;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Http\RedirectResponse;
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;
use Illuminate\View\View;

class PostController extends Controller implements HasMiddleware
{
    use AuthorizesRequests;

    public static function middleware(): array              // ⚠️ Laravel 11+ 写法
    {
        return [
            new Middleware('auth', except: ['index', 'show']),
        ];
    }

    public function __construct()
    {
        $this->authorizeResource(Post::class, 'post');      // ⭐ 一行注册 7 个方法的授权
    }

    public function index(): View
    {
        $posts = Post::published()->with('author')->latest('published_at')->paginate(10);
        return view('posts.index', compact('posts'));
    }

    public function create(): View
    {
        return view('posts.create');
    }

    public function store(StorePostRequest $request): RedirectResponse
    {
        $post = $request->user()->posts()->create($request->validated());  // ⭐ 关系链创建
        return redirect()->route('posts.show', $post)->with('status', 'Post created.');
    }

    public function show(Post $post): View
    {
        return view('posts.show', compact('post'));
    }

    public function edit(Post $post): View
    {
        return view('posts.edit', compact('post'));
    }

    public function update(UpdatePostRequest $request, Post $post): RedirectResponse
    {
        $post->update($request->validated());
        return redirect()->route('posts.show', $post)->with('status', 'Post updated.');
    }

    public function destroy(Post $post): RedirectResponse
    {
        $post->delete();
        return redirect()->route('posts.index')->with('status', 'Post deleted.');
    }
}

9.2 Form Request 标准模板

Store(创建)

php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StorePostRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;                  // ⭐ 授权交给 Policy,这里默认放行
    }

    public function rules(): array
    {
        return [
            'title' => ['required', 'string', 'max:200'],
            'slug'  => ['required', 'string', 'max:255', 'unique:posts,slug'],
            'body'  => ['required', 'string'],
            'published_at' => ['nullable', 'date'],
        ];
    }

    public function messages(): array      // 可选:自定义错误消息
    {
        return [
            'title.required' => '标题是必填的',
        ];
    }

    public function attributes(): array    // 可选:自定义字段名
    {
        return ['title' => '文章标题'];
    }
}

Update(更新)

php
use Illuminate\Validation\Rule;

class UpdatePostRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'title' => ['required', 'string', 'max:200'],
            'slug'  => [
                'required', 'string', 'max:255',
                Rule::unique('posts', 'slug')->ignore($this->route('post')),
            ],
            'body'  => ['required', 'string'],
            'published_at' => ['nullable', 'date'],
        ];
    }
}

9.3 验证规则速查(最常用 30 个)

必填类

规则含义
required必填
required_if:field,value当 field=value 时必填
required_unless:field,value当 field≠value 时必填
required_with:f1,f2f1 或 f2 任一存在时必填
nullable允许 null
sometimes仅当字段存在时校验

类型类

规则含义
string字符串
integer整数
numeric数字(含小数)
booleantrue/false/1/0/'1'/'0'
array数组
json合法 JSON 字符串
date合法日期
email邮箱
urlURL
uuidUUID
ipIP 地址

格式类

规则含义
min:N字符串长度 / 数字 / 数组元素数 ≥ N
max:N≤ N
between:M,N在 M-N 之间
size:N严格等于 N
regex:/^\d+$/正则匹配
alpha仅字母
alpha_num字母+数字
alpha_dash字母+数字+下划线+连字符

数据库类

规则含义
unique:posts,slugposts 表 slug 字段唯一
unique:posts,slug,{id},id唯一但忽略 id 字段为 {id} 的记录
exists:users,idusers 表 id 字段必须存在该值

文件类

规则含义
image是图片(jpg/jpeg/png/bmp/gif/svg/webp)
mimes:jpg,png限定 MIME
max:2048最大 KB(注意单位)
dimensions:min_width=100,min_height=200图片尺寸

关联类

规则含义
confirmed字段需要有 字段_confirmation 同值(密码确认场景)
same:field与 field 相同
different:field与 field 不同
in:a,b,c必须是其中之一
not_in:a,b,c不能是其中之一

9.4 错误处理与回填

Controller 端无需手动处理

php
public function store(StorePostRequest $request)
{
    // 验证失败时,Laravel 自动 redirect()->back()
    //   ->withErrors($validator)
    //   ->withInput()
    // ↑ 不需要任何代码
}

Blade 端读取错误和旧值

blade
<input type="text" name="title" value="{{ old('title', $post?->title) }}">
@error('title')
    <p class="text-red-600">{{ $message }}</p>
@enderror

{{-- 检查所有错误 --}}
@if ($errors->any())
    <ul>
        @foreach ($errors->all() as $error)
            <li>{{ $error }}</li>
        @endforeach
    </ul>
@endif

9.5 Controller 的 4 种"接收数据"姿势

姿势适用场景
$request->input('field')取单个字段,可设默认值
$request->only(['a', 'b'])取指定字段,返回数组
$request->except(['password'])排除字段
$request->validated()⭐ 用 Form Request 时取所有验证过的字段

优先用 validated()——保证拿到的数据已经过验证,不会有意外字段。

9.6 ThinkPHP vs Laravel Controller 对照

ThinkPHP 6Laravel 12
extends Controllerextends Controller
Validate 类放 app/validate/FormRequestapp/Http/Requests/
validate(...) 在 controller 调用type-hint Form Request 自动验证
手写 if (!$result) abort(403)$this->authorize('action', $model)
没有"标准 RESTful 控制器"约定7 方法约定(index/show/create/store/edit/update/destroy)

配套实战第 13 章 §3 HTTP 层 含 PostController + StorePostRequest + UpdatePostRequest 完整实例。


10. Blade + 中间件速查

10.1 Blade 语法速查(一表打尽)

语法作用
{{ $var }}输出(自动 HTML 转义)
{!! $var !!}输出原始 HTML(慎用,XSS 风险)
{{ $var ?? 'default' }}null coalescing
{{-- 注释 --}}Blade 注释(不输出到 HTML)
<!-- 注释 -->HTML 注释(输出到 HTML)
@if / @elseif / @else / @endif条件
@unless ... @endunless反向条件
@isset($var) ... @endisset变量已定义
@empty($var) ... @endempty变量为空
@for / @foreach / @while / @forelse循环
@switch / @case / @default / @endswitch分支
@php ... @endphp内联 PHP(少用
@auth ... @else ... @endauth已登录/游客
@guest ... @endguest仅游客
@can('action', $model)权限判断(调 Policy)
@cannot('action', $model)反向权限
@error('field') {{ $message }} @enderror验证错误显示
@csrfCSRF token 隐藏字段
@method('DELETE')HTTP method spoofing
@include('partial')包含子视图
@yield('content')占位(旧布局方式)
@section('content') ... @endsection填充 yield
@extends('layout')继承(旧布局方式)
@props([...])组件参数定义(新组件方式)
{{ $slot }}组件默认插槽

10.2 @forelse 处理空状态(强烈推荐)

blade
@forelse ($posts as $post)
    <article>{{ $post->title }}</article>
@empty
    <p>没有文章</p>
@endforelse

→ 比"先 @if (count($posts))@foreach"清晰得多。

10.3 现代组件(推荐 over 旧布局)

定义组件 resources/views/components/layout.blade.php

blade
@props(['title' => null])

<!DOCTYPE html>
<html>
<head>
    <title>{{ $title ?? 'My Site' }}</title>
</head>
<body>
    {{ $slot }}
</body>
</html>

使用

blade
<x-layout title="Posts">
    <h1>Posts</h1>
    <p>Hello world</p>
</x-layout>

→ Tailwind class 也可以传:<x-layout class="bg-gray-50"> 然后内部用 {{ $attributes->merge([...]) }}

10.4 命名插槽

blade
{{-- 定义 --}}
<header>{{ $header }}</header>
<main>{{ $slot }}</main>
<footer>{{ $footer }}</footer>

{{-- 使用 --}}
<x-card>
    <x-slot:header>标题</x-slot:header>
    主内容
    <x-slot:footer>页脚</x-slot:footer>
</x-card>

10.5 视图辅助函数

函数作用
view('posts.index', ['posts' => $posts])渲染视图
route('posts.show', $post)反查 URL
url('/posts')生成绝对 URL
asset('images/logo.png')资源 URL(自动加 /public
old('field', $default)旧表单值
csrf_field()CSRF 隐藏字段(推荐用 @csrf
auth()->user()当前登录用户(未登录返 null)
auth()->id()当前用户 id
session('key')取 session
request()->routeIs('posts.*')当前路由名是否匹配
Str::limit($body, 150)截字 + ...
Str::slug($title)转 URL slug
now() / today()当前 Carbon 时间

10.6 中间件三步走

Step 1:创建

bash
php artisan make:middleware EnsureUserIsActive

Step 2:写逻辑

php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class EnsureUserIsActive
{
    public function handle(Request $request, Closure $next)
    {
        if (! $request->user()?->is_active) {
            abort(403, 'Account is inactive');
        }
        return $next($request);
    }
}

Step 3:注册(Laravel 11+ 在 bootstrap/app.php

php
// bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->alias([
            'active' => \App\Http\Middleware\EnsureUserIsActive::class,
        ]);

        // 或者全局:
        // $middleware->web(append: [\App\Http\Middleware\EnsureUserIsActive::class]);
    })
    ->create();

Step 4:使用

php
// 路由
Route::get('/dashboard', ...)->middleware('active');
Route::middleware(['auth', 'active'])->group(function () { ... });

// Controller(HasMiddleware 接口)
public static function middleware(): array
{
    return [
        'auth',
        new Middleware('active', only: ['create', 'store']),
    ];
}

10.7 内置中间件速查

中间件作用
auth必须登录
auth:sanctumSanctum token 已验证
guest必须未登录
verified邮箱已验证
signed签名 URL 校验
throttle:60,1限流(60 次/分钟)
password.confirm必须最近确认过密码
cache.headers:public;max_age=2628000;etag响应缓存头

10.8 ThinkPHP vs Laravel 视图对照

ThinkPHP 6Laravel 12
{$var}{{ $var }}
{:url('xxx')}{{ route('xxx') }}
{volist name="..." id="..."}@foreach (... as ...)
{include file="..."}@include(...)<x-component />
display('xxx')view('xxx')
{:date('Y-m-d', $time)}{{ $time->format('Y-m-d') }}(Carbon)
中间件叫 behavior中间件叫 middleware

配套实战第 13 章 §4 视图层 含完整 5 视图 + 共享组件示例。


11. 认证系统速查

11.1 认证场景速查表

场景推荐方案装哪个包
单体应用 + Web 表单登录手写或 Breezelaravel/breeze --dev
API + 移动端 + TokenSanctumlaravel/sanctum
API + 第三方应用 + OAuth2Passportlaravel/passport
后台管理 + 完整脚手架Filament 自带认证filament/filament
SPA(Vue/React 同域)Sanctum SPA 模式laravel/sanctum
复杂权限(角色/权限)Spatie Permissionspatie/laravel-permission

11.2 手写 Demo 登录(最小可用版)

路由

php
Route::get('/login', [AuthController::class, 'showLogin'])->name('login');
Route::post('/login', [AuthController::class, 'login']);
Route::post('/logout', [AuthController::class, 'logout'])->name('logout');

Controller

php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\View\View;

class AuthController extends Controller
{
    public function showLogin(): View
    {
        return view('auth.login');
    }

    public function login(Request $request): RedirectResponse
    {
        $credentials = $request->validate([
            'email' => ['required', 'email'],
            'password' => ['required'],
        ]);

        if (Auth::attempt($credentials, $request->boolean('remember'))) {
            $request->session()->regenerate();              // ⭐ 防 session fixation
            return redirect()->intended(route('posts.index'));  // ⭐ 回跳原始访问页
        }

        return back()
            ->withErrors(['email' => 'Invalid credentials.'])
            ->onlyInput('email');                            // ⭐ 不回填 password
    }

    public function logout(Request $request): RedirectResponse
    {
        Auth::logout();
        $request->session()->invalidate();
        $request->session()->regenerateToken();              // ⭐ 三件套缺一不可
        return redirect()->route('posts.index');
    }
}

视图(核心)

blade
<form method="POST" action="{{ route('login') }}">
    @csrf
    <input type="email" name="email" value="{{ old('email') }}">
    <input type="password" name="password">
    <input type="checkbox" name="remember">
    <button type="submit">Login</button>
</form>

{{-- Logout 必须用 POST,不能用 a 标签!--}}
<form method="POST" action="{{ route('logout') }}">
    @csrf
    <button type="submit">Logout</button>
</form>

11.3 Auth Facade 常用方法

php
Auth::attempt(['email' => 'x', 'password' => 'y']);  // 尝试登录
Auth::login($user);                                   // 直接登录某个用户
Auth::loginUsingId(1);                                // 按 id 登录
Auth::logout();                                       // 退出
Auth::user();                                         // 当前用户(未登录返 null)
Auth::id();                                           // 当前用户 id
Auth::check();                                        // 是否已登录
Auth::guest();                                        // 是否游客
Auth::viaRemember();                                  // 是否通过 remember cookie 登录

→ 也可用全局函数 auth()->user() / auth()->id()

11.4 Policy 标准模板

bash
php artisan make:policy PostPolicy --model=Post
php
namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    public function viewAny(?User $user): bool          // 列表页(允许游客)
    {
        return true;
    }

    public function view(?User $user, Post $post): bool // 详情页
    {
        if ($post->published_at?->isPast()) {
            return true;                                 // 已发布任何人可看
        }
        return $user !== null && $user->id === $post->user_id;
    }

    public function create(User $user): bool
    {
        return true;                                     // 任何登录用户可创建
    }

    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;             // 只有作者可改
    }

    public function delete(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }

    public function restore(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }

    public function forceDelete(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }
}

11.5 Policy 使用 4 种方式

Controller(推荐)

php
public function __construct()
{
    $this->authorizeResource(Post::class, 'post');  // 一行注册全部 7 个 RESTful action
}

// 或单独调用
public function update(Post $post)
{
    $this->authorize('update', $post);
    // ...
}

Form Request

php
public function authorize(): bool
{
    return $this->user()->can('update', $this->route('post'));
}

Blade

blade
@can('update', $post)
    <a href="{{ route('posts.edit', $post) }}">Edit</a>
@endcan

@cannot('delete', $post)
    <p>You can't delete this</p>
@endcannot

User 模型(直接 can())

php
if (auth()->user()->can('update', $post)) {
    // ...
}

11.6 ?User vs User 类型签名(关键)

签名含义
view(?User $user, ...)允许游客($user 可能为 null)
view(User $user, ...)必须登录(如果游客,框架直接 deny,不调这个方法)

→ 这是 Laravel 11+ 的"用类型表达业务规则"——记住 ? 就是"游客可访问"。

11.7 Sanctum API Token 速查(第 14 章详细做)

装包

bash
composer require laravel/sanctum
php artisan install:api               # Laravel 11+ 一键装

User 模型加 trait

php
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, ...;
}

颁发 Token

php
$token = $user->createToken('mobile-app')->plainTextToken;
return ['token' => $token];

路由保护

php
Route::middleware('auth:sanctum')->get('/api/me', function (Request $request) {
    return $request->user();
});

客户端调用

bash
curl -H "Authorization: Bearer {token}" https://api.example.com/me

11.8 ThinkPHP vs Laravel 认证对照

ThinkPHP 6Laravel 12
自己写 / 用 ThinkAuth 包内置 Auth Facade,开箱可用
自己写 RBAC 表用 Spatie Permission 或 Policy
session('user') 取用户auth()->user()
自己生成 TokenSanctum 一行 createToken()
中间件检查登录内置 auth 中间件

配套实战第 13 章 §5 认证层 含手写 Demo 登录 + 4 个安全细节实战。


附:50 个 ThinkPHP → Laravel 一对一翻译表

路由

ThinkPHP 6Laravel 12
Route::get('hello', 'Hello/index')Route::get('/hello', [HelloController::class, 'index'])
Route::resource('blog', 'Blog')Route::resource('blog', BlogController::class)
Route::group('admin', fn() => ...)Route::prefix('admin')->group(fn() => ...)
url('Hello/index')route('hello.index')

Controller

ThinkPHP 6Laravel 12
class IndexController extends BaseControllerclass IndexController extends Controller
Request::param('name')$request->input('name')
Request::post()$request->all()
redirect('/login')redirect('/login')
redirect()->route('xxx')redirect()->route('xxx')
json([...])response()->json([...])
view('index', $data)view('index', $data)

数据库

ThinkPHP 6Laravel 12
Db::name('users')->select()User::all()
Db::name('users')->where('id', 1)->find()User::find(1)
Db::table('users')->insert([...])User::create([...])DB::table('users')->insert(...)
Db::name('users')->where('id', 1)->update([...])User::where('id', 1)->update([...])
Db::name('users')->where('id', 1)->delete()User::destroy(1)
软删除 trait SoftDeletetrait SoftDeletes
Db::raw('count(*)')DB::raw('count(*)')
Db::transaction(fn() => ...)DB::transaction(fn() => ...)

Model

ThinkPHP 6Laravel 12
protected $table = 'posts'protected $table = 'posts'(一致)
protected $pk = 'id'protected $primaryKey = 'id'
protected $autoWriteTimestamp = true默认开启 timestamps
关联 belongsTo('User')belongsTo(User::class)
关联 hasMany('Post')hasMany(Post::class)
没有 scope 概念scopeXxx(Builder $query)
$model->save()$model->save()(一致)
$model->delete()$model->delete()(一致)

验证

ThinkPHP 6Laravel 12
Validate 类放 app/validate/FormRequestapp/Http/Requests/
validate(Validate::class)type-hint Form Request 自动注入
'name' => 'require|max:25''name' => ['required', 'max:25']
错误 validate->getError()$errors->first('name')

视图

ThinkPHP 6Laravel 12
{$var}{{ $var }}
{:url('xxx')}{{ route('xxx') }}
{volist name="..." id="vo"}@foreach (... as $vo)
{include file="..."}@include(...)<x-...>
{:date('Y-m-d', $time)}{{ $time->format('Y-m-d') }}
<eq name="...">@if (...) ... @endif

认证

ThinkPHP 6Laravel 12
自己写 / topthink/think-auth内置 Auth::attempt()
session('user_id')auth()->id()
Cache::remember('key', fn() => ..., 60)Cache::remember('key', 60, fn() => ...)

命令

ThinkPHP 6Laravel 12
php think runphp artisan serve
php think migrate:runphp artisan migrate
php think make:controller Hellophp artisan make:controller HelloController
php think make:model Userphp artisan make:model User
没有等价php artisan tinker
没有等价php artisan test
没有等价php artisan route:list

速查的速查(备查)

本节是"目录"——遇到问题时第一站。

我想做的事看哪一节
加一条路由§6.1 路由定义 7 种写法
用一行注册 7 个 RESTful 路由§6.2 Route::resource
路由按 slug 解析而不是 id§6.5 路由模型绑定
写 Eloquent 查询§7.2 CRUD 操作速查
防 N+1§7.5 N+1 查询防御
业务条件复用§7.4 Local Scope
加字段 / 改字段§8.1 Migration 字段类型速查
写测试用 Factory 造数据§8.4 Factory 模板
让 seed 不重复报错§8.6 Seeder 幂等模板
写一个 Controller§9.1 Resource Controller 标准模板
验证表单输入§9.2 Form Request 标准模板
编辑时 unique 不报错§9.2 Update 部分
错误回填到表单§9.4 错误处理与回填
用 Blade§10.1 Blade 语法速查
写一个组件§10.3 现代组件
让某些路由强制登录§10.6 中间件三步走 + auth 中间件
实现登录§11.2 手写 Demo 登录
加权限规则§11.4 Policy 标准模板
区分游客和登录用户的权限§11.6 ?User vs User 类型签名
做 API 认证§11.7 Sanctum API Token 速查
找 ThinkPHP 等价物§附录 50 个翻译表

本文档原则:能查到不要写、能写代码不要写文字、能列表不要写段落。

更新时机:每次实战遇到新模式 / 新踩坑,立即回填。

配套阅读

基于 MIT 许可 发布