跳转到主要内容

什么是控制器

除了将所有请求处理逻辑作为闭包定义在路由文件中,你还可以使用「控制器」类来组织这些行为。 控制器可以把相关的请求处理逻辑集中到一个类里。 例如,UserController 类可以处理与用户相关的所有请求,包括展示、创建、更新与删除用户等。 默认情况下,控制器存放在 app/Http/Controllers 目录下。

创建控制器

要快速生成新的控制器,请使用 make:controller Artisan 命令。 默认情况下,应用中所有的控制器都会存放在 app/Http/Controllers 目录下。
php artisan make:controller UserController

基础控制器

控制器类可以拥有若干响应 HTTP 请求的 public 方法。
<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\View\View;

class UserController extends Controller
{
    /**
     * 显示指定用户的个人资料
     */
    public function show(string $id): View
    {
        return view('user.profile', [
            'user' => User::findOrFail($id)
        ]);
    }
}
创建好控制器类与方法后,可以像下面这样定义指向该方法的路由。
use App\Http\Controllers\UserController;

Route::get('/user/{id}', [UserController::class, 'show']);
当接收到与指定路由 URI 匹配的请求时,App\Http\Controllers\UserController 类的 show 方法会被调用,并将路由参数传给该方法。
控制器不必继承任何基类。不过,若所有控制器都需要共享的方法,你可以让它们继承一个基础控制器类,这有时会很有用。

单动作控制器

如果控制器动作特别复杂,将整个控制器类专门用于这一个动作会更方便。 只需在控制器中定义单一的 __invoke 方法。
<?php

namespace App\Http\Controllers;

class ProvisionServer extends Controller
{
    /**
     * 部署一台新的 Web 服务器
     */
    public function __invoke()
    {
        // ...
    }
}
在注册单动作控制器路由时无需指定控制器方法,只需将控制器名传给路由器。
use App\Http\Controllers\ProvisionServer;

Route::post('/server', ProvisionServer::class);
可以通过 make:controller Artisan 命令的 --invokable 选项生成可调用控制器。
php artisan make:controller ProvisionServer --invokable

资源控制器

如果把应用中的每个 Eloquent 模型都看作「资源」,那么通常会对每种资源进行同一组操作(CRUD)。 Laravel 的资源路由允许你用一行代码将典型的增删改查(CRUD)路由分配给控制器。 使用 make:controller Artisan 命令的 --resource 选项,可以快速创建处理这些动作的控制器。
php artisan make:controller PhotoController --resource
该命令会在 app/Http/Controllers/PhotoController.php 中生成控制器。 控制器会包含针对各类资源操作的方法桩。 接着,注册指向该控制器的资源路由。
use App\Http\Controllers\PhotoController;

Route::resource('photos', PhotoController::class);
这一行路由声明会创建多个处理资源各种动作的路由。 生成的控制器已包含每个动作对应的方法桩。
HTTP 方法URI动作路由名
GET/photosindexphotos.index
GET/photos/createcreatephotos.create
POST/photosstorephotos.store
GET/photos/{photo}showphotos.show
GET/photos/{photo}/editeditphotos.edit
PUT/PATCH/photos/{photo}updatephotos.update
DELETE/photos/{photo}destroyphotos.destroy
运行 php artisan route:list 可以快速查看应用中所有路由的概览。

部分资源路由

在声明资源路由时,可以指定控制器要处理的动作子集。
use App\Http\Controllers\PhotoController;

Route::resource('photos', PhotoController::class)->only([
    'index', 'show'
]);

Route::resource('photos', PhotoController::class)->except([
    'create', 'store', 'update', 'destroy'
]);

API 资源路由

在为 API 声明资源路由时,通常会想排除 createedit 这类展示 HTML 模板的路由。 使用 apiResource 方法可以自动排除这两个路由。
use App\Http\Controllers\PhotoController;

Route::apiResource('photos', PhotoController::class);

依赖注入

构造器注入

Laravel 的服务容器会用于解析所有 Laravel 控制器的依赖。 因此,你可以在控制器构造器中通过类型提示声明它所需的依赖。 声明的依赖会被自动解析并注入到控制器实例中。
<?php

namespace App\Http\Controllers;

use App\Repositories\UserRepository;

class UserController extends Controller
{
    /**
     * 创建新的控制器实例
     */
    public function __construct(
        protected UserRepository $users,
    ) {}
}

方法注入

除了构造器注入外,你也可以在控制器方法上通过类型提示声明依赖。 方法注入常见的用途是将 Illuminate\Http\Request 实例注入控制器方法。
<?php

namespace App\Http\Controllers;

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * 保存新用户
     */
    public function store(Request $request): RedirectResponse
    {
        $name = $request->name;

        // 保存用户的处理...

        return redirect('/users');
    }
}
如果控制器方法同时从路由参数接收输入,请把路由参数放在其他依赖之后。 例如,路由定义如下:
use App\Http\Controllers\UserController;

Route::put('/user/{id}', [UserController::class, 'update']);
那么可以按如下方式定义控制器方法,既能类型提示 Illuminate\Http\Request,又能访问 id 参数。
<?php

namespace App\Http\Controllers;

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * 更新指定的用户
     */
    public function update(Request $request, string $id): RedirectResponse
    {
        // 更新用户的处理...

        return redirect('/users');
    }
}
善用依赖注入可以更方便地在测试时替换为 mock,从而提升代码的可测试性。

下一步

路由

回顾路由定义方式以及与控制器的联动。
最后修改于 2026年7月13日