October CMS 后端实现了模型-视图-控制器 (MVC) 模式。本文介绍了如何开发后端控制器以及如何配置控制器行为。
每个控制器都由一个PHP脚本表示,该脚本位于插件目录的 /controllers 子目录中。控制器视图是位于控制器视图目录中的 .php 文件。控制器视图目录的名称与以小写形式编写的控制器类名匹配。视图目录还可以包含控制器配置文件。一个控制器目录结构的示例:
├── plugins
| └── acme
| └── blog
| ├── controllers
| | ├── users ← 视图目录
| | | ├── config_form.yaml ← 配置文件
| | | ├── _partial.php ← 分部文件
| | | └── index.php ← 视图文件
| | └── Users.php ← 控制器类
| └── Plugin.php
:::tip
有关使用后端控制器的实用示例,请查看 行为之上教程系列。
:::
create:controller 命令生成一个控制器、配置和视图文件。 第一个参数指定作者和插件名称。 第二个参数指定控制器类名。
php artisan create:controller Acme.Blog Posts控制器类必须扩展 Backend\Classes\Controller 类。与其他插件类一样,控制器应属于 插件命名空间。在插件中使用的控制器最基本的表示形式如下所示。
namespace Acme\Blog\Controllers;
class Posts extends \Backend\Classes\Controller
{
public function index() // ← Action method
{
}
}通常每个控制器都实现处理单一类型数据的功能 - 例如博客文章或分类。所有下述后端行为都假定遵循此约定。
后端控制器基类定义了若干属性,允许配置页面外观并管理页面安全:
| Property | Description |
|---|---|
| $fatalError | allows to store a fatal exception generated in an action method in order to display it in the view. |
| $user | contains a reference to the the backend user object. |
| $suppressView | allows to prevent the view display. Can be updated in the action method or in the controller constructor. |
| $params | an array of the routed parameters. |
| $action | a name of the action method being executed in the current request. |
| $publicActions | defines an array of actions available without the backend user authentication. Can be overridden in the class definition. |
| $requiredPermissions | permissions required to view this page. Can be set in the class definition or in the controller constructor. See users & permissions for details. |
| $pageTitle | sets the page title. Can be set in the action method. |
| $bodyClass | body class property used for customizing the layout. Can be set in the controller constructor or action method. |
| $guarded | controller specific methods which cannot be called as actions. Can be extended in the controller constructor. |
| $layout | specify a custom layout for the controller views. |
控制器可以在原生的 __construct 方法中执行初始化逻辑。
构造函数内的逻辑不受保护,不应处理任何敏感逻辑。
public function __construct()
{
parent::__construct();
}:::
beforeDisplay 方法在权限检查之后被调用,并且大多数逻辑应该放在这里。这包括初始化部件以及其他共享组件。
public function beforeDisplay()
{
// Initialize widgets, handle file uploads, etc.
}公开的控制器方法,称为动作与视图文件耦合代表与该动作对应的页面。后端视图文件使用 PHP 语法。以下是index.php视图文件内容的示例,对应于index动作方法。
<h1>Hello World</h1>页面的 URL 由作者名、插件名、控制器名和动作名组成。
admin/[author name]/[plugin name]/[controller name]/[action name]例如,本文开头使用的类定义可通过以下 URL 访问。
https://example.tld/admin/acme/blog/users/index如果一个 插件已注册 带有一个 提示 在 pluginDetails 方法中,一个更短的 URL 结构变得可用,这对于在 URL 中隐藏作者名称很有用。
admin/[plugin hint]/[controller name]/[action name]使用控制器的$vars属性将任何数据直接传递到您的视图:
$this->vars['myVariable'] = 'value';通过 $vars 属性传递的变量现在可以直接在您的视图中访问:
<p>The variable value is <?= $myVariable ?></p>插件可以在插件注册文件中注册后端导航菜单和子菜单。导航上下文决定哪个后端菜单和子菜单在当前后端页面上处于活动状态。你可以使用BackendMenu类设置导航上下文:
BackendMenu::setContext('Acme.Blog', 'blog', 'categories');第一个参数指定了作者和插件名称。第二个参数设置了菜单代码。可选的第三个参数指定了子菜单代码。通常你会在控制器构造函数中调用BackendMenu::setContext。
namespace Acme\Blog\Controllers;
class Categories extends \Backend\Classes\Controller {
public function __construct()
{
parent::__construct();
BackendMenu::setContext('Acme.Blog', 'blog', 'categories');
}您可以使用控制器类的 $pageTitle 属性来设置后端页面标题 (请注意,表单和列表行为可以为您完成此操作):
$this->pageTitle = 'Blog Categories';您可以在后端控制器中覆盖响应,作为修改 HTTP 请求响应的一种机制。 例如,您可能希望为控制器中的某些操作指定 HTTP 头, 或者在用户不满足特定条件时重定向他们。
覆盖响应在扩展其他控制器时特别有用。然而,你可能会发现局部调用这些方法很有用。
\Author\Plugin\Controllers\SomeController::extend(function($controller) {
$controller->setResponseHeader('Test-Header', 'Test');
});如果您想检查已路由的动作或参数,您可以在控制器 getAction 和 getParams 方法中找到它们。
Author\Plugin\Controllers\SomeController::extend(function($controller) {
if ($controller->getAction() === 'index') {
// Only do it for the index action
}
if ($controller->getParams()[0] ?? null) {
// Only if first parameter exists
}
});要为您的响应添加标头,您可以调用 setResponseHeader 方法。
$this->setResponseHeader('Test-Header', 'Test');要更改响应的状态码,请使用 setStatusCode 方法。
$this->setStatusCode(404);要覆盖整个响应,请调用 setResponse 方法,这将强制响应,无论页面生命周期中发生什么。
$this->setResponse('Page Not Found');您还可以传递一个 Response 对象给此方法。
$this->setResponse(Response::make(...));查看 视图与响应文章 以获取有关构建响应的更多信息。