class ViewsQueryAlter in Entity API 8
Defines a class for altering views queries.
@internal
Hierarchy
- class \Drupal\entity\QueryAccess\ViewsQueryAlter implements ContainerInjectionInterface
Expanded class hierarchy of ViewsQueryAlter
1 file declares its use of ViewsQueryAlter
- entity.module in ./
entity.module - Provides expanded entity APIs.
File
- src/
QueryAccess/ ViewsQueryAlter.php, line 25
Namespace
Drupal\entity\QueryAccessView source
class ViewsQueryAlter implements ContainerInjectionInterface {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
/**
* The entity field manager.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected $entityFieldManager;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The renderer.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* Constructs a new ViewsQueryAlter object.
*
* @param \Drupal\Core\Database\Connection $connection
* The database connection.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
* The entity field manager.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer.
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
* The request stack.
*/
public function __construct(Connection $connection, EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer, RequestStack $request_stack) {
$this->connection = $connection;
$this->entityFieldManager = $entity_field_manager;
$this->entityTypeManager = $entity_type_manager;
$this->renderer = $renderer;
$this->requestStack = $request_stack;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static($container
->get('database'), $container
->get('entity_field.manager'), $container
->get('entity_type.manager'), $container
->get('renderer'), $container
->get('request_stack'));
}
/**
* Alters the given views query.
*
* @param \Drupal\views\Plugin\views\query\Sql $query
* The views query.
* @param \Drupal\views\ViewExecutable $view
* The view.
*/
public function alter(Sql $query, ViewExecutable $view) {
$table_info = $query
->getEntityTableInfo();
$base_table = reset($table_info);
if (empty($base_table['entity_type']) || $base_table['relationship_id'] != 'none') {
return;
}
$entity_type_id = $base_table['entity_type'];
$entity_type = $this->entityTypeManager
->getDefinition($entity_type_id);
if (!$entity_type
->hasHandlerClass('query_access')) {
return;
}
$storage = $this->entityTypeManager
->getStorage($entity_type_id);
if (!$storage instanceof SqlContentEntityStorage) {
return;
}
/** @var \Drupal\entity\QueryAccess\QueryAccessHandlerInterface $query_access */
$query_access = $this->entityTypeManager
->getHandler($entity_type_id, 'query_access');
$conditions = $query_access
->getConditions('view');
if ($conditions
->isAlwaysFalse()) {
$query
->addWhereExpression(0, '1 = 0');
}
elseif (count($conditions)) {
// Store the data table, in case mapConditions() needs to join it in.
$base_table['data_table'] = $entity_type
->getDataTable();
$field_storage_definitions = $this->entityFieldManager
->getFieldStorageDefinitions($entity_type_id);
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
$table_mapping = $storage
->getTableMapping();
$sql_conditions = $this
->mapConditions($conditions, $query, $base_table, $field_storage_definitions, $table_mapping);
$query
->addWhere(0, $sql_conditions);
}
$this
->applyCacheability(CacheableMetadata::createFromObject($conditions));
}
/**
* Maps an entity type's access conditions to views SQL conditions.
*
* @param \Drupal\entity\QueryAccess\ConditionGroup $conditions
* The access conditions.
* @param \Drupal\views\Plugin\views\query\Sql $query
* The views query.
* @param array $base_table
* The base table information.
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $field_storage_definitions
* The field storage definitions.
* @param \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping
* The table mapping.
*
* @return \Drupal\Core\Database\Query\ConditionInterface
* The SQL conditions.
*/
protected function mapConditions(ConditionGroup $conditions, Sql $query, array $base_table, array $field_storage_definitions, DefaultTableMapping $table_mapping) {
$sql_condition = new SqlCondition($conditions
->getConjunction());
foreach ($conditions
->getConditions() as $condition) {
if ($condition instanceof ConditionGroup) {
$nested_sql_conditions = $this
->mapConditions($condition, $query, $base_table, $field_storage_definitions, $table_mapping);
$sql_condition
->condition($nested_sql_conditions);
}
else {
$field = $condition
->getField();
$property_name = NULL;
if (strpos($field, '.') !== FALSE) {
list($field, $property_name) = explode('.', $field);
}
// Skip unknown fields.
if (!isset($field_storage_definitions[$field])) {
continue;
}
$field_storage_definition = $field_storage_definitions[$field];
if (!$property_name) {
$property_name = $field_storage_definition
->getMainPropertyName();
}
$column = $table_mapping
->getFieldColumnName($field_storage_definition, $property_name);
if ($table_mapping
->requiresDedicatedTableStorage($field_storage_definitions[$field])) {
if ($base_table['revision']) {
$dedicated_table = $table_mapping
->getDedicatedRevisionTableName($field_storage_definition);
}
else {
$dedicated_table = $table_mapping
->getDedicatedDataTableName($field_storage_definition);
}
// Views defaults to LEFT JOIN. For simplicity, we don't try to
// use an INNER JOIN when it's safe to do so (AND conjunctions).
$alias = $query
->ensureTable($dedicated_table);
}
elseif ($base_table['revision'] && !$field_storage_definition
->isRevisionable()) {
// Workaround for #2652652, which causes $query->ensureTable()
// to not work in this case, due to a missing relationship.
if ($data_table = $query
->getTableInfo($base_table['data_table'])) {
$alias = $data_table['alias'];
}
else {
$configuration = [
'type' => 'INNER',
'table' => $base_table['data_table'],
'field' => 'id',
'left_table' => $base_table['alias'],
'left_field' => 'id',
];
/** @var \Drupal\Views\Plugin\views\join\JoinPluginBase $join */
$join = Views::pluginManager('join')
->createInstance('standard', $configuration);
$alias = $query
->addRelationship($base_table['data_table'], $join, $data_table);
}
}
else {
$alias = $base_table['alias'];
}
$value = $condition
->getValue();
$operator = $condition
->getOperator();
// Using LIKE/NOT LIKE ensures a case insensitive comparison.
// @see \Drupal\Core\Entity\Query\Sql\Condition::translateCondition().
$property_definitions = $field_storage_definition
->getPropertyDefinitions();
$case_sensitive = $property_definitions[$property_name]
->getSetting('case_sensitive');
$operator_map = [
'=' => 'LIKE',
'<>' => 'NOT LIKE',
];
if ($case_sensitive === FALSE && isset($operator_map[$operator])) {
$operator = $operator_map[$operator];
$value = $this->connection
->escapeLike($value);
}
$sql_condition
->condition("{$alias}.{$column}", $value, $operator);
}
}
return $sql_condition;
}
/**
* Applies the cacheablity metadata to the current request.
*
* @param \Drupal\Core\Cache\CacheableMetadata $cacheable_metadata
* The cacheability metadata.
*/
protected function applyCacheability(CacheableMetadata $cacheable_metadata) {
$request = $this->requestStack
->getCurrentRequest();
if ($request
->isMethodCacheable() && $this->renderer
->hasRenderContext()) {
$build = [];
$cacheable_metadata
->applyTo($build);
$this->renderer
->render($build);
}
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
ViewsQueryAlter:: |
protected | property | The database connection. | |
ViewsQueryAlter:: |
protected | property | The entity field manager. | |
ViewsQueryAlter:: |
protected | property | The entity type manager. | |
ViewsQueryAlter:: |
protected | property | The renderer. | |
ViewsQueryAlter:: |
protected | property | The request stack. | |
ViewsQueryAlter:: |
public | function | Alters the given views query. | |
ViewsQueryAlter:: |
protected | function | Applies the cacheablity metadata to the current request. | |
ViewsQueryAlter:: |
public static | function |
Instantiates a new instance of this class. Overrides ContainerInjectionInterface:: |
|
ViewsQueryAlter:: |
protected | function | Maps an entity type's access conditions to views SQL conditions. | |
ViewsQueryAlter:: |
public | function | Constructs a new ViewsQueryAlter object. |