控制器是 MVC 架构的一部分。它们是扩展自 yii\base\Controller 的类的对象,负责处理请求并生成响应。特别是,在从 应用程序 接管控制权后,控制器将分析传入的请求数据,将它们传递给 模型,将模型结果注入到 视图 中,最后生成传出的响应。
控制器由操作组成,操作是最终用户可以访问并请求执行的最基本单元。一个控制器可以有一个或多个操作。
以下示例显示了一个具有两个操作的 post
控制器:view
和 create
namespace app\controllers;
use Yii;
use app\models\Post;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
class PostController extends Controller
{
public function actionView($id)
{
$model = Post::findOne($id);
if ($model === null) {
throw new NotFoundHttpException;
}
return $this->render('view', [
'model' => $model,
]);
}
public function actionCreate()
{
$model = new Post;
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
}
在 view
操作(由 actionView()
方法定义)中,代码首先根据请求的模型 ID 加载 模型;如果模型加载成功,它将使用名为 view
的 视图 显示它。否则,它将抛出一个异常。
在 create
操作(由 actionCreate()
方法定义)中,代码类似。它首先尝试使用请求数据填充 模型 的新实例并保存模型。如果两者都成功,它将把浏览器重定向到带有新创建模型 ID 的 view
操作。否则,它将显示 create
视图,用户可以通过该视图提供所需的输入。
最终用户通过所谓的路由来访问操作。路由是一个由以下部分组成的字符串
路由采用以下格式
ControllerID/ActionID
或者如果控制器属于模块,则采用以下格式
ModuleID/ControllerID/ActionID
因此,如果用户使用 URL https://hostname/index.php?r=site/index
发出请求,则将执行 site
控制器中的 index
操作。有关如何将路由解析为操作的更多详细信息,请参阅 路由和 URL 创建 部分。
在 Web 应用 中,控制器应该继承自 yii\web\Controller 或其子类。类似地,在 控制台应用 中,控制器应该继承自 yii\console\Controller 或其子类。以下代码定义了一个 site
控制器
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
}
通常,控制器被设计用来处理有关特定类型资源的请求。因此,控制器 ID 通常是名词,指的是它们正在处理的资源类型。例如,您可以使用 article
作为处理文章数据的控制器的 ID。
默认情况下,控制器 ID 应该只包含以下字符:小写英文字母、数字、下划线、连字符和正斜杠。例如,article
和 post-comment
都是有效的控制器 ID,而 article?
、PostComment
、admin\post
则不是。
控制器 ID 也可以包含一个子目录前缀。例如,admin/article
代表 控制器命名空间 下 admin
子目录中的 article
控制器。子目录前缀的有效字符包括:大小写英文字母、数字、下划线和正斜杠,其中正斜杠用作多级子目录的分隔符(例如 panels/admin
)。
可以根据以下步骤从控制器 ID 派生控制器类名
Controller
。以下是一些示例,假设 控制器命名空间 使用默认值 app\controllers
article
变为 app\controllers\ArticleController
;post-comment
变为 app\controllers\PostCommentController
;admin/post-comment
变为 app\controllers\admin\PostCommentController
;adminPanels/post-comment
变为 app\controllers\adminPanels\PostCommentController
。控制器类必须是 可自动加载 的。因此,在上面的示例中,article
控制器类应保存在别名为 @app/controllers/ArticleController.php
的文件中;而 admin/post-comment
控制器应在 @app/controllers/admin/PostCommentController.php
中。
信息:最后一个示例
admin/post-comment
显示了如何将控制器放在 控制器命名空间 的子目录下。当您想将控制器组织成几个类别并且不想使用 模块 时,这很有用。
您可以配置 控制器映射 来克服上面描述的控制器 ID 和类名的限制。这主要在您使用第三方控制器并且无法控制其类名时有用。
[
'controllerMap' => [
// declares "account" controller using a class name
'account' => 'app\controllers\UserController',
// declares "article" controller using a configuration array
'article' => [
'class' => 'app\controllers\PostController',
'enableCsrfValidation' => false,
],
],
]
每个应用都通过 yii\base\Application::$defaultRoute 属性指定一个默认控制器。当请求未指定 路由 时,将使用此属性指定的路由。对于 Web 应用,其值为 'site'
,而对于 控制台应用,其值为 help
。因此,如果 URL 为 https://hostname/index.php
,则 site
控制器将处理该请求。
您可以使用以下 应用配置 更改默认控制器
[
'defaultRoute' => 'main',
]
创建操作就像在控制器类中定义所谓的操作方法一样简单。操作方法是一个公共方法,其名称以 action
开头。操作方法的返回值表示要发送给最终用户的响应数据。以下代码定义了两个操作,index
和 hello-world
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public function actionIndex()
{
return $this->render('index');
}
public function actionHelloWorld()
{
return 'Hello World';
}
}
操作通常被设计用来执行对资源的特定操作。因此,操作 ID 通常是动词,例如 view
、update
等。
默认情况下,操作 ID 应该只包含以下字符:小写英文字母、数字、下划线和连字符(您可以使用连字符分隔单词)。例如,view
、update2
和 comment-post
都是有效的操作 ID,而 view?
和 Update
则不是。
您可以通过两种方式创建操作:内联操作和独立操作。内联操作是在控制器类中定义的方法,而独立操作是扩展 yii\base\Action 或其子类的类。内联操作创建起来比较省力,如果您不打算重用这些操作,通常会优先选择。另一方面,独立操作主要用于在不同的控制器中使用或作为 扩展 分发。
内联操作指的是我们刚才描述的以操作方法形式定义的操作。
操作方法的名称是从操作 ID 派生出来的,按照以下步骤进行
action
。例如,index
变为 actionIndex
,hello-world
变为 actionHelloWorld
。
注意:操作方法的名称是区分大小写的。如果您有一个名为
ActionIndex
的方法,它将不被视为操作方法,因此,对index
操作的请求将导致异常。另请注意,操作方法必须是公共的。私有或受保护的方法不会定义内联操作。
内联操作是最常定义的操作,因为它们创建起来非常简单。但是,如果您计划在不同的地方重用相同的操作,或者您想重新分发操作,您应该考虑将其定义为独立操作。
独立操作是根据扩展 yii\base\Action 或其子类的操作类定义的。例如,在 Yii 版本中,有 yii\web\ViewAction 和 yii\web\ErrorAction,它们都是独立操作。
要使用独立操作,您应该在操作映射中声明它,方法是在您的控制器类中覆盖 yii\base\Controller::actions() 方法,如下所示
public function actions()
{
return [
// declares "error" action using a class name
'error' => 'yii\web\ErrorAction',
// declares "view" action using a configuration array
'view' => [
'class' => 'yii\web\ViewAction',
'viewPrefix' => '',
],
];
}
如您所见,actions()
方法应该返回一个数组,其键是操作 ID,值是相应的操作类名或 配置。与内联操作不同,独立操作的操作 ID 可以包含任意字符,只要它们在 actions()
方法中声明即可。
要创建独立操作类,您应该扩展 yii\base\Action 或子类,并实现一个名为 run()
的公共方法。run()
方法的作用类似于操作方法。例如,
<?php
namespace app\components;
use yii\base\Action;
class HelloWorldAction extends Action
{
public function run()
{
return "Hello World";
}
}
操作方法或独立操作的 run()
方法的返回值非常重要。它代表相应操作的结果。
返回值可以是 响应 对象,该对象将作为响应发送给最终用户。
在上面显示的示例中,操作结果都是字符串,将被视为要发送给最终用户的响应体。以下示例显示了操作如何通过返回响应对象将用户浏览器重定向到新的 URL(因为 redirect() 方法返回响应对象)
public function actionForward()
{
// redirect the user browser to https://example.com
return $this->redirect('https://example.com');
}
内联操作的操作方法和独立操作的 run()
方法可以接受参数,称为操作参数。它们的值是从请求中获取的。对于 Web 应用,每个操作参数的值都是使用参数名称作为键从 $_GET
中检索的;对于 控制台应用,它们对应于命令行参数。
在以下示例中,view
操作(内联操作)声明了两个参数:$id
和 $version
。
namespace app\controllers;
use yii\web\Controller;
class PostController extends Controller
{
public function actionView($id, $version = null)
{
// ...
}
}
对于不同的请求,操作参数将按如下方式填充
https://hostname/index.php?r=post/view&id=123
:$id
参数将填充值为 '123'
,而 $version
仍然为 null
,因为没有 version
查询参数。https://hostname/index.php?r=post/view&id=123&version=2
:$id
和 $version
参数将分别填充为 '123'
和 '2'
。https://hostname/index.php?r=post/view
:将抛出 yii\web\BadRequestHttpException 异常,因为请求中未提供必需的 $id
参数。https://hostname/index.php?r=post/view&id[]=123
:将抛出 yii\web\BadRequestHttpException 异常,因为 $id
参数正在接收意外的数组值 ['123']
。如果希望操作参数接受数组值,则应使用 array
对其进行类型提示,如下所示
public function actionView(array $id, $version = null)
{
// ...
}
现在,如果请求为 https://hostname/index.php?r=post/view&id[]=123
,则 $id
参数将取值为 ['123']
。如果请求为 https://hostname/index.php?r=post/view&id=123
,则 $id
参数仍将接收相同的数组值,因为标量值 '123'
将自动转换为数组。
以上示例主要展示了操作参数如何在 Web 应用中工作。对于控制台应用,请参阅 控制台命令 部分以获取更多详细信息。
每个控制器都通过 yii\base\Controller::$defaultAction 属性指定一个默认操作。当 路由 只包含控制器 ID 时,表示请求了指定控制器的默认操作。
默认情况下,默认操作设置为 index
。如果要更改默认值,只需在控制器类中覆盖此属性,如下所示
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public $defaultAction = 'home';
public function actionHome()
{
return $this->render('home');
}
}
在处理请求时,应用 将根据请求的 路由 创建一个控制器。然后,控制器将经历以下生命周期来完成请求
beforeAction()
方法。false
,则其余未调用的 beforeAction()
方法将被跳过,并且动作执行将被取消。beforeAction()
方法调用都会触发一个 beforeAction
事件,您可以为其附加处理程序。afterAction()
方法、模块(如果控制器属于某个模块)和应用程序。afterAction()
方法调用都会触发一个 afterAction
事件,您可以为其附加处理程序。在一个设计良好的应用程序中,控制器通常非常精简,每个动作只包含几行代码。如果您的控制器相当复杂,通常表明您应该重构它并将一些代码移动到其他类中。
以下是一些具体的最佳实践。控制器
发现错别字或您认为此页面需要改进?
在 github 上编辑它 !
为了发表评论,请 注册 或 登录。