Skip to content

Laravel - 控制器

控制器是 Model-View-Controller (MVC) 架构模式中的基础部分。在 Laravel 中,控制器负责处理传入的 HTTP 请求,检索或操作数据(通常通过模型 Models),然后加载合适的视图 View 或返回 JSON 响应。

你可以选择使用控制器类来组织请求处理行为,而不是将所有请求处理逻辑定义为路由文件中的闭包 (Closures)。控制器可以将相关的 HTTP 请求处理逻辑组织到一个单独的类中。

你可以使用 Artisan CLI 命令生成新的控制器:

php artisan make:controller <ControllerName>

将 <ControllerName> 替换为你的控制器名称(例如,UserController、PhotoController)。按照惯例,控制器名称是单数,并以 Controller 结尾。

例如,要创建一个 ProductController:

php artisan make:controller ProductController

此命令将在 app/Http/Controllers/ProductController.php 创建一个新的控制器文件。一个基础控制器看起来像这样:

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request; // 自动包含
class ProductController extends Controller
{
// 处理请求的方法将在这里编写
public function index()
{
// 显示产品列表的逻辑
return view('products.index'); // 示例:返回一个 Blade 视图
}
public function show(string $id)
{
// 显示特定产品的逻辑
// Example: Product::findOrFail($id);
return view('products.show', ['productId' => $id]);
}
}

有了控制器方法后,你可以从 routes/web.php 或 routes/api.php 文件路由到它:

use App\Http\Controllers\ProductController;
// 路由到 ProductController 中的 'index' 方法
Route::get('/products', [ProductController::class, 'index']);
// 路由到 'show' 方法,传递一个 'id' 参数
Route::get('/products/{id}', [ProductController::class, 'show']);

这种语法 [ControllerName::class, 'methodName'] 是推荐的方式,因为它提供了更好的 IDE 支持和重构性。

中间件可以分配给路由文件中的控制器路由,或者你可以在控制器的构造函数或方法中指定中间件。这允许你将过滤器(如认证或授权)应用于特定的控制器行为。

在控制器构造函数中分配中间件:

Section titled “在控制器构造函数中分配中间件:”

你可以在控制器的构造函数中使用 middleware 方法将中间件分配给控制器中的行为。你甚至可以限制中间件仅对某些方法运行。

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserProfileController extends Controller
{
public function __construct()
{
// 将 'auth' 中间件应用于此控制器中的所有方法
$this->middleware('auth');
// 仅将 'subscribed' 中间件应用于 'settings' 方法
$this->middleware('subscribed')->only('settings');
// 将 'admin' 中间件应用于除 'show' 以外的所有方法
$this->middleware('admin')->except('show');
}
public function show(string $id)
{
return view('profile.show', ['userId' => $id]);
}
public function edit(string $id)
{
// 只有认证用户才能访问此方法
return view('profile.edit', ['userId' => $id]);
}
public function settings()
{
// 只有认证且已订阅用户才能访问此方法
return view('profile.settings');
}
}

或者,通常更推荐为了清晰起见,直接在路由定义中分配中间件:

use App\Http\Controllers\UserProfileController;
Route::get('/profile/{id}', [UserProfileController::class, 'show']);
Route::get('/profile/{id}/edit', [UserProfileController::class, 'edit'])
->middleware('auth');
Route::get('/profile/settings', [UserProfileController::class, 'settings'])
->middleware(['auth', 'subscribed']);

对于管理资源(如照片、文章、产品)的应用,Laravel 的资源控制器 (resource controllers) 可以轻松构建围绕这些资源的 RESTful 控制器。当你创建一个资源控制器时,Laravel 会预填充它,其中包含常见的 CRUD(创建、读取、更新、删除)操作的方法。

使用 make:controller 命令时带上 --resource 或 -r 标志:

php artisan make:controller PhotoController --resource

这将生成包含 index、create、store、show、edit、update 和 destroy 等方法的 app/Http/Controllers/PhotoController.php。

示例 PhotoController(简化存根):

Section titled “示例 PhotoController(简化存根):”
<?php
namespace App\Http\Controllers;
use App\Models\Photo; // 假设你有一个 Photo 模型
use Illuminate\Http\Request;
use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
class PhotoController extends Controller
{
public function index(): View
{
// $photos = Photo::all();
// return view('photos.index', ['photos' => $photos]);
return view('photos.index');
}
public function create(): View
{
return view('photos.create');
}
public function store(Request $request): RedirectResponse
{
// $validated = $request->validate(['title' => 'required|string|max:255']);
// Photo::create($validated);
// return redirect()->route('photos.index')->with('success', 'Photo created!');
return redirect()->route('photos.index');
}
public function show(string $id): View // 或者使用 Photo $photo 实现路由模型绑定 (Route Model Binding)
{
// $photo = Photo::findOrFail($id);
// return view('photos.show', ['photo' => $photo]);
return view('photos.show', ['photoId' => $id]);
}
public function edit(string $id): View // 或者使用 Photo $photo
{
// $photo = Photo::findOrFail($id);
// return view('photos.edit', ['photo' => $photo]);
return view('photos.edit', ['photoId' => $id]);
}
public function update(Request $request, string $id): RedirectResponse // 或者使用 Photo $photo
{
// $photo = Photo::findOrFail($id);
// $validated = $request->validate(['title' => 'required|string|max:255']);
// $photo->update($validated);
// return redirect()->route('photos.show', $photo)->with('success', 'Photo updated!');
return redirect()->route('photos.index');
}
public function destroy(string $id): RedirectResponse // 或者使用 Photo $photo
{
// $photo = Photo::findOrFail($id);
// $photo->delete();
// return redirect()->route('photos.index')->with('success', 'Photo deleted!');
return redirect()->route('photos.index');
}
}

要注册资源控制器的所有必要路由,请使用 Route::resource:

// 在 routes/web.php 文件中
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class);

这一行代码会创建多个路由来处理资源的各种操作。它通常生成以下路由:

HTTP 动词URI对应方法 (Action)路由命名 (Route Name)
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

提示:如果你只需要资源路由的一个子集,请使用 only 或 except 方法:Route::resource('photos', PhotoController::class)->only(['index', 'show']);

如果一个控制器只处理一个行为,你可以通过在控制器中放置一个单独的 __invoke 方法来创建一个单行为控制器。这可以简化那些具有非常特定、单一用途的控制器。

使用 make:controller 命令时带上 --invokable 或 -i 标志:

php artisan make:controller ProvisionServer --invokable

这将生成包含 __invoke 方法的 app/Http/Controllers/ProvisionServer.php:

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response; // 或其他特定的响应类型
class ProvisionServer extends Controller
{
/**
* 处理传入的请求。
*/
public function __invoke(Request $request): Response
{
// 服务器 provision 逻辑...
return response('Server provisioning started.');
}
}

为单行为控制器注册路由时,你不需要指定方法:

// 在 routes/web.php 文件中
use App\Http\Controllers\ProvisionServer;
Route::post('/server/provision', ProvisionServer::class);

Laravel 的服务容器 (service container) 用于解析所有 Laravel 控制器。因此,你可以在控制器的构造函数或行为方法中对控制器可能需要的任何依赖项进行类型提示 (type-hint)。这些依赖项将自动被解析并注入。

你可以在控制器的构造函数中进行依赖项类型提示。Laravel 会自动注入这些服务的实例。

示例:注入自定义服务 UserService。

// app/Services/UserService.php(示例服务类)
namespace App\Services;
class UserService
{
public function getActiveUsersCount(): int
{
// 假设这是计算活跃用户的逻辑
return 150;
}
}
// app/Http/Controllers/DashboardController.php
namespace App\Http\Controllers;
use App\Services\UserService;
use Illuminate\View\View;
class DashboardController extends Controller
{
private UserService $userService;
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function index(): View
{
$activeUsers = $this->userService->getActiveUsersCount();
return view('dashboard.index', ['activeUsers' => $activeUsers]);
}
}

除了构造函数注入,你还可以在控制器的行为方法上进行依赖项类型提示。这对于仅由特定行为所需的依赖项非常有用。

Laravel 的 Illuminate\Http\Request 对象是方法注入的常见示例。

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Services\OrderProcessorService; // 另一个服务类示例
use Illuminate\Http\JsonResponse;
class OrderController extends Controller
{
// Request 和自定义服务类的方法注入示例
public function store(Request $request, OrderProcessorService $processor): JsonResponse
{
$validatedData = $request->validate([
'product_id' => 'required|exists:products,id',
'quantity' => 'required|integer|min:1',
]);
$order = $processor->createNewOrder($validatedData);
return response()->json(['message' => 'Order created!', 'order_id' => $order->id], 201);
}
}

实际应用:构建用户管理模块

想象一下,你正在为管理面板构建用户管理部分。你可以创建一个 UserManagementController 作为资源控制器。

  • index() 用于列出所有用户(带分页)。
  • create() 用于显示添加新用户的表单。
  • store() 用于验证并保存新用户。
  • edit() 用于显示编辑现有用户的表单。
  • update() 用于验证并更新用户。
  • destroy() 用于删除用户。 你会应用 auth 以及可能还有一个 admin 角色中间件来保护这些路由。依赖注入可以用于注入 UserRepository 或 UserService 来处理数据操作。

常见学习障碍:臃肿的控制器 (Fat Controllers)

一个常见的错误是将过多的逻辑直接放入控制器中(使它们变得“臃肿”)。尽量通过将业务逻辑委托给 Service 类、数据访问委托给 Repository 或 Eloquent 模型、以及将复杂的请求验证委托给 Form Request 类来保持控制器精简。控制器主要应负责协调流程:接收请求,调用服务/模型,返回响应。

有关更多详细信息,请访问官方 Laravel 关于控制器的文档:https://laravel.com/docs/controllers