Skip to content

HTTP 控制器

介绍

与其在单个 routes.php 文件中定义所有请求处理逻辑,不如使用控制器类来组织这些行为。控制器可以将相关的 HTTP 请求处理逻辑分组到一个类中。控制器通常存储在 app/Http/Controllers 目录中。

基本控制器

以下是一个基本控制器类的示例:

php
<?php namespace App\Http\Controllers;

use App\Http\Controllers\Controller;

class UserController extends Controller {

	/**
	 * 显示给定用户的个人资料。
	 *
	 * @param  int  $id
	 * @return Response
	 */
	public function showProfile($id)
	{
		return view('user.profile', ['user' => User::findOrFail($id)]);
	}

}

我们可以这样路由到控制器动作:

php
Route::get('user/{id}', 'UserController@showProfile');
lightbulb

所有控制器都应继承基础控制器类。

控制器与命名空间

需要特别注意的是,我们不需要指定完整的控制器命名空间,只需指定 App\Http\Controllers 命名空间 "根" 之后的类名部分。默认情况下,RouteServiceProvider 会在包含根控制器命名空间的路由组中加载 routes.php 文件。

如果您选择使用更深的 PHP 命名空间来嵌套或组织控制器,只需使用相对于 App\Http\Controllers 根命名空间的特定类名即可。因此,如果您的完整控制器类是 App\Http\Controllers\Photos\AdminController,您可以这样注册路由:

php
Route::get('foo', 'Photos\AdminController@method');

命名控制器路由

与闭包路由一样,您可以在控制器路由上指定名称:

php
Route::get('foo', ['uses' => 'FooController@method', 'as' => 'name']);

控制器动作的 URL

要生成指向控制器动作的 URL,请使用 action 辅助方法:

php
$url = action('App\Http\Controllers\FooController@method');

如果您希望在仅使用相对于控制器命名空间的类名部分的情况下生成指向控制器动作的 URL,请使用 URL 生成器注册根控制器命名空间:

php
URL::setRootControllerNamespace('App\Http\Controllers');

$url = action('FooController@method');

您可以使用 currentRouteAction 方法访问正在运行的控制器动作的名称:

php
$action = Route::currentRouteAction();

控制器中间件

可以在控制器路由上指定 中间件

php
Route::get('profile', [
	'middleware' => 'auth',
	'uses' => 'UserController@showProfile'
]);

此外,您可以在控制器的构造函数中指定中间件:

php
class UserController extends Controller {

	/**
	 * 实例化一个新的 UserController 实例。
	 */
	public function __construct()
	{
		$this->middleware('auth');

		$this->middleware('log', ['only' => ['fooAction', 'barAction']]);

		$this->middleware('subscribed', ['except' => ['fooAction', 'barAction']]);
	}

}

隐式控制器

Laravel 允许您轻松定义单个路由来处理控制器中的每个动作。首先,使用 Route::controller 方法定义路由:

php
Route::controller('users', 'UserController');

controller 方法接受两个参数。第一个是控制器处理的基本 URI,第二个是控制器的类名。接下来,只需在控制器中添加方法,方法名前缀为它们响应的 HTTP 动词:

php
class UserController extends Controller {

	public function getIndex()
	{
		//
	}

	public function postProfile()
	{
		//
	}

	public function anyLogin()
	{
		//
	}

}

index 方法将响应控制器处理的根 URI,在本例中为 users

如果您的控制器动作包含多个单词,您可以在 URI 中使用 "dash" 语法访问该动作。例如,以下控制器动作在我们的 UserController 中将响应 users/admin-profile URI:

php
public function getAdminProfile() {}

分配路由名称

如果您希望为控制器上的某些路由 "命名",可以将第三个参数传递给 controller 方法:

php
Route::controller('users', 'UserController', [
	'anyLogin' => 'user.login',
]);

RESTful 资源控制器

资源控制器使围绕资源构建 RESTful 控制器变得轻而易举。例如,您可能希望创建一个控制器来处理应用程序存储的 "照片" 的 HTTP 请求。使用 make:controller Artisan 命令,我们可以快速创建这样的控制器:

php
php artisan make:controller PhotoController

接下来,我们注册一个资源路由到控制器:

php
Route::resource('photo', 'PhotoController');

此单个路由声明创建多个路由来处理照片资源上的各种 RESTful 动作。同样,生成的控制器将已经为每个动作生成了方法存根,包括说明它们处理的 URI 和动词的注释。

资源控制器处理的动作

动词路径动作路由名称
GET/photoindexphoto.index
GET/photo/createcreatephoto.create
POST/photostorephoto.store
GET/photo/showphoto.show
GET/photo/{photo}/editeditphoto.edit
PUT/PATCH/photo/updatephoto.update
DELETE/photo/destroyphoto.destroy

自定义资源路由

此外,您可以指定仅处理路由上的某个子集的动作:

php
Route::resource('photo', 'PhotoController',
				['only' => ['index', 'show']]);

Route::resource('photo', 'PhotoController',
				['except' => ['create', 'store', 'update', 'destroy']]);

默认情况下,所有资源控制器动作都有一个路由名称;但是,您可以通过传递 names 数组来覆盖这些名称:

php
Route::resource('photo', 'PhotoController',
				['names' => ['create' => 'photo.build']]);

处理嵌套资源控制器

要 "嵌套" 资源控制器,请在路由声明中使用 "dot" 符号:

php
Route::resource('photos.comments', 'PhotoCommentController');

此路由将注册一个 "嵌套" 资源,可以通过以下 URL 访问:photos/{photos}/comments/{comments}

php
class PhotoCommentController extends Controller {

	/**
	 * 显示指定的照片评论。
	 *
	 * @param  int  $photoId
	 * @param  int  $commentId
	 * @return Response
	 */
	public function show($photoId, $commentId)
	{
		//
	}

}

向资源控制器添加额外路由

如果需要向资源控制器添加默认资源路由之外的额外路由,您应该在调用 Route::resource 之前定义这些路由:

php
Route::get('photos/popular', 'PhotoController@method');

Route::resource('photos', 'PhotoController');

依赖注入与控制器

构造函数注入

Laravel 服务容器 用于解析所有 Laravel 控制器。因此,您可以在控制器的构造函数中类型提示任何依赖项:

php
<?php namespace App\Http\Controllers;

use Illuminate\Routing\Controller;
use App\Repositories\UserRepository;

class UserController extends Controller {

	/**
	 * 用户仓库实例。
	 */
	protected $users;

	/**
	 * 创建一个新的控制器实例。
	 *
	 * @param  UserRepository  $users
	 * @return void
	 */
	public function __construct(UserRepository $users)
	{
		$this->users = $users;
	}

}

当然,您也可以类型提示任何 Laravel 合约。如果容器可以解析它,您就可以类型提示它。

方法注入

除了构造函数注入,您还可以在控制器的方法上类型提示依赖项。例如,让我们在一个方法上类型提示 Request 实例:

php
<?php namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;

class UserController extends Controller {

	/**
	 * 存储一个新用户。
	 *
	 * @param  Request  $request
	 * @return Response
	 */
	public function store(Request $request)
	{
		$name = $request->input('name');

		//
	}

}

如果您的控制器方法还期望从路由参数中获取输入,只需在其他依赖项之后列出路由参数:

php
<?php namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;

class UserController extends Controller {

	/**
	 * 更新指定的用户。
	 *
	 * @param  Request  $request
	 * @param  int  $id
	 * @return Response
	 */
	public function update(Request $request, $id)
	{
		//
	}

}
lightbulb

方法注入与 模型绑定 完全兼容。容器将智能地确定哪些参数是模型绑定的,哪些参数应该被注入。

路由缓存

如果您的应用程序仅使用控制器路由,您可以利用 Laravel 的路由缓存。使用路由缓存将大大减少注册所有应用程序路由所需的时间。在某些情况下,您的路由注册速度甚至可能提高 100 倍!要生成路由缓存,只需执行 route:cache Artisan 命令:

php
php artisan route:cache

就是这样!您的缓存路由文件现在将被使用,而不是 app/Http/routes.php 文件。请记住,如果您添加了任何新路由,则需要生成新的路由缓存。因此,您可能希望仅在项目部署期间运行 route:cache 命令。

要在不生成新缓存的情况下删除缓存的路由文件,请使用 route:clear 命令:

php
php artisan route:clear