如何通过 URL 或菜单路径查询 Drupal 快捷方式 (8.x/9.x)?

How do I query a Drupal Shortcut (8.x/9.x) by its URL or Menu Path?

我们正在对 in-house Drupal 安装配置文件进行更新,经常使用的菜单路径之一正在更改。我们的大多数安装都在快捷方式中引用该菜单路径(通过核心中的“快捷方式”模块)。在更新挂钩中,我们希望能够查询这些快捷方式并更新它们。

感觉这应该很简单,但出于某种原因,我们发现很难通过 url 查询快捷方式。我们可以按标题查询它们,但这似乎很脆弱(因为安装之间的标题可能不同,可能因本地化等原因而不同)。

我们尝试了以下方法,但这会导致错误消息 'link' not found

// This does NOT work.
$shortcuts_needing_update = 
  \Drupal::entityTypeManager()
    ->getStorage('shortcut')
    ->loadByProperties([
      'link' => [
        'internal:/admin/timeline-management',
      ],
    ]);

// This works, but is fragile.
$shortcuts_needing_update = 
  \Drupal::entityTypeManager()
    ->getStorage('shortcut')
    ->loadByProperties([
      'title' => 'My shortcut',
    ]);

根据 \Drupal\shortcut\Entity\Shortcut::baseFieldDefinitions()\Drupal\shortcut\Controller\ShortcutSetController::addShortcutLinkInline() 中的代码,很明显快捷方式实体有一个名为 link 的 属性,可以将其设置为包含 uri 键,但似乎无法通过此 属性 进行查询,即使它是一个基字段。

查看数据库,Drupal 似乎将 URL 存储在名为 link__uri:

的数据库列中

TL;DR 这意味着这有效:

$shortcuts_needing_update = 
  \Drupal::entityTypeManager()
    ->getStorage('shortcut')
    ->loadByProperties([
      'link__uri' => 'internal:/admin/old/path',
    ]
  );

如果您想知道出现这种情况的微妙原因,请继续阅读。

Drupal 的数据库层使用可插入的“table 映射”对象来告诉它如何将实体(如快捷方式)映射到一个或多个数据库 table 和数据库 table列。在默认 table 映射 (\Drupal\Core\Entity\Sql\DefaultTableMapping) 中,为字段生成列名的逻辑如下所示:

如上所示,如果一个字段表明它允许“共享”table存储,并且该字段有多个属性(urititle等),那么映射将字段扁平化为每个 属性 的不同列,并以字段名称为前缀。因此,具有 link => ['uri' => 'xyz']] 的快捷方式实体成为数据库中值为 xyz 的列 link__uri

对于像节点这样的实体,您不会经常看到这种情况,这就是为什么这在这里看起来很奇怪。我通常习惯于看到一个单独的数据库 table 用于 link 字段之类的东西。那是因为节点和其他内容实体通常不允许共享 table 存储它们的字段。

映射如何确定字段是否应使用共享 table 存储?该逻辑如下所示:

因此,默认 table 映射将仅在特定情况下对字段使用共享 table 存储:

  1. 该字段不能有自定义存储处理程序(在此处查看,因为快捷方式不提供自己的存储逻辑)。
  2. 该字段必须是一个基字段(快捷方式没有没有一个link,因此该字段被定义为OP中提到的基字段) .
  3. 该字段必须是单值的(签出 -- 快捷方式只有一个 link)。
  4. 该字段一定没有被删除(检查;再一次,没有 link 字段的快捷方式是什么?)。

节点或其他内容实体通常不满足这种特定情况,这就是为什么这里有点令人惊讶的原因。

我们可以通过使用 Devel PHP 直接向 table 映射查询快捷方式来确认这一点,代码如下:

$shortcut_table_mapping = 
  \Drupal::entityTypeManager()
    ->getStorage('shortcut')
    ->getTableMapping();

$efm = \Drupal::service('entity_field.manager');
$storage_definitions = $efm->getFieldStorageDefinitions('shortcut');

$link_storage_definition = $storage_definitions['link'];

$has_dedicated_storage = $shortcut_table_mapping->requiresDedicatedTableStorage($link_storage_definition);
$link_column = $shortcut_table_mapping->getFieldColumnName($link_storage_definition, 'url');

dpm($has_dedicated_storage, 'has_dedicated_storage(link)');
dpm($link_column, 'link_column');

结果如下: