9.6. 带参数的路由
我们可以在URL中使用参数来定义路由。它们的工作方式与视图中的上下文过滤器相同。例如,我们可以在URL中传递各种实体的ID、文本字符串或由逗号或加号分隔的顺序ID。在本课程中,我们将传递节点的ID,并在内容中显示该节点的标题和正文。
代码示例可以在GitHub上查看:
https://github.com/levmyshkin/drupalbook8
让我们将路由添加到我们的drupalbook.routing.yml模块文件中:
drupalbook.display_node:
path: '/display-node/{node}'
defaults:
_controller: '\Drupal\drupalbook\Controller\DisplayNode::content'
_title_callback: '\Drupal\drupalbook\Controller\DisplayNode::getTitle'
requirements:
_custom_access: '\Drupal\drupalbook\Controller\DisplayNode::access'
options:
parameters:
node:
type: entity:node
在路径中,我们传递第二个参数{node},在URL中我们将写入常规ID:/display-node/101,但是准备好的节点对象将传递到我们的控制器中。为此,我们在选项中指定参数,指明应该传递什么以及输出是什么。
options:
parameters:
node: # 参数的名称,{}之间的内容,可以是node1、node2,如果传递两个不同的参数。
type: entity: node # 输出的内容,我们可以在控制器内使用该实体的对象。
我还确定了使用_title_callback参数在哪个方法中显示标题。我们将限制匿名用户的文章输出,为此我们使用_custom_access参数,在其中指明在哪个方法中施加各种限制。
现在当我们弄清楚路由的描述后,接下来编写该路由的类。
modules/custom/drupalbook/src/Controller/DisplayNode.php:
<?php
/**
* @file
* Contains \Drupal\drupalbook\Controller\DisplayNode.
*/
namespace Drupal\drupalbook\Controller;
use Drupal\Core\Access\AccessResult;
use Drupal\node\NodeInterface;
/**
* Provides route responses for the DrupalBook module.
*/
class DisplayNode {
/**
* Returns a simple page.
*
* @return array
* A simple renderable array.
*/
public function content(NodeInterface $node) {
$element = array(
'#markup' => $node->body->value,
);
return $element;
}
/**
* Checks access for this controller.
*/
public function access(NodeInterface $node) {
$user = \Drupal::currentUser();
if ($node->getType() == 'article' && !in_array('authenticated', $user->getRoles())) {
return AccessResult::forbidden();
}
return AccessResult::allowed();
}
/**
* Returns a page title.
*/
public function getTitle(NodeInterface $node) {
return $node->getTitle();
}
}
我为我的路由创建了一个单独的类,以免将所有内容都放在一个控制器类中。让我们来看一下每一行是什么意思。
\drupalbook\Controller;
我们指明了控制器文件所在的位置。
\Core\Access\AccessResult;
use Drupal\node\NodeInterface;
我们包含了AccessResult类文件——我们将使用它来输出403错误,以及NodeInterface——我们将在控制器的参数方法中使用它来获取节点对象。
function content(NodeInterface $node) {
$element = array(
'#markup' => $node->body->value,
);
return $element;
}
这里,请注意参数,我们获取节点对象,Drupal会自动从URL转换ID并传递给我们,以便我们不需要再次加载节点对象,而是直接使用它。$node->body->value,这是我们从节点中获取字段值的方式,但我们将在下一课中详细处理如何操作对象。最后,我们返回一个带有#markup的数组来输出我们的节点到文本页面。
function access(NodeInterface $node) {
$user = \Drupal::currentUser();
if ($node->getType() == 'article' && !in_array('authenticated', $user->getRoles())) {
return AccessResult::forbidden();
}
return AccessResult::allowed();
}
首先,我们使用Drupal的currentUser()方法获取当前用户对象,然后使用该对象获取当前用户的角色。在if检查中,我们检查节点的内容类型和用户角色,匿名用户将收到403错误,如果一切正常,我们继续执行并返回allowed(),也就是简单地批准我们的检查。让我们打开AccessResult类,看看该类还有哪些方法。为此,在PhpStorm中需要按两次shift并输入类名:
在这里,你可以找到用于检查权限的以下方法:
neutral allowed forbidden allowedIf forbiddenIf allowedIfHasPermission allowedIfHasPermissions isAllowed isForbidden isNeutral orIf andIf
你可以尝试使用allowedIfHasPermission(),例如,为不同角色的路由设置不同的权限。例如,在你的模块中创建一个新的权限,并在控制器中使用它。虽然在yml文件中使用权限会更简单一些。但通过AccessResult类,你可以灵活地描述内容访问的逻辑,例如,“允许12到16点间的授权用户访问内容,拥有高级账户角色的用户可以全天访问。”你可以尝试并实现这种访问权限差异化。
如你所见,Drupal提供了多种方法来实现内容访问控制,这非常方便,因为99%的情况只需使用一种权限就足够了,这对于区分访问权限来说已经足够了。
function getTitle(NodeInterface $node) {
return $node->getTitle();
}
这里我们只是简单地返回节点的标题,但我们可以扩展回调的功能。例如,像这样:
function getTitle(NodeInterface $node) {
$user = \Drupal::currentUser();
if ($node->getType() == 'article' && !in_array('authenticated', $user->getRoles())) {
return 'Premium content: ' . $node->getTitle();
}
else {
return 'Free access content: ' . $node->getTitle();
}
}
或者在标题中显示发布日期:
如你所见,Drupal提供了灵活配置路由和控制器的能力。因此,当客户有关于如何展示材料到网站的想法时,你可以通过Drupal API和一些PHP代码轻松实现。
代码示例可以在GitHub上查看:
https://github.com/levmyshkin/drupalbook8