Drupal 8:在视图中创建子查询
您以前可能去过那里。您正在使用的Drupal View看起来很棒,并且具有您需要的所有数据和字段,但是当您仔细查看结果时,您会发现有些问题。查看生成的SQL查询后,您会发现其中一个联接存在问题,这会导致计数减少。最终,您需要删除此联接,但实际上您需要包含在结果中的数据。
创建子查询可以使您从特定字段中提取数据,而无需添加另一个导致结果不正确的联接。
前几天我在一个视图中遇到了这个问题,该视图合并了“下载计数”模块中的数据。此模块记录用户在您的站点上下载文件的操作。尽管确实提供了一些Views集成,但我添加了其他一些关系,这些关系导致下载计数不正确。经过一番调查后,我知道我需要创建一个子查询以将下载计数的值注入到View的输出中。
做到这一点的最佳方法是创建一个View字段处理程序,该处理程序将在字段设置中创建一个子查询。要创建字段处理程序插件,您需要在my_module / src / Plugin / views / field位置创建一个类。以下是一个名为FileDownloadCount的类,该类扩展了默认的NumericField Views字段。这是在类级别的注释,它使Views可以了解此字段,因此这一点非常重要。我的插件的完整文件路径为my_module / src / Plugin / views / field / FileDownloadCount.php。
<?php namespace Drupal\my_module\Plugin\views\field; use Drupal\views\Plugin\views\field\NumericField; use Drupal\views\ResultRow; /** * Field handler for search score. * * @ingroup views_field_handlers * * @ViewsField("file_download_count") */ class FileDownloadCount extends NumericField { }
Views会自动拾取它,但是直到我们添加一些其他方法后它才做任何事情。
我们需要添加的第一件事是一种query()方法,以便Views知道子查询并将其添加到显示输出中。通过创建数据库查询的新实例,然后将其作为联接添加到主查询中来创建子查询,就好像它是真实表一样。此处的子查询(由于我要获取的数据)取决于file_managed表,因此这构成了我们创建的联接的基础。
这是完整的方法,带有注释以帮助分解发生的情况。
/** * {@inheritdoc} */ public function query() { // 将子查询添加到查询中,以找到下载计数。 $subQuery = \Drupal::database()->select('download_count', 'download_count'); $subQuery->addField('download_count', 'fid'); $subQuery->addExpression("COUNT(fid)", 'file_download_count'); $subQuery->groupBy("download_count.fid"); // 将子查询添加为联接的组件。 $joinDefinition = [ 'table formula' => $subQuery, 'field' => 'fid', 'left_table' => 'file_managed', 'left_field' => 'fid', 'adjust' => TRUE, ]; // 创建一个连接对象,并在主查询和子查询之间创建一个关系。 $join = \Drupal::service('plugin.manager.views.join')->createInstance('standard', $joinDefinition); $this->query->addRelationship('download_data', $join, 'file_managed'); // 将字段添加到“视图”界面。 $this->query->addField(NULL, 'file_download_count', 'file_download_count'); }
现在,我们已经有了子查询,并且在主查询中添加了字段,因此我们现在需要的数据将通过。最重要的是,查询的输出为null时的默认值。如果从未下载过文件,则将返回此值,因此在项目开始时会非常定期地进行,并且此功能也很难通过以前的测试。为了解决这个问题,我们只需要检测该值是否为null并使用render()方法将其重置为0 。
/** * {@inheritdoc} */ public function render(ResultRow $values) { if (is_null($values->file_download_count)) { // 确保将空值打印为0。 $values->file_download_count = 0; } return parent::render($values); }
当渲染输出时,Views会自动调用该渲染。
最后,最好还让您的新字段具有单击排序功能,这在创建表时尤为重要,因为允许对列进行排序是一个非常好的功能。为此,您需要创建一个名为的方法clickSort(),该addOderBy()方法在查询中调用该方法。当需要按字段对视图进行排序时,视图将自动调用此方法。
/** * {@inheritdoc} */ public function clickSort($order) { if (isset($this->field_alias)) { $params = $this->options['group_type'] != 'group' ? ['function' => $this->options['group_type']] : []; $this->query->addOrderBy(NULL, 'file_download_count', $order, $this->field_alias, $params); } }
一切就绪后,我们只需要像普通字段一样将此字段添加到视图中即可。您还需要在“视图”表输出中配置排序,以使排序也能正常工作。
我在互联网上看到了几篇文章,展示了如何在Views中创建子查询,但是它们往往会遗漏将数据也添加到View输出中的关键项。上面的解决方案将添加子查询并将数据添加到您的视图中,以便您可以执行一些操作。