You are here

class Query in Apigee Edge 8

Defines the entity query for Apigee Edge entities.

Query loader always tries to use the best Apigee Edge endpoint for retrieving already filtered results from Apigee Edge and only do the necessary filtering in the PHP side. It does it in the getFromStorage() method by filtering conditions to find those that can used directly on Apigee Edge. This process does not work on group conditions (OR, AND) it only supports direct field conditions added to the query. Group conditions always evaluated on the PHP side.

// This works.
$query
  ->condition('developerId', 'XY');

// But this does not.
$or = $query
  ->orConditionGroup() . $or
  ->condition('developerId', 'XY')
  ->condition('developerId', 'YX');
$query
  ->condition($or);

Hierarchy

Expanded class hierarchy of Query

File

src/Entity/Query/Query.php, line 48

Namespace

Drupal\apigee_edge\Entity\Query
View source
class Query extends QueryBase implements QueryInterface {

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * Constructs a Query object.
   *
   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
   *   The entity type definition.
   * @param string $conjunction
   *   - AND: all of the conditions on the query need to match.
   *   - OR: at least one of the conditions on the query need to match.
   * @param array $namespaces
   *   List of potential namespaces of the classes belonging to this query.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   */
  public function __construct(EntityTypeInterface $entity_type, string $conjunction, array $namespaces, EntityTypeManagerInterface $entity_type_manager) {
    parent::__construct($entity_type, $conjunction, $namespaces);
    $this->entityTypeManager = $entity_type_manager;
  }

  /**
   * {@inheritdoc}
   */
  public function execute() {

    // We have to allow getFromStorage() to remove unnecessary query conditions
    // so we have to run it before compile(). Example: DeveloperAppQuery
    // can load only apps of a specific developer by developerId or email.
    // If it does that by email then the email condition should be removed
    // because developer apps do not have email property only developerId.
    // Basically, DeveloperAppQuery already applies a condition on the returned
    // result because this function gets called.
    $all_records = $this
      ->getFromStorage();
    $filter = $this->condition
      ->compile($this);
    $result = array_filter($all_records, $filter);
    if ($this->count) {
      return count($result);
    }
    if ($this->sort) {
      uasort($result, function (EntityInterface $entity0, EntityInterface $entity1) : int {
        foreach ($this->sort as $sort) {
          $value0 = Condition::getProperty($entity0, $sort['field']);
          $value1 = Condition::getProperty($entity1, $sort['field']);
          $cmp = $value0 <=> $value1;
          if ($cmp === 0) {
            continue;
          }
          if ($sort['direction'] === 'DESC') {
            $cmp *= -1;
          }
          return $cmp;
        }
        return 0;
      });
    }
    $this
      ->initializePager();
    if ($this->range) {
      $result = array_slice($result, $this->range['start'], $this->range['length']);
    }
    return array_map(function (EntityInterface $entity) : string {
      return (string) $entity
        ->id();
    }, $result);
  }

  /**
   * Returns an array of properties that should be considered as entity ids.
   *
   * Usually one entity has one primary id, but in case of Apigee Edge
   * entities one entity could have multiple ids (primary keys).
   * Ex.: Developer => ['email', 'developerId'].
   *
   * @return string[]
   *   Array of property names that should be considered as unique entity ids.
   */
  protected function getEntityIdProperties() : array {
    $storage = $this->entityTypeManager
      ->getStorage($this->entityTypeId);

    /** @var \Drupal\apigee_edge\Entity\EdgeEntityInterface $entity */
    $entity = $storage
      ->create();
    return $entity::uniqueIdProperties();
  }

  /**
   * Loads entities from the entity storage for querying.
   *
   * @return \Drupal\Core\Entity\EntityInterface[]
   *   Array of matching entities.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   */
  protected function getFromStorage() : array {
    $storage = $this->entityTypeManager
      ->getStorage($this->entityTypeId);

    // The worst case: load all entities from Apigee Edge.
    $ids = NULL;
    $original_conditions =& $this->condition
      ->conditions();
    $filtered_conditions = [];
    foreach ($original_conditions as $key => $condition) {
      $filtered_conditions[$key] = $condition;
      $id = NULL;

      // Indicates whether we found a single entity id in this condition
      // or not.
      $id_found = FALSE;

      // \Drupal\Core\Entity\EntityStorageBase::buildPropertyQuery() always adds
      // conditions with IN this is the reason why the last part of this
      // condition is needed.
      if (in_array($condition['field'], $this
        ->getEntityIdProperties()) && (in_array($condition['operator'], [
        NULL,
        '=',
      ]) || $condition['operator'] === 'IN' && is_array($condition['value']) && count($condition['value']) === 1)) {
        if (is_array($condition['value'])) {
          $id = reset($condition['value']);
          $id_found = TRUE;
        }
        else {
          $id = $condition['value'];
          $id_found = TRUE;
        }
      }

      // We have to handle propertly when a developer probably unintentionally
      // passed an empty value (null, false, "", etc.) as a value of a condition
      // for a primary entity id. In this case we should return empty result
      // immediately because this condition can not be evaluated Apigee Edge
      // and we should not load all entities unnecessarily to get same result
      // after filtered the results in the PHP side.
      if ($id_found) {
        if (empty($id)) {
          return [];
        }
        else {
          $ids = [
            $id,
          ];
          unset($filtered_conditions[$key]);

          // If we found an id field in the query do not look for an another
          // because that would not make any sense to query one entity by
          // both id fields. (Where in theory both id field could refer to a
          // different entity.)
          break;
        }
      }
    }

    // Remove conditions that is going to be applied on Apigee Edge
    // (by calling the proper API with the proper parameters).
    // We do not want to apply the same filters on the result in execute()
    // again.
    $original_conditions = $filtered_conditions;
    return $storage
      ->loadMultiple($ids);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
Query::$entityTypeManager protected property The entity type manager.
Query::execute public function Execute the query. Overrides QueryInterface::execute
Query::getEntityIdProperties protected function Returns an array of properties that should be considered as entity ids.
Query::getFromStorage protected function Loads entities from the entity storage for querying. 1
Query::__construct public function Constructs a Query object. Overrides QueryBase::__construct
QueryBase::$accessCheck protected property Whether access check is requested or not. Defaults to TRUE.
QueryBase::$aggregate protected property The list of aggregate expressions.
QueryBase::$allRevisions protected property Flag indicating whether to query the current revision or all revisions.
QueryBase::$alterMetaData protected property The query metadata for alter purposes.
QueryBase::$alterTags protected property The query tags.
QueryBase::$condition protected property Conditions. 1
QueryBase::$conditionAggregate protected property Aggregate Conditions
QueryBase::$count protected property TRUE if this is a count query, FALSE if it isn't.
QueryBase::$entityType protected property Information about the entity type. 1
QueryBase::$entityTypeId protected property The entity type this query runs against.
QueryBase::$groupBy protected property The list of columns to group on.
QueryBase::$latestRevision protected property Flag indicating whether to query the latest revision.
QueryBase::$namespaces protected property List of potential namespaces of the classes belonging to this query.
QueryBase::$pager protected property The query pager data.
QueryBase::$range protected property The query range.
QueryBase::$sort protected property The list of sorts.
QueryBase::$sortAggregate protected property The list of sorts over the aggregate results.
QueryBase::accessCheck public function Overrides QueryInterface::accessCheck
QueryBase::addMetaData public function Adds additional metadata to the query. Overrides AlterableInterface::addMetaData
QueryBase::addTag public function Adds a tag to a query. Overrides AlterableInterface::addTag
QueryBase::aggregate public function
QueryBase::allRevisions public function Queries all the revisions. Overrides QueryInterface::allRevisions
QueryBase::andConditionGroup public function Creates a new group of conditions ANDed together. Overrides QueryInterface::andConditionGroup
QueryBase::condition public function Add a condition to the query or a condition group. Overrides QueryInterface::condition 1
QueryBase::conditionAggregate public function
QueryBase::conditionGroupFactory protected function Creates an object holding a group of conditions.
QueryBase::count public function Makes this a count query. Overrides QueryInterface::count
QueryBase::currentRevision public function Queries the current revision. Overrides QueryInterface::currentRevision
QueryBase::exists public function Queries for a non-empty value on a field. Overrides QueryInterface::exists
QueryBase::getAggregationAlias protected function Generates an alias for a field and its aggregated function.
QueryBase::getClass public static function Finds a class in a list of namespaces.
QueryBase::getEntityTypeId public function Gets the ID of the entity type for this query. Overrides QueryInterface::getEntityTypeId
QueryBase::getMetaData public function Retrieves a given piece of metadata. Overrides AlterableInterface::getMetaData
QueryBase::getNamespaces public static function Gets a list of namespaces of the ancestors of a class.
QueryBase::groupBy public function
QueryBase::hasAllTags public function Determines if a given query has all specified tags. Overrides AlterableInterface::hasAllTags
QueryBase::hasAnyTag public function Determines if a given query has any specified tag. Overrides AlterableInterface::hasAnyTag
QueryBase::hasTag public function Determines if a given query has a given tag. Overrides AlterableInterface::hasTag
QueryBase::initializePager protected function Gets the total number of results and initialize a pager for the query.
QueryBase::latestRevision public function Queries the latest revision. Overrides QueryInterface::latestRevision
QueryBase::notExists public function Queries for an empty field. Overrides QueryInterface::notExists
QueryBase::orConditionGroup public function Creates a new group of conditions ORed together. Overrides QueryInterface::orConditionGroup
QueryBase::pager public function Enables a pager for the query. Overrides QueryInterface::pager
QueryBase::range public function Overrides QueryInterface::range
QueryBase::sort public function Overrides QueryInterface::sort
QueryBase::sortAggregate public function
QueryBase::tableSort public function Enables sortable tables for this query. Overrides QueryInterface::tableSort
QueryBase::__clone public function Makes sure that the Condition object is cloned as well. 1