请求后获取模式
Joomla 遵循请求后重定向/获取模式,这意味着当用户以 HTTP POST 提交表单后,Joomla 不会使用 HTML 页面响应 POST,而是重定向到浏览器将使用 HTTP GET 访问的另一个页面。在本节中,我们将了解一些这个模式的示例,以及它如何与任务参数对齐,随后与 Joomla 库 MVC 类对齐。
尽管 Joomla 广泛使用了 Post-Request-Get 模式,但这并不意味着您必须在自己的组件中随处使用它。例如,如果您的组件有一个用于提交订单的前端表单,那么将订单确认文本作为订单提交的 HTTP POST 的 HTTP 响应提供可能是非常合理的。
示例 1 发布文章
该图表展示了在 Joomla 后端发布文章时涉及的步骤。对于您来说,在自己的 Joomla 实例上执行这些操作并使用浏览器的开发工具检查 HTTP 请求和响应可能会非常有帮助。
操作 1
管理员单击内容/文章以显示文章列表。这只是一个 HTTP GET 请求,目标是 com_content
管理员网页,其中 view
参数设置为 articles
。(如果正在使用 SEF URL,那么它便是 Joomla 路由器,该路由器将根据解析传入的 URL 设置 view
参数。)在此 HTTP 请求中未设置 任务 参数,因此 实例化了 com_content
DisplayController 并调用了 display()
方法。基于 view
参数,默认视图类为 Joomla\Component\Content\Administrator\View\Articles\HtmlView,并且默认模型为 Joomla\Component\Content\Administrator\Model\ArticlesModel。
需要注意的关键一点是,此视图会在表单顶部显示各种按钮。当您选择一个或多个文章时,这些按钮会在页面顶部的操作按钮下拉框中显示。正是这些按钮触发了到服务器的 HTTP POST,并且内置在这些按钮中,这就是 任务 参数在该 POST 请求中设置的内容。
操作 2
管理员选择 تعدد 文章,然后按下发布按钮。此时,值得打开浏览器的开发工具并检查消息。您将看到按下发布触发
- 到服务器的 HTTP POST 请求,其中
- 任务 参数设置为 “articles.publish”
- 受到影响的文章的 id 数组 cid[]
- 以及来自分页/筛选字段/批处理选项的许多其他参数——所有这些都与我们这里无关
此请求接下来将发送到 com_content
管理员 ArticlesController,并且其中的 publish()
方法将被调用。如果查看 administrator/components/com_content/src/Controller/ArticlesController.php 中的代码,将不会找到其中的 publish()
函数,因此将运行的是它所扩展的类中的 publish()
函数,即 librarie/src/MVC/Controller/AdminController.php 中的 Joomla\CMS\MVC\Controller\AdminController。让我们从该 publish()
方法中挑选几行
$cid = (array) $this->input->get('cid', [], 'int');
它获取管理员选择的一系列 id。
$model = $this->getModel();
$model->publish($cid, $value);
getModel
函数在 ArticlesController 中定义
public function getModel($name = 'Article', $prefix = 'Administrator', $config = ['ignore_request' => true])
{
return parent::getModel($name, $prefix, $config);
}
并且当默认 $name
为“Article”时,将获取 com_content
管理员 ArticleModel(而不是默认 ArticlesModel)。如果在此文件中查找,将找到 publish()
方法,但它基本上只有
return parent::publish($pks, $value);
因此处理发布操作的实际工作在 ArticleModel 扩展的类中,即 librarie/src/MVC/Model/AdminModel.php 中的 Joomla\CMS\MVC\Model\AdminModel,它的 publish
方法中。
回到 AdminController.php 代码,稍后我们在其中有
$ntext = $this->text_prefix . '_N_ITEMS_PUBLISHED';
$this->setMessage(Text::plural($ntext, \count($cid)));
此处代码正在设置将向管理员显示的一条消息,基本上确认其状态已设为已发布的记录数量。
最后,在 AdminController.php 中的 publish
函数末尾有
$this->setRedirect(
Route::_(
'index.php?option=' . $this->option . '&view=' . $this->view_list
. $this->getRedirectToListAppend(),
false
)
);
此处是后请求获取模式实际操作 - Joomla 并未显示网页以响应 POST,而是设置重定向 - 在此情况下,返回到显示文章列表的表单。
如 MVC 概述 和 分配器组件 中所述,在组件的 execute
方法完成之后,分配器调用组件的 redirect
方法。此方法(实际在 Joomla\CMS\MVC\Controller\BaseController 中)将做两件事
- 使用 Joomla 应用程序的
enqueueMessage
函数将确认消息(“发布 n 篇文章”)存储于会话数据中 - 通知 Joomla 应用程序重定向到存储于上方
setRedirect
调用的 URL。
如果在阅读此页面时一直在查看 Joomla 源代码,则可能已发现两点。
首先,AdminController 中的 publish()
函数不仅处理 publish
功能,还用于将状态设置为其他值,例如 unpublish
。Joomla BaseController 有受保护的实例变量 $taskmap
,它将 task 参数的 <method>
部分映射到将被调用的函数,在 AdminController 构造器中,它具有
$this->registerTask('unpublish', 'publish');
这意味着在 task 设置为 "Articles.unpublish" 的情况下,其 execute
函数也将调用 publish
函数。
这可实现对 2 个操作的代码共享,它们大同小异,但是当然会有细微差别,而且在 publish()
中它调用
$task = $this->getTask();
以获取任务参数的<方法>
部分,以便对差异进行编码。请注意在控制器代码中的这些调用中,“任务”只引用任务参数的<方法>
部分;Joomla 在此处的命名并不一致。
其次,你注意到了多大的发布操作处理代码实际上在 com_content
中吗?非常少!几乎所有代码都在 Joomla 库 MVC 类中。明智地选择如何命名组件字段和组件 MVC 类如何扩展 Joomla 库类可以让你能够通过极少的编码工作包括功能。我们将在下一部分更加详细地研究 Joomla 库 MVC 类。
操作 1 再来一次
操作 2 末尾的 URL 重定向导致文章列表再次显示。不过,此次用户将看到类似“2 篇文章已发布”的消息在表单顶部。
可能有 2 个管理员同时登录,并且几乎同时显示文章列表。Joomla 如何知道向哪个管理员显示“2 篇文章已发布”消息?答案是它使用 cookie。当管理员浏览器发送 HTTP GET 请求以显示文章列表时,它在请求中发送服务器之前发送给它的所有 cookie。其中一个会被 Joomla 会话功能用作存储给该用户的会话数据的索引。上面描述的 enqueueMessage
函数在这个会话数据中存储一条消息,并且当 Joomla 处理下一个 HTTP 请求时,它会看到那里存储了一条消息,并且会将它输出到页面。
你可以通过在全局配置中启用 Joomla 调试,然后点击每个网页左下角的 Joomla 小符号来查看会话数据中存储的内容。但是你不会看到任何排队消息,因为当 Joomla 输出排队消息时,它会从会话中删除该消息,以便它不会在下一个 HTTP 请求时再次显示。
示例 2 编辑文章
操作 1
这与上述相同,显示文章列表。
操作 3
管理员单击文章将其编辑。如果你在全局配置/网站参数中关闭搜索引擎友好 (SEF) 网址,那么你将看到每个文章标题后面的链接类似于 http://yourdomain.org/administrator/index.php?option=com_content&task=article.edit&id=1
。因此,发送到服务器的 HTTP GET 请求将把 _task_ 设置为 "article.edit",且这会导致实例化 `com_content` ArticleController 并调用其 `edit()` 函数。
类似于我们先前发现的,ArticleController.php 中没有 `edit` 函数,因此运行的是其扩展的类中的 `edit` 函数,即 libraries/src/MVC/Controller/FormController.php 中的 Joomla\CMS\MVC\Controller\FormController。它基本上检查用户是否允许执行此操作,如果允许,检查内容记录(通过在数据库中为文章记录设置 `checked_out` 和 `checked_out_time` 列),并设置一个重定向以显示文章编辑表单。
操作 4
该重定向导致一个 HTTP GET 到类似 http://yourdomain.org/administrator/index.php?option=com_content&view=article&layout=edit&id=1 的 URL。没有设置 _task_ 参数,因此这将由 `com_content` DisplayController 的 `display()` 函数处理。它将在 src/View/Article/HtmlView.php 中使用 `com_content` Article 视图类,并将使用 tmpl/article/edit.php(来自 `layout` 参数)做为模板文件。这会显示编辑单个文章的表单。
操作 5
当管理员单击 保存并关闭
时,这将导致一个带有这篇文章的字段值和 _task_ 设置为 "article.save" 的 HTTP POST 到服务器。这将路由到 `com_content` ArticleController 及其 `save()` 函数。再次地,`save` 函数缺失,因此它会回退到使用 ArticleController 扩展的类的 `save` 函数,即 libraries/src/MVC/Controller/FormController.php 中的 Joomla\CMS\MVC\Controller\FormController。此函数执行以下操作(其“正常路径”中):
- 检查用户是否允许执行操作
- 验证数据(即提交的文章字段)
- 保存数据(通过 ArticleModel)
- 签入记录
- 排入一条确认消息,表明保存成功
- 设置重定向返回到显示文章列表的表单
再做一次操作 1
与之前一样,这会显示文章列表以及排入的消息。