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('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 && 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()) { $build = []; $cacheable_metadata->applyTo($build); $this->renderer->render($build); } } }