HTTP 控制器
介绍
与其在单个 routes.php
文件中定义所有请求处理逻辑,不如使用控制器类来组织这些行为。控制器可以将相关的 HTTP 请求处理逻辑分组到一个类中。控制器通常存储在 app/Http/Controllers
目录中。
基本控制器
以下是一个基本控制器类的示例。所有 Laravel 控制器都应扩展默认 Laravel 安装中包含的基控制器类:
<?php
namespace App\Http\Controllers;
use App\User;
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)]);
}
}
我们可以这样路由到控制器动作:
Route::get('user/{id}', 'UserController@showProfile');
现在,当请求匹配指定的路由 URI 时,将执行 UserController
类上的 showProfile
方法。当然,路由参数也会传递给该方法。
控制器与命名空间
需要特别注意的是,在定义控制器路由时,我们不需要指定完整的控制器命名空间。我们只定义了 App\Http\Controllers
命名空间“根”之后的类名部分。默认情况下,RouteServiceProvider
将在包含根控制器命名空间的路由组中加载 routes.php
文件。
如果您选择使用更深的 PHP 命名空间来嵌套或组织控制器,只需使用相对于 App\Http\Controllers
根命名空间的特定类名即可。因此,如果您的完整控制器类是 App\Http\Controllers\Photos\AdminController
,您可以这样注册路由:
Route::get('foo', 'Photos\AdminController@method');
命名控制器路由
与闭包路由一样,您可以在控制器路由上指定名称:
Route::get('foo', ['uses' => 'FooController@method', 'as' => 'name']);
控制器动作的 URL
您还可以使用 route
辅助函数生成指向命名控制器路由的 URL:
$url = route('name');
您还可以使用 action
辅助方法通过控制器的类和方法名生成 URL。同样,我们只需指定控制器类名在基本 App\Http\Controllers
命名空间之后的部分:
$url = action('FooController@method');
您可以使用 Route
facade 上的 currentRouteAction
方法访问正在运行的控制器动作的名称:
$action = Route::currentRouteAction();
控制器中间件
中间件 可以像这样分配给控制器的路由:
Route::get('profile', [
'middleware' => 'auth',
'uses' => 'UserController@showProfile'
]);
然而,在控制器的构造函数中指定中间件更为方便。使用控制器构造函数中的 middleware
方法,您可以轻松地将中间件分配给控制器。您甚至可以将中间件限制为仅适用于控制器类上的某些方法:
class UserController extends Controller
{
/**
* 实例化一个新的 UserController 实例。
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('log', ['only' => ['fooAction', 'barAction']]);
$this->middleware('subscribed', ['except' => ['fooAction', 'barAction']]);
}
}
RESTful 资源控制器
资源控制器使围绕资源构建 RESTful 控制器变得轻而易举。例如,您可能希望创建一个控制器来处理有关应用程序存储的“照片”的 HTTP 请求。使用 make:controller
Artisan 命令,我们可以快速创建这样的控制器:
php artisan make:controller PhotoController
Artisan 命令将在 app/Http/Controllers/PhotoController.php
生成一个控制器文件。该控制器将包含每个可用资源操作的方法。
接下来,您可以注册一个资源路由到控制器:
Route::resource('photo', 'PhotoController');
这个单一的路由声明创建了多个路由来处理照片资源的各种 RESTful 操作。同样,生成的控制器将已经为每个这些操作预留了方法,包括说明它们处理的 URI 和动词的注释。
资源控制器处理的动作
动词 | 路径 | 动作 | 路由名称 |
---|---|---|---|
GET | /photo | index | photo.index |
GET | /photo/create | create | photo.create |
POST | /photo | store | photo.store |
GET | /photo/{photo} | show | photo.show |
GET | /photo/{photo}/edit | edit | photo.edit |
PUT/PATCH | /photo/{photo} | update | photo.update |
DELETE | /photo/{photo} | destroy | photo.destroy |
部分资源路由
在声明资源路由时,您可以指定要在路由上处理的动作子集:
Route::resource('photo', 'PhotoController',
['only' => ['index', 'show']]);
Route::resource('photo', 'PhotoController',
['except' => ['create', 'store', 'update', 'destroy']]);
命名资源路由
默认情况下,所有资源控制器动作都有一个路由名称;但是,您可以通过传递 names
数组来覆盖这些名称:
Route::resource('photo', 'PhotoController',
['names' => ['create' => 'photo.build']]);
嵌套资源
有时您可能需要定义到“嵌套”资源的路由。例如,照片资源可能有多个“评论”可以附加到照片上。要“嵌套”资源控制器,请在路由声明中使用“点”符号:
Route::resource('photos.comments', 'PhotoCommentController');
此路由将注册一个“嵌套”资源,可以通过以下 URL 访问:photos/{photos}/comments/{comments}
。
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
class PhotoCommentController extends Controller
{
/**
* 显示指定的照片评论。
*
* @param int $photoId
* @param int $commentId
* @return Response
*/
public function show($photoId, $commentId)
{
//
}
}
补充资源控制器
如果需要在默认资源路由之外为资源控制器添加其他路由,您应在调用 Route::resource
之前定义这些路由;否则,由 resource
方法定义的路由可能会无意中优先于您的补充路由:
Route::get('photos/popular', 'PhotoController@method');
Route::resource('photos', 'PhotoController');
隐式控制器
Laravel 允许您轻松定义单个路由来处理控制器类中的每个动作。首先,使用 Route::controller
方法定义路由。controller
方法接受两个参数。第一个是控制器处理的基本 URI,第二个是控制器的类名:
Route::controller('users', 'UserController');
接下来,只需向控制器添加方法。方法名称应以它们响应的 HTTP 动词开头,后跟 URI 的标题大小写版本:
<?php
namespace App\Http\Controllers;
class UserController extends Controller
{
/**
* 响应对 GET /users 的请求
*/
public function getIndex()
{
//
}
/**
* 响应对 GET /users/show/1 的请求
*/
public function getShow($id)
{
//
}
/**
* 响应对 GET /users/admin-profile 的请求
*/
public function getAdminProfile()
{
//
}
/**
* 响应对 POST /users/profile 的请求
*/
public function postProfile()
{
//
}
}
如上例所示,index
方法将响应控制器处理的根 URI,在本例中为 users
。
分配路由名称
如果您希望为控制器上的某些路由命名,可以将名称数组作为第三个参数传递给 controller
方法:
Route::controller('users', 'UserController', [
'getShow' => 'user.show',
]);
依赖注入与控制器
构造函数注入
Laravel 服务容器用于解析所有 Laravel 控制器。因此,您可以在构造函数中类型提示控制器可能需要的任何依赖项。依赖项将自动解析并注入到控制器实例中:
<?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 合约。如果容器可以解析它,您就可以类型提示它。
方法注入
除了构造函数注入,您还可以在控制器的动作方法上类型提示依赖项。例如,让我们在一个方法上类型提示 Illuminate\Http\Request
实例:
<?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');
//
}
}
如果您的控制器方法还期望从路由参数中获取输入,只需在其他依赖项之后列出路由参数。例如,如果您的路由定义如下:
Route::put('user/{id}', 'UserController@update');
您仍然可以类型提示 Illuminate\Http\Request
并通过以下方式定义控制器方法来访问路由参数 id
:
<?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)
{
//
}
}
路由缓存
路由缓存不适用于基于闭包的路由。要使用路由缓存,您必须将任何闭包路由转换为使用控制器类。
如果您的应用程序完全使用基于控制器的路由,您可以利用 Laravel 的路由缓存。使用路由缓存将大大减少注册应用程序所有路由所需的时间。在某些情况下,您的路由注册速度甚至可能提高 100 倍!要生成路由缓存,只需执行 route:cache
Artisan 命令:
php artisan route:cache
就是这样!您的缓存路由文件现在将被使用,而不是您的 app/Http/routes.php
文件。请记住,如果您添加了任何新路由,则需要生成新的路由缓存。因此,您可能希望仅在项目部署期间运行 route:cache
命令。
要在不生成新缓存的情况下删除缓存的路由文件,请使用 route:clear
命令:
php artisan route:clear