class EntityQueryAlter in Entity API 8
Defines a class for altering entity queries.
EntityQuery doesn't have an alter hook, forcing this class to operate on the underlying SQL query, duplicating the EntityQuery condition logic.
@internal
Hierarchy
- class \Drupal\entity\QueryAccess\EntityQueryAlter implements ContainerInjectionInterface
Expanded class hierarchy of EntityQueryAlter
1 file declares its use of EntityQueryAlter
- entity.module in ./
entity.module - Provides expanded entity APIs.
File
- src/
QueryAccess/ EntityQueryAlter.php, line 26
Namespace
Drupal\entity\QueryAccessView source
class EntityQueryAlter implements ContainerInjectionInterface {
/**
* 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 EntityQueryAlter object.
*
* @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(EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer, RequestStack $request_stack) {
$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('entity_field.manager'), $container
->get('entity_type.manager'), $container
->get('renderer'), $container
->get('request_stack'));
}
/**
* Alters the select query for the given entity type.
*
* @param \Drupal\Core\Database\Query\SelectInterface $query
* The select query.
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type.
*/
public function alter(SelectInterface $query, EntityTypeInterface $entity_type) {
if (!$entity_type
->hasHandlerClass('query_access')) {
return;
}
$entity_type_id = $entity_type
->id();
$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($query
->getMetaData('op') ?: 'view');
if ($conditions
->isAlwaysFalse()) {
$query
->where('1 = 0');
}
elseif (count($conditions)) {
$sql_conditions = $this
->mapConditions($conditions, $query);
$query
->condition($sql_conditions);
}
$this
->applyCacheability(CacheableMetadata::createFromObject($conditions));
}
/**
* Maps an entity type's access conditions to SQL conditions.
*
* @param \Drupal\entity\QueryAccess\ConditionGroup $conditions
* The access conditions.
* @param \Drupal\Core\Database\Query\SelectInterface $query
* The SQL query.
* @param bool $nested_inside_or
* Whether the access conditions are nested inside an OR condition.
*
* @return \Drupal\Core\Database\Query\ConditionInterface
* The SQL conditions.
*/
protected function mapConditions(ConditionGroup $conditions, SelectInterface $query, $nested_inside_or = FALSE) {
$sql_condition = $query
->conditionGroupFactory($conditions
->getConjunction());
$tables = new Tables($query);
$nested_inside_or = $nested_inside_or || $conditions
->getConjunction() == 'OR';
foreach ($conditions
->getConditions() as $condition) {
if ($condition instanceof ConditionGroup) {
$nested_sql_conditions = $this
->mapConditions($condition, $query, $nested_inside_or);
$sql_condition
->condition($nested_sql_conditions);
}
else {
// Access conditions don't specify a langcode.
$langcode = NULL;
$type = $nested_inside_or || $condition
->getOperator() === 'IS NULL' ? 'LEFT' : 'INNER';
$sql_field = $tables
->addField($condition
->getField(), $type, $langcode);
$value = $condition
->getValue();
$operator = $condition
->getOperator();
// Using LIKE/NOT LIKE ensures a case insensitive comparison.
// @see \Drupal\Core\Entity\Query\Sql\Condition::translateCondition().
$case_sensitive = $tables
->isFieldCaseSensitive($condition
->getField());
$operator_map = [
'=' => 'LIKE',
'<>' => 'NOT LIKE',
];
if ($case_sensitive === FALSE && isset($operator_map[$operator])) {
$operator = $operator_map[$operator];
$value = $query
->escapeLike($value);
}
$sql_condition
->condition($sql_field, $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() && $this
->hasCacheableMetadata($cacheable_metadata)) {
$build = [];
$cacheable_metadata
->applyTo($build);
$this->renderer
->render($build);
}
}
/**
* Check if the cacheable metadata is not empty.
*
* An empty cacheable metadata object has no context, tags, and is permanent.
*
* @param \Drupal\Core\Cache\CacheableMetadata $cacheable_metadata
* The cacheable metadata.
*
* @return bool
* TRUE if there is cacheability metadata, otherwise FALSE.
*/
protected function hasCacheableMetadata(CacheableMetadata $cacheable_metadata) {
return $cacheable_metadata
->getCacheMaxAge() !== Cache::PERMANENT || count($cacheable_metadata
->getCacheContexts()) > 0 || count($cacheable_metadata
->getCacheTags()) > 0;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
EntityQueryAlter:: |
protected | property | The entity field manager. | |
EntityQueryAlter:: |
protected | property | The entity type manager. | |
EntityQueryAlter:: |
protected | property | The renderer. | |
EntityQueryAlter:: |
protected | property | The request stack. | |
EntityQueryAlter:: |
public | function | Alters the select query for the given entity type. | |
EntityQueryAlter:: |
protected | function | Applies the cacheablity metadata to the current request. | |
EntityQueryAlter:: |
public static | function |
Instantiates a new instance of this class. Overrides ContainerInjectionInterface:: |
|
EntityQueryAlter:: |
protected | function | Check if the cacheable metadata is not empty. | |
EntityQueryAlter:: |
protected | function | Maps an entity type's access conditions to SQL conditions. | |
EntityQueryAlter:: |
public | function | Constructs a new EntityQueryAlter object. |