Drupal PHP Cross Reference Content Management Systems

Source: /includes/entity.inc - 1365 lines - 46174 bytes - Summary - Text - Print

   1  <?php
   2  
   3  /**
   4   * Interface for entity controller classes.
   5   *
   6   * All entity controller classes specified via the 'controller class' key
   7   * returned by hook_entity_info() or hook_entity_info_alter() have to implement
   8   * this interface.
   9   *
  10   * Most simple, SQL-based entity controllers will do better by extending
  11   * DrupalDefaultEntityController instead of implementing this interface
  12   * directly.
  13   */
  14  interface DrupalEntityControllerInterface {
  15  
  16    /**
  17     * Constructor.
  18     *
  19     * @param $entityType
  20     *   The entity type for which the instance is created.
  21     */
  22    public function __construct($entityType);
  23  
  24    /**
  25     * Resets the internal, static entity cache.
  26     *
  27     * @param $ids
  28     *   (optional) If specified, the cache is reset for the entities with the
  29     *   given ids only.
  30     */
  31    public function resetCache(array $ids = NULL);
  32  
  33    /**
  34     * Loads one or more entities.
  35     *
  36     * @param $ids
  37     *   An array of entity IDs, or FALSE to load all entities.
  38     * @param $conditions
  39     *   An array of conditions in the form 'field' => $value.
  40     *
  41     * @return
  42     *   An array of entity objects indexed by their ids. When no results are
  43     *   found, an empty array is returned.
  44     */
  45    public function load($ids = array(), $conditions = array());
  46  }
  47  
  48  /**
  49   * Default implementation of DrupalEntityControllerInterface.
  50   *
  51   * This class can be used as-is by most simple entity types. Entity types
  52   * requiring special handling can extend the class.
  53   */
  54  class DrupalDefaultEntityController implements DrupalEntityControllerInterface {
  55  
  56    /**
  57     * Static cache of entities.
  58     *
  59     * @var array
  60     */
  61    protected $entityCache;
  62  
  63    /**
  64     * Entity type for this controller instance.
  65     *
  66     * @var string
  67     */
  68    protected $entityType;
  69  
  70    /**
  71     * Array of information about the entity.
  72     *
  73     * @var array
  74     *
  75     * @see entity_get_info()
  76     */
  77    protected $entityInfo;
  78  
  79    /**
  80     * Additional arguments to pass to hook_TYPE_load().
  81     *
  82     * Set before calling DrupalDefaultEntityController::attachLoad().
  83     *
  84     * @var array
  85     */
  86    protected $hookLoadArguments;
  87  
  88    /**
  89     * Name of the entity's ID field in the entity database table.
  90     *
  91     * @var string
  92     */
  93    protected $idKey;
  94  
  95    /**
  96     * Name of entity's revision database table field, if it supports revisions.
  97     *
  98     * Has the value FALSE if this entity does not use revisions.
  99     *
 100     * @var string
 101     */
 102    protected $revisionKey;
 103  
 104    /**
 105     * The table that stores revisions, if the entity supports revisions.
 106     *
 107     * @var string
 108     */
 109    protected $revisionTable;
 110  
 111    /**
 112     * Whether this entity type should use the static cache.
 113     *
 114     * Set by entity info.
 115     *
 116     * @var boolean
 117     */
 118    protected $cache;
 119  
 120    /**
 121     * Constructor: sets basic variables.
 122     */
 123    public function __construct($entityType) {
 124      $this->entityType = $entityType;
 125      $this->entityInfo = entity_get_info($entityType);
 126      $this->entityCache = array();
 127      $this->hookLoadArguments = array();
 128      $this->idKey = $this->entityInfo['entity keys']['id'];
 129  
 130      // Check if the entity type supports revisions.
 131      if (!empty($this->entityInfo['entity keys']['revision'])) {
 132        $this->revisionKey = $this->entityInfo['entity keys']['revision'];
 133        $this->revisionTable = $this->entityInfo['revision table'];
 134      }
 135      else {
 136        $this->revisionKey = FALSE;
 137      }
 138  
 139      // Check if the entity type supports static caching of loaded entities.
 140      $this->cache = !empty($this->entityInfo['static cache']);
 141    }
 142  
 143    /**
 144     * Implements DrupalEntityControllerInterface::resetCache().
 145     */
 146    public function resetCache(array $ids = NULL) {
 147      if (isset($ids)) {
 148        foreach ($ids as $id) {
 149          unset($this->entityCache[$id]);
 150        }
 151      }
 152      else {
 153        $this->entityCache = array();
 154      }
 155    }
 156  
 157    /**
 158     * Implements DrupalEntityControllerInterface::load().
 159     */
 160    public function load($ids = array(), $conditions = array()) {
 161      $entities = array();
 162  
 163      // Revisions are not statically cached, and require a different query to
 164      // other conditions, so separate the revision id into its own variable.
 165      if ($this->revisionKey && isset($conditions[$this->revisionKey])) {
 166        $revision_id = $conditions[$this->revisionKey];
 167        unset($conditions[$this->revisionKey]);
 168      }
 169      else {
 170        $revision_id = FALSE;
 171      }
 172  
 173      // Create a new variable which is either a prepared version of the $ids
 174      // array for later comparison with the entity cache, or FALSE if no $ids
 175      // were passed. The $ids array is reduced as items are loaded from cache,
 176      // and we need to know if it's empty for this reason to avoid querying the
 177      // database when all requested entities are loaded from cache.
 178      $passed_ids = !empty($ids) ? array_flip($ids) : FALSE;
 179      // Try to load entities from the static cache, if the entity type supports
 180      // static caching.
 181      if ($this->cache && !$revision_id) {
 182        $entities += $this->cacheGet($ids, $conditions);
 183        // If any entities were loaded, remove them from the ids still to load.
 184        if ($passed_ids) {
 185          $ids = array_keys(array_diff_key($passed_ids, $entities));
 186        }
 187      }
 188  
 189      // Load any remaining entities from the database. This is the case if $ids
 190      // is set to FALSE (so we load all entities), if there are any ids left to
 191      // load, if loading a revision, or if $conditions was passed without $ids.
 192      if ($ids === FALSE || $ids || $revision_id || ($conditions && !$passed_ids)) {
 193        // Build the query.
 194        $query = $this->buildQuery($ids, $conditions, $revision_id);
 195        $queried_entities = $query
 196          ->execute()
 197          ->fetchAllAssoc($this->idKey);
 198      }
 199  
 200      // Pass all entities loaded from the database through $this->attachLoad(),
 201      // which attaches fields (if supported by the entity type) and calls the
 202      // entity type specific load callback, for example hook_node_load().
 203      if (!empty($queried_entities)) {
 204        $this->attachLoad($queried_entities, $revision_id);
 205        $entities += $queried_entities;
 206      }
 207  
 208      if ($this->cache) {
 209        // Add entities to the cache if we are not loading a revision.
 210        if (!empty($queried_entities) && !$revision_id) {
 211          $this->cacheSet($queried_entities);
 212        }
 213      }
 214  
 215      // Ensure that the returned array is ordered the same as the original
 216      // $ids array if this was passed in and remove any invalid ids.
 217      if ($passed_ids) {
 218        // Remove any invalid ids from the array.
 219        $passed_ids = array_intersect_key($passed_ids, $entities);
 220        foreach ($entities as $entity) {
 221          $passed_ids[$entity->{$this->idKey}] = $entity;
 222        }
 223        $entities = $passed_ids;
 224      }
 225  
 226      return $entities;
 227    }
 228  
 229    /**
 230     * Builds the query to load the entity.
 231     *
 232     * This has full revision support. For entities requiring special queries,
 233     * the class can be extended, and the default query can be constructed by
 234     * calling parent::buildQuery(). This is usually necessary when the object
 235     * being loaded needs to be augmented with additional data from another
 236     * table, such as loading node type into comments or vocabulary machine name
 237     * into terms, however it can also support $conditions on different tables.
 238     * See CommentController::buildQuery() or TaxonomyTermController::buildQuery()
 239     * for examples.
 240     *
 241     * @param $ids
 242     *   An array of entity IDs, or FALSE to load all entities.
 243     * @param $conditions
 244     *   An array of conditions in the form 'field' => $value.
 245     * @param $revision_id
 246     *   The ID of the revision to load, or FALSE if this query is asking for the
 247     *   most current revision(s).
 248     *
 249     * @return SelectQuery
 250     *   A SelectQuery object for loading the entity.
 251     */
 252    protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
 253      $query = db_select($this->entityInfo['base table'], 'base');
 254  
 255      $query->addTag($this->entityType . '_load_multiple');
 256  
 257      if ($revision_id) {
 258        $query->join($this->revisionTable, 'revision', "revision.{$this->idKey} = base.{$this->idKey} AND revision.{$this->revisionKey} = :revisionId", array(':revisionId' => $revision_id));
 259      }
 260      elseif ($this->revisionKey) {
 261        $query->join($this->revisionTable, 'revision', "revision.{$this->revisionKey} = base.{$this->revisionKey}");
 262      }
 263  
 264      // Add fields from the {entity} table.
 265      $entity_fields = $this->entityInfo['schema_fields_sql']['base table'];
 266  
 267      if ($this->revisionKey) {
 268        // Add all fields from the {entity_revision} table.
 269        $entity_revision_fields = drupal_map_assoc($this->entityInfo['schema_fields_sql']['revision table']);
 270        // The id field is provided by entity, so remove it.
 271        unset($entity_revision_fields[$this->idKey]);
 272  
 273        // Remove all fields from the base table that are also fields by the same
 274        // name in the revision table.
 275        $entity_field_keys = array_flip($entity_fields);
 276        foreach ($entity_revision_fields as $key => $name) {
 277          if (isset($entity_field_keys[$name])) {
 278            unset($entity_fields[$entity_field_keys[$name]]);
 279          }
 280        }
 281        $query->fields('revision', $entity_revision_fields);
 282      }
 283  
 284      $query->fields('base', $entity_fields);
 285  
 286      if ($ids) {
 287        $query->condition("base.{$this->idKey}", $ids, 'IN');
 288      }
 289      if ($conditions) {
 290        foreach ($conditions as $field => $value) {
 291          $query->condition('base.' . $field, $value);
 292        }
 293      }
 294      return $query;
 295    }
 296  
 297    /**
 298     * Attaches data to entities upon loading.
 299     *
 300     * This will attach fields, if the entity is fieldable. It calls
 301     * hook_entity_load() for modules which need to add data to all entities.
 302     * It also calls hook_TYPE_load() on the loaded entities. For example
 303     * hook_node_load() or hook_user_load(). If your hook_TYPE_load()
 304     * expects special parameters apart from the queried entities, you can set
 305     * $this->hookLoadArguments prior to calling the method.
 306     * See NodeController::attachLoad() for an example.
 307     *
 308     * @param $queried_entities
 309     *   Associative array of query results, keyed on the entity ID.
 310     * @param $revision_id
 311     *   ID of the revision that was loaded, or FALSE if the most current revision
 312     *   was loaded.
 313     */
 314    protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
 315      // Attach fields.
 316      if ($this->entityInfo['fieldable']) {
 317        if ($revision_id) {
 318          field_attach_load_revision($this->entityType, $queried_entities);
 319        }
 320        else {
 321          field_attach_load($this->entityType, $queried_entities);
 322        }
 323      }
 324  
 325      // Call hook_entity_load().
 326      foreach (module_implements('entity_load') as $module) {
 327        $function = $module . '_entity_load';
 328        $function($queried_entities, $this->entityType);
 329      }
 330      // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are
 331      // always the queried entities, followed by additional arguments set in
 332      // $this->hookLoadArguments.
 333      $args = array_merge(array($queried_entities), $this->hookLoadArguments);
 334      foreach (module_implements($this->entityInfo['load hook']) as $module) {
 335        call_user_func_array($module . '_' . $this->entityInfo['load hook'], $args);
 336      }
 337    }
 338  
 339    /**
 340     * Gets entities from the static cache.
 341     *
 342     * @param $ids
 343     *   If not empty, return entities that match these IDs.
 344     * @param $conditions
 345     *   If set, return entities that match all of these conditions.
 346     *
 347     * @return
 348     *   Array of entities from the entity cache.
 349     */
 350    protected function cacheGet($ids, $conditions = array()) {
 351      $entities = array();
 352      // Load any available entities from the internal cache.
 353      if (!empty($this->entityCache)) {
 354        if ($ids) {
 355          $entities += array_intersect_key($this->entityCache, array_flip($ids));
 356        }
 357        // If loading entities only by conditions, fetch all available entities
 358        // from the cache. Entities which don't match are removed later.
 359        elseif ($conditions) {
 360          $entities = $this->entityCache;
 361        }
 362      }
 363  
 364      // Exclude any entities loaded from cache if they don't match $conditions.
 365      // This ensures the same behavior whether loading from memory or database.
 366      if ($conditions) {
 367        foreach ($entities as $entity) {
 368          $entity_values = (array) $entity;
 369          if (array_diff_assoc($conditions, $entity_values)) {
 370            unset($entities[$entity->{$this->idKey}]);
 371          }
 372        }
 373      }
 374      return $entities;
 375    }
 376  
 377    /**
 378     * Stores entities in the static entity cache.
 379     *
 380     * @param $entities
 381     *   Entities to store in the cache.
 382     */
 383    protected function cacheSet($entities) {
 384      $this->entityCache += $entities;
 385    }
 386  }
 387  
 388  /**
 389   * Exception thrown by EntityFieldQuery() on unsupported query syntax.
 390   *
 391   * Some storage modules might not support the full range of the syntax for
 392   * conditions, and will raise an EntityFieldQueryException when an unsupported
 393   * condition was specified.
 394   */
 395  class EntityFieldQueryException extends Exception {}
 396  
 397  /**
 398   * Retrieves entities matching a given set of conditions.
 399   *
 400   * This class allows finding entities based on entity properties (for example,
 401   * node->changed), field values, and generic entity meta data (bundle,
 402   * entity type, entity id, and revision ID). It is not possible to query across
 403   * multiple entity types. For example, there is no facility to find published
 404   * nodes written by users created in the last hour, as this would require
 405   * querying both node->status and user->created.
 406   *
 407   * Normally we would not want to have public properties on the object, as that
 408   * allows the object's state to become inconsistent too easily. However, this
 409   * class's standard use case involves primarily code that does need to have
 410   * direct access to the collected properties in order to handle alternate
 411   * execution routines. We therefore use public properties for simplicity. Note
 412   * that code that is simply creating and running a field query should still use
 413   * the appropriate methods to add conditions on the query.
 414   *
 415   * Storage engines are not required to support every type of query. By default,
 416   * an EntityFieldQueryException will be raised if an unsupported condition is
 417   * specified or if the query has field conditions or sorts that are stored in
 418   * different field storage engines. However, this logic can be overridden in
 419   * hook_entity_query_alter().
 420   *
 421   * Also note that this query does not automatically respect entity access
 422   * restrictions. Node access control is performed by the SQL storage engine but
 423   * other storage engines might not do this.
 424   */
 425  class EntityFieldQuery {
 426  
 427    /**
 428     * Indicates that both deleted and non-deleted fields should be returned.
 429     *
 430     * @see EntityFieldQuery::deleted()
 431     */
 432    const RETURN_ALL = NULL;
 433  
 434    /**
 435     * TRUE if the query has already been altered, FALSE if it hasn't.
 436     *
 437     * Used in alter hooks to check for cloned queries that have already been
 438     * altered prior to the clone (for example, the pager count query).
 439     *
 440     * @var boolean
 441     */
 442    public $altered = FALSE;
 443  
 444    /**
 445     * Associative array of entity-generic metadata conditions.
 446     *
 447     * @var array
 448     *
 449     * @see EntityFieldQuery::entityCondition()
 450     */
 451    public $entityConditions = array();
 452  
 453    /**
 454     * List of field conditions.
 455     *
 456     * @var array
 457     *
 458     * @see EntityFieldQuery::fieldCondition()
 459     */
 460    public $fieldConditions = array();
 461  
 462    /**
 463     * List of field meta conditions (language and delta).
 464     *
 465     * Field conditions operate on columns specified by hook_field_schema(),
 466     * the meta conditions operate on columns added by the system: delta
 467     * and language. These can not be mixed with the field conditions because
 468     * field columns can have any name including delta and language.
 469     *
 470     * @var array
 471     *
 472     * @see EntityFieldQuery::fieldLanguageCondition()
 473     * @see EntityFieldQuery::fieldDeltaCondition()
 474     */
 475    public $fieldMetaConditions = array();
 476  
 477    /**
 478     * List of property conditions.
 479     *
 480     * @var array
 481     *
 482     * @see EntityFieldQuery::propertyCondition()
 483     */
 484    public $propertyConditions = array();
 485  
 486    /**
 487     * List of order clauses.
 488     *
 489     * @var array
 490     */
 491    public $order = array();
 492  
 493    /**
 494     * The query range.
 495     *
 496     * @var array
 497     *
 498     * @see EntityFieldQuery::range()
 499     */
 500    public $range = array();
 501  
 502    /**
 503     * The query pager data.
 504     *
 505     * @var array
 506     *
 507     * @see EntityFieldQuery::pager()
 508     */
 509    public $pager = array();
 510  
 511    /**
 512     * Query behavior for deleted data.
 513     *
 514     * TRUE to return only deleted data, FALSE to return only non-deleted data,
 515     * EntityFieldQuery::RETURN_ALL to return everything.
 516     *
 517     * @see EntityFieldQuery::deleted()
 518     */
 519    public $deleted = FALSE;
 520  
 521    /**
 522     * A list of field arrays used.
 523     *
 524     * Field names passed to EntityFieldQuery::fieldCondition() and
 525     * EntityFieldQuery::fieldOrderBy() are run through field_info_field() before
 526     * stored in this array. This way, the elements of this array are field
 527     * arrays.
 528     *
 529     * @var array
 530     */
 531    public $fields = array();
 532  
 533    /**
 534     * TRUE if this is a count query, FALSE if it isn't.
 535     *
 536     * @var boolean
 537     */
 538    public $count = FALSE;
 539  
 540    /**
 541     * Flag indicating whether this is querying current or all revisions.
 542     *
 543     * @var int
 544     *
 545     * @see EntityFieldQuery::age()
 546     */
 547    public $age = FIELD_LOAD_CURRENT;
 548  
 549    /**
 550     * A list of the tags added to this query.
 551     *
 552     * @var array
 553     *
 554     * @see EntityFieldQuery::addTag()
 555     */
 556    public $tags = array();
 557  
 558    /**
 559     * A list of metadata added to this query.
 560     *
 561     * @var array
 562     *
 563     * @see EntityFieldQuery::addMetaData()
 564     */
 565    public $metaData = array();
 566  
 567    /**
 568     * The ordered results.
 569     *
 570     * @var array
 571     *
 572     * @see EntityFieldQuery::execute().
 573     */
 574    public $orderedResults = array();
 575  
 576    /**
 577     * The method executing the query, if it is overriding the default.
 578     *
 579     * @var string
 580     *
 581     * @see EntityFieldQuery::execute().
 582     */
 583    public $executeCallback = '';
 584  
 585    /**
 586     * Adds a condition on entity-generic metadata.
 587     *
 588     * If the overall query contains only entity conditions or ordering, or if
 589     * there are property conditions, then specifying the entity type is
 590     * mandatory. If there are field conditions or ordering but no property
 591     * conditions or ordering, then specifying an entity type is optional. While
 592     * the field storage engine might support field conditions on more than one
 593     * entity type, there is no way to query across multiple entity base tables by
 594     * default. To specify the entity type, pass in 'entity_type' for $name,
 595     * the type as a string for $value, and no $operator (it's disregarded).
 596     *
 597     * 'bundle', 'revision_id' and 'entity_id' have no such restrictions.
 598     *
 599     * Note: The "comment" entity type does not support bundle conditions.
 600     *
 601     * @param $name
 602     *   'entity_type', 'bundle', 'revision_id' or 'entity_id'.
 603     * @param $value
 604     *   The value for $name. In most cases, this is a scalar. For more complex
 605     *   options, it is an array. The meaning of each element in the array is
 606     *   dependent on $operator.
 607     * @param $operator
 608     *   Possible values:
 609     *   - '=', '<>', '>', '>=', '<', '<=', 'STARTS_WITH', 'CONTAINS': These
 610     *     operators expect $value to be a literal of the same type as the
 611     *     column.
 612     *   - 'IN', 'NOT IN': These operators expect $value to be an array of
 613     *     literals of the same type as the column.
 614     *   - 'BETWEEN': This operator expects $value to be an array of two literals
 615     *     of the same type as the column.
 616     *   The operator can be omitted, and will default to 'IN' if the value is an
 617     *   array, or to '=' otherwise.
 618     *
 619     * @return EntityFieldQuery
 620     *   The called object.
 621     */
 622    public function entityCondition($name, $value, $operator = NULL) {
 623      // The '!=' operator is deprecated in favour of the '<>' operator since the
 624      // latter is ANSI SQL compatible.
 625      if ($operator == '!=') {
 626        $operator = '<>';
 627      }
 628      $this->entityConditions[$name] = array(
 629        'value' => $value,
 630        'operator' => $operator,
 631      );
 632      return $this;
 633    }
 634  
 635    /**
 636     * Adds a condition on field values.
 637     * 
 638     * Note that entities with empty field values will be excluded from the
 639     * EntityFieldQuery results when using this method.
 640     *
 641     * @param $field
 642     *   Either a field name or a field array.
 643     * @param $column
 644     *   The column that should hold the value to be matched.
 645     * @param $value
 646     *   The value to test the column value against.
 647     * @param $operator
 648     *   The operator to be used to test the given value.
 649     * @param $delta_group
 650     *   An arbitrary identifier: conditions in the same group must have the same
 651     *   $delta_group.
 652     * @param $language_group
 653     *   An arbitrary identifier: conditions in the same group must have the same
 654     *   $language_group.
 655     *
 656     * @return EntityFieldQuery
 657     *   The called object.
 658     *
 659     * @see EntityFieldQuery::addFieldCondition
 660     * @see EntityFieldQuery::deleted
 661     */
 662    public function fieldCondition($field, $column = NULL, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
 663      return $this->addFieldCondition($this->fieldConditions, $field, $column, $value, $operator, $delta_group, $language_group);
 664    }
 665  
 666    /**
 667     * Adds a condition on the field language column.
 668     *
 669     * @param $field
 670     *   Either a field name or a field array.
 671     * @param $value
 672     *   The value to test the column value against.
 673     * @param $operator
 674     *   The operator to be used to test the given value.
 675     * @param $delta_group
 676     *   An arbitrary identifier: conditions in the same group must have the same
 677     *   $delta_group.
 678     * @param $language_group
 679     *   An arbitrary identifier: conditions in the same group must have the same
 680     *   $language_group.
 681     *
 682     * @return EntityFieldQuery
 683     *   The called object.
 684     *
 685     * @see EntityFieldQuery::addFieldCondition
 686     * @see EntityFieldQuery::deleted
 687     */
 688    public function fieldLanguageCondition($field, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
 689      return $this->addFieldCondition($this->fieldMetaConditions, $field, 'language', $value, $operator, $delta_group, $language_group);
 690    }
 691  
 692    /**
 693     * Adds a condition on the field delta column.
 694     *
 695     * @param $field
 696     *   Either a field name or a field array.
 697     * @param $value
 698     *   The value to test the column value against.
 699     * @param $operator
 700     *   The operator to be used to test the given value.
 701     * @param $delta_group
 702     *   An arbitrary identifier: conditions in the same group must have the same
 703     *   $delta_group.
 704     * @param $language_group
 705     *   An arbitrary identifier: conditions in the same group must have the same
 706     *   $language_group.
 707     *
 708     * @return EntityFieldQuery
 709     *   The called object.
 710     *
 711     * @see EntityFieldQuery::addFieldCondition
 712     * @see EntityFieldQuery::deleted
 713     */
 714    public function fieldDeltaCondition($field, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
 715      return $this->addFieldCondition($this->fieldMetaConditions, $field, 'delta', $value, $operator, $delta_group, $language_group);
 716    }
 717  
 718    /**
 719     * Adds the given condition to the proper condition array.
 720     *
 721     * @param $conditions
 722     *   A reference to an array of conditions.
 723     * @param $field
 724     *   Either a field name or a field array.
 725     * @param $column
 726     *   A column defined in the hook_field_schema() of this field. If this is
 727     *   omitted then the query will find only entities that have data in this
 728     *   field, using the entity and property conditions if there are any.
 729     * @param $value
 730     *   The value to test the column value against. In most cases, this is a
 731     *   scalar. For more complex options, it is an array. The meaning of each
 732     *   element in the array is dependent on $operator.
 733     * @param $operator
 734     *   Possible values:
 735     *   - '=', '<>', '>', '>=', '<', '<=', 'STARTS_WITH', 'CONTAINS': These
 736     *     operators expect $value to be a literal of the same type as the
 737     *     column.
 738     *   - 'IN', 'NOT IN': These operators expect $value to be an array of
 739     *     literals of the same type as the column.
 740     *   - 'BETWEEN': This operator expects $value to be an array of two literals
 741     *     of the same type as the column.
 742     *   The operator can be omitted, and will default to 'IN' if the value is an
 743     *   array, or to '=' otherwise.
 744     * @param $delta_group
 745     *   An arbitrary identifier: conditions in the same group must have the same
 746     *   $delta_group. For example, let's presume a multivalue field which has
 747     *   two columns, 'color' and 'shape', and for entity id 1, there are two
 748     *   values: red/square and blue/circle. Entity ID 1 does not have values
 749     *   corresponding to 'red circle', however if you pass 'red' and 'circle' as
 750     *   conditions, it will appear in the  results - by default queries will run
 751     *   against any combination of deltas. By passing the conditions with the
 752     *   same $delta_group it will ensure that only values attached to the same
 753     *   delta are matched, and entity 1 would then be excluded from the results.
 754     * @param $language_group
 755     *   An arbitrary identifier: conditions in the same group must have the same
 756     *   $language_group.
 757     *
 758     * @return EntityFieldQuery
 759     *   The called object.
 760     */
 761    protected function addFieldCondition(&$conditions, $field, $column = NULL, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
 762      // The '!=' operator is deprecated in favour of the '<>' operator since the
 763      // latter is ANSI SQL compatible.
 764      if ($operator == '!=') {
 765        $operator = '<>';
 766      }
 767      if (is_scalar($field)) {
 768        $field_definition = field_info_field($field);
 769        if (empty($field_definition)) {
 770          throw new EntityFieldQueryException(t('Unknown field: @field_name', array('@field_name' => $field)));
 771        }
 772        $field = $field_definition;
 773      }
 774      // Ensure the same index is used for field conditions as for fields.
 775      $index = count($this->fields);
 776      $this->fields[$index] = $field;
 777      if (isset($column)) {
 778        $conditions[$index] = array(
 779          'field' => $field,
 780          'column' => $column,
 781          'value' => $value,
 782          'operator' => $operator,
 783          'delta_group' => $delta_group,
 784          'language_group' => $language_group,
 785        );
 786      }
 787      return $this;
 788    }
 789  
 790    /**
 791     * Adds a condition on an entity-specific property.
 792     *
 793     * An $entity_type must be specified by calling
 794     * EntityFieldCondition::entityCondition('entity_type', $entity_type) before
 795     * executing the query. Also, by default only entities stored in SQL are
 796     * supported; however, EntityFieldQuery::executeCallback can be set to handle
 797     * different entity storage.
 798     *
 799     * @param $column
 800     *   A column defined in the hook_schema() of the base table of the entity.
 801     * @param $value
 802     *   The value to test the field against. In most cases, this is a scalar. For
 803     *   more complex options, it is an array. The meaning of each element in the
 804     *   array is dependent on $operator.
 805     * @param $operator
 806     *   Possible values:
 807     *   - '=', '<>', '>', '>=', '<', '<=', 'STARTS_WITH', 'CONTAINS': These
 808     *     operators expect $value to be a literal of the same type as the
 809     *     column.
 810     *   - 'IN', 'NOT IN': These operators expect $value to be an array of
 811     *     literals of the same type as the column.
 812     *   - 'BETWEEN': This operator expects $value to be an array of two literals
 813     *     of the same type as the column.
 814     *   The operator can be omitted, and will default to 'IN' if the value is an
 815     *   array, or to '=' otherwise.
 816     *
 817     * @return EntityFieldQuery
 818     *   The called object.
 819     */
 820    public function propertyCondition($column, $value, $operator = NULL) {
 821      // The '!=' operator is deprecated in favour of the '<>' operator since the
 822      // latter is ANSI SQL compatible.
 823      if ($operator == '!=') {
 824        $operator = '<>';
 825      }
 826      $this->propertyConditions[] = array(
 827        'column' => $column,
 828        'value' => $value,
 829        'operator' => $operator,
 830      );
 831      return $this;
 832    }
 833  
 834    /**
 835     * Orders the result set by entity-generic metadata.
 836     *
 837     * If called multiple times, the query will order by each specified column in
 838     * the order this method is called.
 839     *
 840     * Note: The "comment" and "taxonomy_term" entity types don't support ordering
 841     * by bundle. For "taxonomy_term", propertyOrderBy('vid') can be used instead.
 842     *
 843     * @param $name
 844     *   'entity_type', 'bundle', 'revision_id' or 'entity_id'.
 845     * @param $direction
 846     *   The direction to sort. Legal values are "ASC" and "DESC".
 847     *
 848     * @return EntityFieldQuery
 849     *   The called object.
 850     */
 851    public function entityOrderBy($name, $direction = 'ASC') {
 852      $this->order[] = array(
 853        'type' => 'entity',
 854        'specifier' => $name,
 855        'direction' => $direction,
 856      );
 857      return $this;
 858    }
 859  
 860    /**
 861     * Orders the result set by a given field column.
 862     *
 863     * If called multiple times, the query will order by each specified column in
 864     * the order this method is called. Note that entities with empty field
 865     * values will be excluded from the EntityFieldQuery results when using this
 866     * method.
 867     *
 868     * @param $field
 869     *   Either a field name or a field array.
 870     * @param $column
 871     *   A column defined in the hook_field_schema() of this field. entity_id and
 872     *   bundle can also be used.
 873     * @param $direction
 874     *   The direction to sort. Legal values are "ASC" and "DESC".
 875     *
 876     * @return EntityFieldQuery
 877     *   The called object.
 878     */
 879    public function fieldOrderBy($field, $column, $direction = 'ASC') {
 880      if (is_scalar($field)) {
 881        $field_definition = field_info_field($field);
 882        if (empty($field_definition)) {
 883          throw new EntityFieldQueryException(t('Unknown field: @field_name', array('@field_name' => $field)));
 884        }
 885        $field = $field_definition;
 886      }
 887      // Save the index used for the new field, for later use in field storage.
 888      $index = count($this->fields);
 889      $this->fields[$index] = $field;
 890      $this->order[] = array(
 891        'type' => 'field',
 892        'specifier' => array(
 893          'field' => $field,
 894          'index' => $index,
 895          'column' => $column,
 896        ),
 897        'direction' => $direction,
 898      );
 899      return $this;
 900    }
 901  
 902    /**
 903     * Orders the result set by an entity-specific property.
 904     *
 905     * An $entity_type must be specified by calling
 906     * EntityFieldCondition::entityCondition('entity_type', $entity_type) before
 907     * executing the query.
 908     *
 909     * If called multiple times, the query will order by each specified column in
 910     * the order this method is called.
 911     *
 912     * @param $column
 913     *   The column on which to order.
 914     * @param $direction
 915     *   The direction to sort. Legal values are "ASC" and "DESC".
 916     *
 917     * @return EntityFieldQuery
 918     *   The called object.
 919     */
 920    public function propertyOrderBy($column, $direction = 'ASC') {
 921      $this->order[] = array(
 922        'type' => 'property',
 923        'specifier' => $column,
 924        'direction' => $direction,
 925      );
 926      return $this;
 927    }
 928  
 929    /**
 930     * Sets the query to be a count query only.
 931     *
 932     * @return EntityFieldQuery
 933     *   The called object.
 934     */
 935    public function count() {
 936      $this->count = TRUE;
 937      return $this;
 938    }
 939  
 940    /**
 941     * Restricts a query to a given range in the result set.
 942     *
 943     * @param $start
 944     *   The first entity from the result set to return. If NULL, removes any
 945     *   range directives that are set.
 946     * @param $length
 947     *   The number of entities to return from the result set.
 948     *
 949     * @return EntityFieldQuery
 950     *   The called object.
 951     */
 952    public function range($start = NULL, $length = NULL) {
 953      $this->range = array(
 954        'start' => $start,
 955        'length' => $length,
 956      );
 957      return $this;
 958    }
 959  
 960    /**
 961     * Enables a pager for the query.
 962     *
 963     * @param $limit
 964     *   An integer specifying the number of elements per page.  If passed a false
 965     *   value (FALSE, 0, NULL), the pager is disabled.
 966     * @param $element
 967     *   An optional integer to distinguish between multiple pagers on one page.
 968     *   If not provided, one is automatically calculated.
 969     *
 970     * @return EntityFieldQuery
 971     *   The called object.
 972     */
 973    public function pager($limit = 10, $element = NULL) {
 974      if (!isset($element)) {
 975        $element = PagerDefault::$maxElement++;
 976      }
 977      elseif ($element >= PagerDefault::$maxElement) {
 978        PagerDefault::$maxElement = $element + 1;
 979      }
 980  
 981      $this->pager = array(
 982        'limit' => $limit,
 983        'element' => $element,
 984      );
 985      return $this;
 986    }
 987  
 988    /**
 989     * Enables sortable tables for this query.
 990     *
 991     * @param $headers
 992     *   An EFQ Header array based on which the order clause is added to the
 993     *   query.
 994     *
 995     * @return EntityFieldQuery
 996     *   The called object.
 997     */
 998    public function tableSort(&$headers) {
 999      // If 'field' is not initialized, the header columns aren't clickable
1000      foreach ($headers as $key =>$header) {
1001        if (is_array($header) && isset($header['specifier'])) {
1002          $headers[$key]['field'] = '';
1003        }
1004      }
1005  
1006      $order = tablesort_get_order($headers);
1007      $direction = tablesort_get_sort($headers);
1008      foreach ($headers as $header) {
1009        if (is_array($header) && ($header['data'] == $order['name'])) {
1010          if ($header['type'] == 'field') {
1011            $this->fieldOrderBy($header['specifier']['field'], $header['specifier']['column'], $direction);
1012          }
1013          else {
1014            $header['direction'] = $direction;
1015            $this->order[] = $header;
1016          }
1017        }
1018      }
1019  
1020      return $this;
1021    }
1022  
1023    /**
1024     * Filters on the data being deleted.
1025     *
1026     * @param $deleted
1027     *   TRUE to only return deleted data, FALSE to return non-deleted data,
1028     *   EntityFieldQuery::RETURN_ALL to return everything. Defaults to FALSE.
1029     *
1030     * @return EntityFieldQuery
1031     *   The called object.
1032     */
1033    public function deleted($deleted = TRUE) {
1034      $this->deleted = $deleted;
1035      return $this;
1036    }
1037  
1038    /**
1039     * Queries the current or every revision.
1040     *
1041     * Note that this only affects field conditions. Property conditions always
1042     * apply to the current revision.
1043     * @TODO: Once revision tables have been cleaned up, revisit this.
1044     *
1045     * @param $age
1046     *   - FIELD_LOAD_CURRENT (default): Query the most recent revisions for all
1047     *     entities. The results will be keyed by entity type and entity ID.
1048     *   - FIELD_LOAD_REVISION: Query all revisions. The results will be keyed by
1049     *     entity type and entity revision ID.
1050     *
1051     * @return EntityFieldQuery
1052     *   The called object.
1053     */
1054    public function age($age) {
1055      $this->age = $age;
1056      return $this;
1057    }
1058  
1059    /**
1060     * Adds a tag to the query.
1061     *
1062     * Tags are strings that mark a query so that hook_query_alter() and
1063     * hook_query_TAG_alter() implementations may decide if they wish to alter
1064     * the query. A query may have any number of tags, and they must be valid PHP
1065     * identifiers (composed of letters, numbers, and underscores). For example,
1066     * queries involving nodes that will be displayed for a user need to add the
1067     * tag 'node_access', so that the node module can add access restrictions to
1068     * the query.
1069     *
1070     * If an entity field query has tags, it must also have an entity type
1071     * specified, because the alter hook will need the entity base table.
1072     *
1073     * @param string $tag
1074     *   The tag to add.
1075     *
1076     * @return EntityFieldQuery
1077     *   The called object.
1078     */
1079    public function addTag($tag) {
1080      $this->tags[$tag] = $tag;
1081      return $this;
1082    }
1083  
1084    /**
1085     * Adds additional metadata to the query.
1086     *
1087     * Sometimes a query may need to provide additional contextual data for the
1088     * alter hook. The alter hook implementations may then use that information
1089     * to decide if and how to take action.
1090     *
1091     * @param $key
1092     *   The unique identifier for this piece of metadata. Must be a string that
1093     *   follows the same rules as any other PHP identifier.
1094     * @param $object
1095     *   The additional data to add to the query. May be any valid PHP variable.
1096     *
1097     * @return EntityFieldQuery
1098     *   The called object.
1099     */
1100    public function addMetaData($key, $object) {
1101      $this->metaData[$key] = $object;
1102      return $this;
1103    }
1104  
1105    /**
1106     * Executes the query.
1107     *
1108     * After executing the query, $this->orderedResults will contain a list of
1109     * the same stub entities in the order returned by the query. This is only
1110     * relevant if there are multiple entity types in the returned value and
1111     * a field ordering was requested. In every other case, the returned value
1112     * contains everything necessary for processing.
1113     *
1114     * @return
1115     *   Either a number if count() was called or an array of associative arrays
1116     *   of stub entities. The outer array keys are entity types, and the inner
1117     *   array keys are the relevant ID. (In most cases this will be the entity
1118     *   ID. The only exception is when age=FIELD_LOAD_REVISION is used and field
1119     *   conditions or sorts are present -- in this case, the key will be the
1120     *   revision ID.) The entity type will only exist in the outer array if
1121     *   results were found. The inner array values are always stub entities, as
1122     *   returned by entity_create_stub_entity(). To traverse the returned array:
1123     *   @code
1124     *     foreach ($query->execute() as $entity_type => $entities) {
1125     *       foreach ($entities as $entity_id => $entity) {
1126     *   @endcode
1127     *   Note if the entity type is known, then the following snippet will load
1128     *   the entities found:
1129     *   @code
1130     *     $result = $query->execute();
1131     *     if (!empty($result[$my_type])) {
1132     *       $entities = entity_load($my_type, array_keys($result[$my_type]));
1133     *     }
1134     *   @endcode
1135     */
1136    public function execute() {
1137      // Give a chance to other modules to alter the query.
1138      drupal_alter('entity_query', $this);
1139      $this->altered = TRUE;
1140  
1141      // Initialize the pager.
1142      $this->initializePager();
1143  
1144      // Execute the query using the correct callback.
1145      $result = call_user_func($this->queryCallback(), $this);
1146  
1147      return $result;
1148    }
1149  
1150    /**
1151     * Determines the query callback to use for this entity query.
1152     *
1153     * @return
1154     *   A callback that can be used with call_user_func().
1155     */
1156    public function queryCallback() {
1157      // Use the override from $this->executeCallback. It can be set either
1158      // while building the query, or using hook_entity_query_alter().
1159      if (function_exists($this->executeCallback)) {
1160        return $this->executeCallback;
1161      }
1162      // If there are no field conditions and sorts, and no execute callback
1163      // then we default to querying entity tables in SQL.
1164      if (empty($this->fields)) {
1165        return array($this, 'propertyQuery');
1166      }
1167      // If no override, find the storage engine to be used.
1168      foreach ($this->fields as $field) {
1169        if (!isset($storage)) {
1170          $storage = $field['storage']['module'];
1171        }
1172        elseif ($storage != $field['storage']['module']) {
1173          throw new EntityFieldQueryException(t("Can't handle more than one field storage engine"));
1174        }
1175      }
1176      if ($storage) {
1177        // Use hook_field_storage_query() from the field storage.
1178        return $storage . '_field_storage_query';
1179      }
1180      else {
1181        throw new EntityFieldQueryException(t("Field storage engine not found."));
1182      }
1183    }
1184  
1185    /**
1186     * Queries entity tables in SQL for property conditions and sorts.
1187     *
1188     * This method is only used if there are no field conditions and sorts.
1189     *
1190     * @return
1191     *   See EntityFieldQuery::execute().
1192     */
1193    protected function propertyQuery() {
1194      if (empty($this->entityConditions['entity_type'])) {
1195        throw new EntityFieldQueryException(t('For this query an entity type must be specified.'));
1196      }
1197      $entity_type = $this->entityConditions['entity_type']['value'];
1198      $entity_info = entity_get_info($entity_type);
1199      if (empty($entity_info['base table'])) {
1200        throw new EntityFieldQueryException(t('Entity %entity has no base table.', array('%entity' => $entity_type)));
1201      }
1202      $base_table = $entity_info['base table'];
1203      $base_table_schema = drupal_get_schema($base_table);
1204      $select_query = db_select($base_table);
1205      $select_query->addExpression(':entity_type', 'entity_type', array(':entity_type' => $entity_type));
1206      // Process the property conditions.
1207      foreach ($this->propertyConditions as $property_condition) {
1208        $this->addCondition($select_query, $base_table . '.' . $property_condition['column'], $property_condition);
1209      }
1210      // Process the four possible entity condition.
1211      // The id field is always present in entity keys.
1212      $sql_field = $entity_info['entity keys']['id'];
1213      $id_map['entity_id'] = $sql_field;
1214      $select_query->addField($base_table, $sql_field, 'entity_id');
1215      if (isset($this->entityConditions['entity_id'])) {
1216        $this->addCondition($select_query, $base_table . '.' . $sql_field, $this->entityConditions['entity_id']);
1217      }
1218  
1219      // If there is a revision key defined, use it.
1220      if (!empty($entity_info['entity keys']['revision'])) {
1221        $sql_field = $entity_info['entity keys']['revision'];
1222        $select_query->addField($base_table, $sql_field, 'revision_id');
1223        if (isset($this->entityConditions['revision_id'])) {
1224          $this->addCondition($select_query, $base_table . '.' . $sql_field, $this->entityConditions['revision_id']);
1225        }
1226      }
1227      else {
1228        $sql_field = 'revision_id';
1229        $select_query->addExpression('NULL', 'revision_id');
1230      }
1231      $id_map['revision_id'] = $sql_field;
1232  
1233      // Handle bundles.
1234      if (!empty($entity_info['entity keys']['bundle'])) {
1235        $sql_field = $entity_info['entity keys']['bundle'];
1236        $having = FALSE;
1237  
1238        if (!empty($base_table_schema['fields'][$sql_field])) {
1239          $select_query->addField($base_table, $sql_field, 'bundle');
1240        }
1241      }
1242      else {
1243        $sql_field = 'bundle';
1244        $select_query->addExpression(':bundle', 'bundle', array(':bundle' => $entity_type));
1245        $having = TRUE;
1246      }
1247      $id_map['bundle'] = $sql_field;
1248      if (isset($this->entityConditions['bundle'])) {
1249        if (!empty($entity_info['entity keys']['bundle'])) {
1250          $this->addCondition($select_query, $base_table . '.' . $sql_field, $this->entityConditions['bundle'], $having);
1251        }
1252        else {
1253          // This entity has no bundle, so invalidate the query.
1254          $select_query->where('1 = 0');
1255        }
1256      }
1257  
1258      // Order the query.
1259      foreach ($this->order as $order) {
1260        if ($order['type'] == 'entity') {
1261          $key = $order['specifier'];
1262          if (!isset($id_map[$key])) {
1263            throw new EntityFieldQueryException(t('Do not know how to order on @key for @entity_type', array('@key' => $key, '@entity_type' => $entity_type)));
1264          }
1265          $select_query->orderBy($id_map[$key], $order['direction']);
1266        }
1267        elseif ($order['type'] == 'property') {
1268          $select_query->orderBy($base_table . '.' . $order['specifier'], $order['direction']);
1269        }
1270      }
1271  
1272      return $this->finishQuery($select_query);
1273    }
1274  
1275    /**
1276     * Gets the total number of results and initializes a pager for the query.
1277     *
1278     * The pager can be disabled by either setting the pager limit to 0, or by
1279     * setting this query to be a count query.
1280     */
1281    function initializePager() {
1282      if ($this->pager && !empty($this->pager['limit']) && !$this->count) {
1283        $page = pager_find_page($this->pager['element']);
1284        $count_query = clone $this;
1285        $this->pager['total'] = $count_query->count()->execute();
1286        $this->pager['start'] = $page * $this->pager['limit'];
1287        pager_default_initialize($this->pager['total'], $this->pager['limit'], $this->pager['element']);
1288        $this->range($this->pager['start'], $this->pager['limit']);
1289      }
1290    }
1291  
1292    /**
1293     * Finishes the query.
1294     *
1295     * Adds tags, metaData, range and returns the requested list or count.
1296     *
1297     * @param SelectQuery $select_query
1298     *   A SelectQuery which has entity_type, entity_id, revision_id and bundle
1299     *   fields added.
1300     * @param $id_key
1301     *   Which field's values to use as the returned array keys.
1302     *
1303     * @return
1304     *   See EntityFieldQuery::execute().
1305     */
1306    function finishQuery($select_query, $id_key = 'entity_id') {
1307      foreach ($this->tags as $tag) {
1308        $select_query->addTag($tag);
1309      }
1310      foreach ($this->metaData as $key => $object) {
1311        $select_query->addMetaData($key, $object);
1312      }
1313      $select_query->addMetaData('entity_field_query', $this);
1314      if ($this->range) {
1315        $select_query->range($this->range['start'], $this->range['length']);
1316      }
1317      if ($this->count) {
1318        return $select_query->countQuery()->execute()->fetchField();
1319      }
1320      $return = array();
1321      foreach ($select_query->execute() as $partial_entity) {
1322        $bundle = isset($partial_entity->bundle) ? $partial_entity->bundle : NULL;
1323        $entity = entity_create_stub_entity($partial_entity->entity_type, array($partial_entity->entity_id, $partial_entity->revision_id, $bundle));
1324        $return[$partial_entity->entity_type][$partial_entity->$id_key] = $entity;
1325        $this->ordered_results[] = $partial_entity;
1326      }
1327      return $return;
1328    }
1329  
1330    /**
1331     * Adds a condition to an already built SelectQuery (internal function).
1332     *
1333     * This is a helper for hook_entity_query() and hook_field_storage_query().
1334     *
1335     * @param SelectQuery $select_query
1336     *   A SelectQuery object.
1337     * @param $sql_field
1338     *   The name of the field.
1339     * @param $condition
1340     *   A condition as described in EntityFieldQuery::fieldCondition() and
1341     *   EntityFieldQuery::entityCondition().
1342     * @param $having
1343     *   HAVING or WHERE. This is necessary because SQL can't handle WHERE
1344     *   conditions on aliased columns.
1345     */
1346    public function addCondition(SelectQuery $select_query, $sql_field, $condition, $having = FALSE) {
1347      $method = $having ? 'havingCondition' : 'condition';
1348      $like_prefix = '';
1349      switch ($condition['operator']) {
1350        case 'CONTAINS':
1351          $like_prefix = '%';
1352        case 'STARTS_WITH':
1353          $select_query->$method($sql_field, $like_prefix . db_like($condition['value']) . '%', 'LIKE');
1354          break;
1355        default:
1356          $select_query->$method($sql_field, $condition['value'], $condition['operator']);
1357      }
1358    }
1359  
1360  }
1361  
1362  /**
1363   * Defines an exception thrown when a malformed entity is passed.
1364   */
1365  class EntityMalformedException extends Exception { }

title

Description

title

Description

title

Description

title

title

Body