ExistsSubqueryField.php
<?php
declare(strict_types=1);
namespace Drupal\my_module\Plugin\views\field;
use Drupal\views\Attribute\ViewsField;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\ResultRow;
/**
* Sortable yes/no column backed by an EXISTS subquery from views data.
*
* '@base' in the 'exists subquery' definition is replaced with the alias of
* the base table.
*/
#[ViewsField('exists_subquery')]
class ExistsSubquery extends FieldPluginBase {
public function query() {
$this->ensureMyTable();
$exists = 'EXISTS (' . str_replace('@base', $this->tableAlias, $this->definition['exists subquery']) . ')';
$this->field_alias = $this->query->addField(NULL, "CASE WHEN $exists THEN 1 ELSE 0 END", $this->realField);
}
public function clickSort($order) {
$this->query->addOrderBy(NULL, NULL, $order, $this->field_alias);
}
public function render(ResultRow $values) {
return $this->getValue($values) ? $this->t('yes') : $this->t('no');
}
}ExistsSubqueryFilter.php
<?php
declare(strict_types=1);
namespace Drupal\my_module\Plugin\views\filter;
use Drupal\views\Attribute\ViewsFilter;
use Drupal\views\Plugin\views\filter\BooleanOperator;
/**
* Yes/no filter backed by an EXISTS subquery from views data.
*/
#[ViewsFilter('exists_subquery')]
class ExistsSubquery extends BooleanOperator {
public function defaultExposeOptions() {
parent::defaultExposeOptions();
// BooleanOperator exposes as required without a stored value, which fails
// form validation. Default to "- Any -" instead.
$this->options['expose']['required'] = FALSE;
$this->options['value'] = 'All';
}
public function query() {
if ($this->value === 'All') {
return;
}
$this->ensureMyTable();
$exists = 'EXISTS (' . str_replace('@base', $this->tableAlias, $this->definition['exists subquery']) . ')';
$this->query->addWhereExpression($this->options['group'], empty($this->value) ? "NOT $exists" : $exists);
}
}MyModuleViewsHooks.php
<?php
declare(strict_types=1);
namespace Drupal\my_module\Hook;
use Drupal\Core\Hook\Attribute\Hook;
use Drupal\Core\StringTranslation\StringTranslationTrait;
final class MyModuleViewsHooks {
use StringTranslationTrait;
#[Hook('views_data_alter')]
public function viewsDataAlter(array &$data): void {
$subquery = 'SELECT 1 FROM {node__field_related_content} ref WHERE ref.field_related_content_target_id = @base.nid';
$data['node_field_data']['is_referenced'] = [
'title' => $this->t('Is referenced'),
'field' => ['id' => 'exists_subquery', 'click sortable' => TRUE, 'exists subquery' => $subquery],
'filter' => ['id' => 'exists_subquery', 'label' => $this->t('Is referenced'), 'exists subquery' => $subquery],
];
}
}
08.10.2026 | Lothar Ferreira Neumann