diff --git a/core/includes/entity.api.php b/core/includes/entity.api.php index 9c9d123..23f72b4 100644 --- a/core/includes/entity.api.php +++ b/core/includes/entity.api.php @@ -345,20 +345,9 @@ function hook_entity_delete(Drupal\Core\Entity\EntityInterface $entity) { } /** - * Alter or execute an Drupal\Core\Entity\EntityFieldQuery. - * - * @param Drupal\Core\Entity\EntityFieldQuery $query - * An EntityFieldQuery. One of the most important properties to be changed is - * EntityFieldQuery::executeCallback. If this is set to an existing function, - * this function will get the query as its single argument and its result - * will be the returned as the result of EntityFieldQuery::execute(). This can - * be used to change the behavior of EntityFieldQuery entirely. For example, - * the default implementation can only deal with one field storage engine, but - * it is possible to write a module that can query across field storage - * engines. Also, the default implementation presumes entities are stored in - * SQL, but the execute callback could instead query any other entity storage, - * local or remote. + * Alter or execute an Drupal\Core\Entity\Query\EntityQueryInterface. * + * @param Drupal\Core\Entity\Query\EntityQueryInterface $query * Note the $query->altered attribute which is TRUE in case the query has * already been altered once. This happens with cloned queries. * If there is a pager, then such a cloned query will be executed to count @@ -366,8 +355,8 @@ function hook_entity_delete(Drupal\Core\Entity\EntityInterface $entity) { * ($query->pager && $query->count), allowing the driver to return 0 from * the count query and disable the pager. */ -function hook_entity_query_alter(Drupal\Core\Entity\EntityFieldQuery $query) { - $query->executeCallback = 'my_module_query_callback'; +function hook_entity_query_alter(Drupal\Core\Entity\Query\EntityQueryInterface $query) { + } /** diff --git a/core/includes/entity.inc b/core/includes/entity.inc index 75c3386..0836a1a 100644 --- a/core/includes/entity.inc +++ b/core/includes/entity.inc @@ -5,10 +5,7 @@ * Entity API for handling entities like nodes or users. */ -use \InvalidArgumentException; use Drupal\Core\Cache\CacheBackendInterface; -use Drupal\Core\Entity\EntityFieldQuery; -use Drupal\Core\Entity\EntityMalformedException; use Drupal\Core\Entity\EntityStorageException; use Drupal\Core\Entity\EntityInterface; @@ -122,7 +119,7 @@ function entity_info_cache_clear() { * @see entity_load_multiple() * @see Drupal\Core\Entity\EntityStorageControllerInterface * @see Drupal\Core\Entity\DatabaseStorageController - * @see Drupal\Core\Entity\EntityFieldQuery + * @see Drupal\Core\Entity\Query\QueryInterface */ function entity_load($entity_type, $id, $reset = FALSE) { $entities = entity_load_multiple($entity_type, array($id), $reset); @@ -214,7 +211,7 @@ function entity_load_by_uuid($entity_type, $uuid, $reset = FALSE) { * @see hook_entity_info() * @see Drupal\Core\Entity\EntityStorageControllerInterface * @see Drupal\Core\Entity\DatabaseStorageController - * @see Drupal\Core\Entity\EntityFieldQuery + * @see Drupal\Core\Entity\Query\QueryInterface */ function entity_load_multiple($entity_type, array $ids = NULL, $reset = FALSE) { if ($reset) { diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php index ed15dd5..86bd7f0 100644 --- a/core/lib/Drupal/Core/CoreBundle.php +++ b/core/lib/Drupal/Core/CoreBundle.php @@ -66,6 +66,9 @@ public function build(ContainerBuilder $container) { $container->register('router.builder', 'Drupal\Core\Routing\RouteBuilder') ->addArgument(new Reference('router.dumper')); + $container->register('entity.query', 'Drupal\Core\Entity\Query\QueryFactory') + ->addArgument(new Reference('service_container')); + // @todo Replace below lines with the commented out block below it when it's // performant to do so: http://drupal.org/node/1706064. $dispatcher = $container->get('dispatcher'); diff --git a/core/lib/Drupal/Core/Database/Query/Select.php b/core/lib/Drupal/Core/Database/Query/Select.php index 22aca8f..1bfe9a0 100644 --- a/core/lib/Drupal/Core/Database/Query/Select.php +++ b/core/lib/Drupal/Core/Database/Query/Select.php @@ -124,8 +124,9 @@ class Select extends Query implements SelectInterface { public function __construct($table, $alias = NULL, Connection $connection, $options = array()) { $options['return'] = Database::RETURN_STATEMENT; parent::__construct($connection, $options); - $this->where = new Condition('AND'); - $this->having = new Condition('AND'); + $conjunction = isset($options['conjunction']) ? $options['conjunction'] : 'AND'; + $this->where = new Condition($conjunction); + $this->having = new Condition($conjunction); $this->addJoin(NULL, $table, $alias); } diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php index 1fa59ec..9db3013 100644 --- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php +++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Entity; use PDO; +use Drupal\Core\Entity\Query\QueryInterface; use Exception; use Drupal\Component\Uuid\Uuid; @@ -247,30 +248,24 @@ public function loadRevision($revision_id) { */ public function loadByProperties(array $values = array()) { // Build a query to fetch the entity IDs. - $entity_query = new EntityFieldQuery(); - $entity_query->entityCondition('entity_type', $this->entityType); + $entity_query = drupal_container()->get('entity.query')->get($this->entityType); $this->buildPropertyQuery($entity_query, $values); $result = $entity_query->execute(); - - if (empty($result[$this->entityType])) { - return array(); - } - // Load and return the found entities. - return $this->load(array_keys($result[$this->entityType])); + return $result ? $this->load($result) : array(); } /** * Builds an entity query. * - * @param Drupal\Core\Entity\EntityFieldQuery $entity_query - * EntityFieldQuery instance. + * @param \Drupal\Core\Entity\Query\EntityQueryInterface $entity_query + * EntityQuery instance. * @param array $values * An associative array of properties of the entity, where the keys are the * property names and the values are the values those properties must have. */ - protected function buildPropertyQuery(EntityFieldQuery $entity_query, array $values) { + protected function buildPropertyQuery(QueryInterface $entity_query, array $values) { foreach ($values as $name => $value) { - $entity_query->propertyCondition($name, $value); + $entity_query->condition($name, $value); } } diff --git a/core/lib/Drupal/Core/Entity/EntityFieldQuery.php b/core/lib/Drupal/Core/Entity/EntityFieldQuery.php deleted file mode 100644 index 0c054ce..0000000 --- a/core/lib/Drupal/Core/Entity/EntityFieldQuery.php +++ /dev/null @@ -1,1168 +0,0 @@ -changed), field values, and generic entity meta data (bundle, - * entity type, entity id, and revision ID). It is not possible to query across - * multiple entity types. For example, there is no facility to find published - * nodes written by users created in the last hour, as this would require - * querying both node->status and user->created. - * - * Normally we would not want to have public properties on the object, as that - * allows the object's state to become inconsistent too easily. However, this - * class's standard use case involves primarily code that does need to have - * direct access to the collected properties in order to handle alternate - * execution routines. We therefore use public properties for simplicity. Note - * that code that is simply creating and running a field query should still use - * the appropriate methods to add conditions on the query. - * - * Storage engines are not required to support every type of query. By default, - * an EntityFieldQueryException will be raised if an unsupported condition is - * specified or if the query has field conditions or sorts that are stored in - * different field storage engines. However, this logic can be overridden in - * hook_entity_query_alter(). - * - * Also note that this query does not automatically respect entity access - * restrictions. Node access control is performed by the SQL storage engine but - * other storage engines might not do this. - */ -class EntityFieldQuery { - - /** - * Indicates that both deleted and non-deleted fields should be returned. - * - * @see Drupal\Core\Entity\EntityFieldQuery::deleted() - */ - const RETURN_ALL = NULL; - - /** - * TRUE if the query has already been altered, FALSE if it hasn't. - * - * Used in alter hooks to check for cloned queries that have already been - * altered prior to the clone (for example, the pager count query). - * - * @var boolean - */ - public $altered = FALSE; - - /** - * Associative array of entity-generic metadata conditions. - * - * @var array - * - * @see Drupal\Core\Entity\EntityFieldQuery::entityCondition() - */ - public $entityConditions = array(); - - /** - * List of field conditions. - * - * @var array - * - * @see Drupal\Core\Entity\EntityFieldQuery::fieldCondition() - */ - public $fieldConditions = array(); - - /** - * List of field meta conditions (language and delta). - * - * Field conditions operate on columns specified by hook_field_schema(), - * the meta conditions operate on columns added by the system: delta - * and language. These can not be mixed with the field conditions because - * field columns can have any name including delta and language. - * - * @var array - * - * @see Drupal\Core\Entity\EntityFieldQuery::fieldLanguageCondition() - * @see Drupal\Core\Entity\EntityFieldQuery::fieldDeltaCondition() - */ - public $fieldMetaConditions = array(); - - /** - * List of property conditions. - * - * @var array - * - * @see Drupal\Core\Entity\EntityFieldQuery::propertyCondition() - */ - public $propertyConditions = array(); - - /** - * List of order clauses. - * - * @var array - */ - public $order = array(); - - /** - * The query range. - * - * @var array - * - * @see Drupal\Core\Entity\EntityFieldQuery::range() - */ - public $range = array(); - - /** - * The query pager data. - * - * @var array - * - * @see Drupal\Core\Entity\EntityFieldQuery::pager() - */ - public $pager = array(); - - /** - * Query behavior for deleted data. - * - * TRUE to return only deleted data, FALSE to return only non-deleted data, - * EntityFieldQuery::RETURN_ALL to return everything. - * - * @see Drupal\Core\Entity\EntityFieldQuery::deleted() - */ - public $deleted = FALSE; - - /** - * A list of field arrays used. - * - * Field names passed to EntityFieldQuery::fieldCondition() and - * EntityFieldQuery::fieldOrderBy() are run through field_info_field() before - * stored in this array. This way, the elements of this array are field - * arrays. - * - * @var array - */ - public $fields = array(); - - /** - * A list of used properties keyed by group. - * - * @var array - */ - public $properties = array(); - - /** - * TRUE if this is a count query, FALSE if it isn't. - * - * @var boolean - */ - public $count = FALSE; - - /** - * Flag indicating whether this is querying current or all revisions. - * - * @var int - * - * @see Drupal\Core\Entity\EntityFieldQuery::age() - */ - public $age = FIELD_LOAD_CURRENT; - - /** - * A list of the tags added to this query. - * - * @var array - * - * @see Drupal\Core\Entity\EntityFieldQuery::addTag() - */ - public $tags = array(); - - /** - * A list of metadata added to this query. - * - * @var array - * - * @see Drupal\Core\Entity\EntityFieldQuery::addMetaData() - */ - public $metaData = array(); - - /** - * The ordered results. - * - * @var array - * - * @see Drupal\Core\Entity\EntityFieldQuery::execute(). - */ - public $orderedResults = array(); - - /** - * The method executing the query, if it is overriding the default. - * - * @var string - * - * @see Drupal\Core\Entity\EntityFieldQuery::execute() - */ - public $executeCallback = ''; - - /** - * Adds a condition on entity-generic metadata. - * - * If the overall query contains only entity conditions or ordering, or if - * there are property conditions, then specifying the entity type is - * mandatory. If there are field conditions or ordering but no property - * conditions or ordering, then specifying an entity type is optional. While - * the field storage engine might support field conditions on more than one - * entity type, there is no way to query across multiple entity base tables by - * default. To specify the entity type, pass in 'entity_type' for $name, - * the type as a string for $value, and no $operator (it's disregarded). - * - * 'bundle', 'revision_id' and 'entity_id' have no such restrictions. - * - * Note: The "comment" entity type does not support bundle conditions. - * - * @param $name - * 'entity_type', 'bundle', 'revision_id' or 'entity_id'. - * @param $value - * The value for $name. In most cases, this is a scalar. For more complex - * options, it is an array. The meaning of each element in the array is - * dependent on $operator. - * @param $operator - * Possible values: - * - '=', '<>', '>', '>=', '<', '<=', 'STARTS_WITH', 'CONTAINS': These - * operators expect $value to be a literal of the same type as the - * column. - * - 'IN', 'NOT IN': These operators expect $value to be an array of - * literals of the same type as the column. - * - 'BETWEEN': This operator expects $value to be an array of two literals - * of the same type as the column. - * The operator can be omitted, and will default to 'IN' if the value is an - * array, or to '=' otherwise. - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - */ - public function entityCondition($name, $value, $operator = NULL) { - $this->entityConditions[$name] = array( - 'value' => $value, - 'operator' => $operator, - ); - return $this; - } - - /** - * Adds a condition on field values. - * - * Note that entities with empty field values will be excluded from the - * EntityFieldQuery results when using this method. - * - * @param $field - * Either a field name or a field array. - * @param $column - * The column that should hold the value to be matched. - * @param $value - * The value to test the column value against. - * @param $operator - * The operator to be used to test the given value. - * @param $delta_group - * An arbitrary identifier: conditions in the same group must have the same - * $delta_group. - * @param $langcode_group - * An arbitrary identifier: conditions in the same group must have the same - * $langcode_group. - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - * - * @see Drupal\Core\Entity\EntityFieldQuery::addFieldCondition() - * @see Drupal\Core\Entity\EntityFieldQuery::deleted() - */ - public function fieldCondition($field, $column = NULL, $value = NULL, $operator = NULL, $delta_group = NULL, $langcode_group = NULL) { - return $this->addFieldCondition($this->fieldConditions, $field, $column, $value, $operator, $delta_group, $langcode_group); - } - - /** - * Adds a condition on the field language column. - * - * @param $field - * Either a field name or a field array. - * @param $value - * The value to test the column value against. - * @param $operator - * The operator to be used to test the given value. - * @param $delta_group - * An arbitrary identifier: conditions in the same group must have the same - * $delta_group. - * @param $langcode_group - * An arbitrary identifier: conditions in the same group must have the same - * $langcode_group. - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - * - * @see Drupal\Core\Entity\EntityFieldQuery::addFieldCondition() - * @see Drupal\Core\Entity\EntityFieldQuery::deleted() - */ - public function fieldLanguageCondition($field, $value = NULL, $operator = NULL, $delta_group = NULL, $langcode_group = NULL) { - return $this->addFieldCondition($this->fieldMetaConditions, $field, 'langcode', $value, $operator, $delta_group, $langcode_group); - } - - /** - * Adds a condition on the field delta column. - * - * @param $field - * Either a field name or a field array. - * @param $value - * The value to test the column value against. - * @param $operator - * The operator to be used to test the given value. - * @param $delta_group - * An arbitrary identifier: conditions in the same group must have the same - * $delta_group. - * @param $langcode_group - * An arbitrary identifier: conditions in the same group must have the same - * $langcode_group. - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - * - * @see Drupal\Core\Entity\EntityFieldQuery::addFieldCondition() - * @see Drupal\Core\Entity\EntityFieldQuery::deleted() - */ - public function fieldDeltaCondition($field, $value = NULL, $operator = NULL, $delta_group = NULL, $langcode_group = NULL) { - return $this->addFieldCondition($this->fieldMetaConditions, $field, 'delta', $value, $operator, $delta_group, $langcode_group); - } - - /** - * Adds the given condition to the proper condition array. - * - * @param $conditions - * A reference to an array of conditions. - * @param $field - * Either a field name or a field array. - * @param $column - * A column defined in the hook_field_schema() of this field. If this is - * omitted then the query will find only entities that have data in this - * field, using the entity and property conditions if there are any. - * @param $value - * The value to test the column value against. In most cases, this is a - * scalar. For more complex options, it is an array. The meaning of each - * element in the array is dependent on $operator. - * @param $operator - * Possible values: - * - '=', '<>', '>', '>=', '<', '<=', 'STARTS_WITH', 'CONTAINS': These - * operators expect $value to be a literal of the same type as the - * column. - * - 'IN', 'NOT IN': These operators expect $value to be an array of - * literals of the same type as the column. - * - 'BETWEEN': This operator expects $value to be an array of two literals - * of the same type as the column. - * The operator can be omitted, and will default to 'IN' if the value is an - * array, or to '=' otherwise. - * @param $delta_group - * An arbitrary identifier: conditions in the same group must have the same - * $delta_group. For example, let's presume a multivalue field which has - * two columns, 'color' and 'shape', and for entity id 1, there are two - * values: red/square and blue/circle. Entity ID 1 does not have values - * corresponding to 'red circle', however if you pass 'red' and 'circle' as - * conditions, it will appear in the results - by default queries will run - * against any combination of deltas. By passing the conditions with the - * same $delta_group it will ensure that only values attached to the same - * delta are matched, and entity 1 would then be excluded from the results. - * @param $langcode_group - * An arbitrary identifier: conditions in the same group must have the same - * $langcode_group. - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - */ - protected function addFieldCondition(&$conditions, $field, $column = NULL, $value = NULL, $operator = NULL, $delta_group = NULL, $langcode_group = NULL) { - if (is_scalar($field)) { - $field_definition = field_info_field($field); - if (empty($field_definition)) { - throw new EntityFieldQueryException(t('Unknown field: @field_name', array('@field_name' => $field))); - } - $field = $field_definition; - } - // Ensure the same index is used for field conditions as for fields. - $index = count($this->fields); - $this->fields[$index] = $field; - if (isset($column)) { - $conditions[$index] = array( - 'field' => $field, - 'column' => $column, - 'value' => $value, - 'operator' => $operator, - 'delta_group' => $delta_group, - 'langcode_group' => $langcode_group, - ); - } - return $this; - } - - /** - * Adds a condition on an entity-specific property. - * - * An $entity_type must be specified by calling - * EntityFieldCondition::entityCondition('entity_type', $entity_type) before - * executing the query. Also, by default only entities stored in SQL are - * supported; however, EntityFieldQuery::executeCallback can be set to handle - * different entity storage. - * - * @param $column - * A column defined in the hook_schema() of the base table of the entity. - * @param $value - * The value to test the field against. In most cases, this is a scalar. For - * more complex options, it is an array. The meaning of each element in the - * array is dependent on $operator. - * @param $operator - * Possible values: - * - '=', '<>', '>', '>=', '<', '<=', 'STARTS_WITH', 'CONTAINS': These - * operators expect $value to be a literal of the same type as the - * column. - * - 'IN', 'NOT IN': These operators expect $value to be an array of - * literals of the same type as the column. - * - 'BETWEEN': This operator expects $value to be an array of two literals - * of the same type as the column. - * The operator can be omitted, and will default to 'IN' if the value is an - * array, or to '=' otherwise. - * @param $langcode_group - * (optional) An arbitrary identifier: conditions in the same group must - * have the same group identifier. This is used to group the condition with - * a related set of other property conditions and meta conditions. By - * default all conditions belong to the same group. - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - * - * @see Drupal\entity\EntityFieldQuery::propertyLanguageCondition() - */ - public function propertyCondition($column, $value, $operator = NULL, $langcode_group = 0) { - $this->properties[$langcode_group][$column] = $column; - $this->propertyConditions[] = array( - 'column' => $column, - 'value' => $value, - 'operator' => $operator, - 'langcode_group' => $langcode_group, - ); - return $this; - } - - - /** - * Adds a condition on the property language. - * - * Since the entity storage controller may support multilingual properties, - * there may be cases where different conditions on different languages may be - * needed. This method allows to specify which language a particular set of - * conditions and order settings should have. For example: - * @code - * $query = new EntityFieldQuery(); - * $query->entityCondition('entity_type', 'entity_test'); - * // Find all English entities. - * $query->entityCondition('langcode', 'en'); - * // Having the specified English values for uid and name. - * $query->propertyCondition('uid', $uid, '=', 'original'); - * $query->propertyCondition('name', $name, '=', 'original'); - * $query->propertyLanguageCondition('en', '=', 'original'); - * // And having the specified Italian value for name. - * $query->propertyCondition('name', $name_it, '=', 'translation'); - * $query->propertyLanguageCondition('it', '=', 'translation'); - * // Order the result set by the English name. - * $query->propertyOrderBy('name', 'ASC', 'original'); - * $result = $query->execute(); - * @endcode - * Without specifiying two different language groupings there would be no way - * to apply both name conditions, as they would mutually exclude each other. - * - * @param $langcode - * The language code that the properties belonging to the language group - * should match. - * @param $operator - * The operator to be used to test the given value. - * @param $langcode_group - * (optional) An arbitrary identifier: conditions in the same group must - * have the same group identifier. This is used to group the language meta - * condition with a related set of property conditions. By default all - * conditions belong to the same group. - * - * @return Drupal\entity\EntityFieldQuery - * The called object. - * - * @see Drupal\entity\EntityFieldQuery::addFieldCondition() - * @see Drupal\entity\EntityFieldQuery::deleted() - */ - public function propertyLanguageCondition($langcode = NULL, $operator = NULL, $langcode_group = 0) { - // We have a separate method here to ensure there is a distinction at API - // level between properties and metadata, even if from the implementation - // perspective both conditions are implemented the same way. However this - // might not be the case in other storages. - // @todo Actually we could also implement the same functionality and keep - // this distinction by using language codes as group identifiers: - // - // $query->propertyCondition('title', 'english_title', NULL, 'en'); - // $query->propertyCondition('uid', 1, NULL, 'en'); - // $query->propertyCondition('title', 'german_title', NULL, 'de'); - // - // We probably want to move to this approach when refactoring EFQ to work - // with storage-independent entities. For now we are keeping the current - // approach for consistency with field meta conditions. - return $this->propertyCondition('langcode', $langcode, $operator, $langcode_group); - } - - /** - * Orders the result set by entity-generic metadata. - * - * If called multiple times, the query will order by each specified column in - * the order this method is called. - * - * Note: The "comment" and "taxonomy_term" entity types don't support ordering - * by bundle. For "taxonomy_term", propertyOrderBy('vid') can be used instead. - * - * @param $name - * 'entity_type', 'bundle', 'revision_id' or 'entity_id'. - * @param $direction - * The direction to sort. Legal values are "ASC" and "DESC". - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - */ - public function entityOrderBy($name, $direction = 'ASC') { - $this->order[] = array( - 'type' => 'entity', - 'specifier' => $name, - 'direction' => $direction, - ); - return $this; - } - - /** - * Orders the result set by a given field column. - * - * If called multiple times, the query will order by each specified column in - * the order this method is called. Note that entities with empty field - * values will be excluded from the EntityFieldQuery results when using this - * method. - * - * @param $field - * Either a field name or a field array. - * @param $column - * A column defined in the hook_field_schema() of this field. entity_id and - * bundle can also be used. - * @param $direction - * The direction to sort. Legal values are "ASC" and "DESC". - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - */ - public function fieldOrderBy($field, $column, $direction = 'ASC') { - if (is_scalar($field)) { - $field_definition = field_info_field($field); - if (empty($field_definition)) { - throw new EntityFieldQueryException(t('Unknown field: @field_name', array('@field_name' => $field))); - } - $field = $field_definition; - } - // Save the index used for the new field, for later use in field storage. - $index = count($this->fields); - $this->fields[$index] = $field; - $this->order[] = array( - 'type' => 'field', - 'specifier' => array( - 'field' => $field, - 'index' => $index, - 'column' => $column, - ), - 'direction' => $direction, - ); - return $this; - } - - /** - * Orders the result set by an entity-specific property. - * - * An $entity_type must be specified by calling - * EntityFieldCondition::entityCondition('entity_type', $entity_type) before - * executing the query. - * - * If called multiple times, the query will order by each specified column in - * the order this method is called. - * - * @param $column - * The column on which to order. - * @param $direction - * The direction to sort. Legal values are "ASC" and "DESC". - * @param $langcode_group - * (optional) An arbitrary identifier: order settings in the same group must - * have the same group identifier. This is used to group the property order - * setting with a related set of property conditions, meta conditions and - * other order settings. By default all conditions and order settings belong - * to the same group. - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - * - * @see Drupal\entity\EntityFieldQuery::propertyLanguageCondition() - */ - public function propertyOrderBy($column, $direction = 'ASC', $langcode_group = 0) { - $this->properties[$langcode_group][$column] = $column; - $this->order[] = array( - 'type' => 'property', - 'specifier' => $column, - 'direction' => $direction, - 'langcode_group' => $langcode_group, - ); - return $this; - } - - /** - * Sets the query to be a count query only. - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - */ - public function count() { - $this->count = TRUE; - return $this; - } - - /** - * Restricts a query to a given range in the result set. - * - * @param $start - * The first entity from the result set to return. If NULL, removes any - * range directives that are set. - * @param $length - * The number of entities to return from the result set. - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - */ - public function range($start = NULL, $length = NULL) { - $this->range = array( - 'start' => $start, - 'length' => $length, - ); - return $this; - } - - /** - * Enables a pager for the query. - * - * @param $limit - * An integer specifying the number of elements per page. If passed a false - * value (FALSE, 0, NULL), the pager is disabled. - * @param $element - * An optional integer to distinguish between multiple pagers on one page. - * If not provided, one is automatically calculated. - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - */ - public function pager($limit = 10, $element = NULL) { - if (!isset($element)) { - $element = PagerSelectExtender::$maxElement++; - } - elseif ($element >= PagerSelectExtender::$maxElement) { - PagerSelectExtender::$maxElement = $element + 1; - } - - $this->pager = array( - 'limit' => $limit, - 'element' => $element, - ); - return $this; - } - - /** - * Enables sortable tables for this query. - * - * @param $headers - * An EFQ Header array based on which the order clause is added to the - * query. - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - */ - public function tableSort(&$headers) { - // If 'field' is not initialized, the header columns aren't clickable - foreach ($headers as $key =>$header) { - if (is_array($header) && isset($header['specifier'])) { - $headers[$key]['field'] = ''; - } - } - - $order = tablesort_get_order($headers); - $direction = tablesort_get_sort($headers); - foreach ($headers as $header) { - if (is_array($header) && ($header['data'] == $order['name'])) { - if ($header['type'] == 'field') { - $this->fieldOrderBy($header['specifier']['field'], $header['specifier']['column'], $direction); - } - else { - $header['direction'] = $direction; - $this->order[] = $header; - } - } - } - - return $this; - } - - /** - * Filters on the data being deleted. - * - * @param $deleted - * TRUE to only return deleted data, FALSE to return non-deleted data, - * EntityFieldQuery::RETURN_ALL to return everything. Defaults to FALSE. - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - */ - public function deleted($deleted = TRUE) { - $this->deleted = $deleted; - return $this; - } - - /** - * Queries the current or every revision. - * - * Note that this only affects field conditions. Property conditions always - * apply to the current revision. - * @TODO: Once revision tables have been cleaned up, revisit this. - * - * @param $age - * - FIELD_LOAD_CURRENT (default): Query the most recent revisions for all - * entities. The results will be keyed by entity type and entity ID. - * - FIELD_LOAD_REVISION: Query all revisions. The results will be keyed by - * entity type and entity revision ID. - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - */ - public function age($age) { - $this->age = $age; - return $this; - } - - /** - * Adds a tag to the query. - * - * Tags are strings that mark a query so that hook_query_alter() and - * hook_query_TAG_alter() implementations may decide if they wish to alter - * the query. A query may have any number of tags, and they must be valid PHP - * identifiers (composed of letters, numbers, and underscores). For example, - * queries involving nodes that will be displayed for a user need to add the - * tag 'node_access', so that the node module can add access restrictions to - * the query. - * - * If an entity field query has tags, it must also have an entity type - * specified, because the alter hook will need the entity base table. - * - * @param string $tag - * The tag to add. - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - */ - public function addTag($tag) { - $this->tags[$tag] = $tag; - return $this; - } - - /** - * Adds additional metadata to the query. - * - * Sometimes a query may need to provide additional contextual data for the - * alter hook. The alter hook implementations may then use that information - * to decide if and how to take action. - * - * @param $key - * The unique identifier for this piece of metadata. Must be a string that - * follows the same rules as any other PHP identifier. - * @param $object - * The additional data to add to the query. May be any valid PHP variable. - * - * @return Drupal\Core\Entity\EntityFieldQuery - * The called object. - */ - public function addMetaData($key, $object) { - $this->metaData[$key] = $object; - return $this; - } - - /** - * Executes the query. - * - * After executing the query, $this->orderedResults will contain a list of - * the same entity ids in the order returned by the query. This is only - * relevant if there are multiple entity types in the returned value and - * a field ordering was requested. In every other case, the returned value - * contains everything necessary for processing. - * - * @return - * Either a number if count() was called or an array of associative arrays - * of the entity ids. The outer array keys are entity types, and the inner - * array keys are the relevant ID. (In most cases this will be the entity - * ID. The only exception is when age=FIELD_LOAD_REVISION is used and field - * conditions or sorts are present -- in this case, the key will be the - * revision ID.) The entity type will only exist in the outer array if - * results were found. The inner array values consist of an object with the - * entity_id, revision_id and bundle properties. To traverse the returned - * array: - * @code - * foreach ($query->execute() as $entity_type => $entities) { - * foreach ($entities as $entity_id => $entity) { - * @endcode - * Note if the entity type is known, then the following snippet will load - * the entities found: - * @code - * $result = $query->execute(); - * if (!empty($result[$my_type])) { - * $entities = entity_load_multiple($my_type, array_keys($result[$my_type])); - * } - * @endcode - */ - public function execute() { - // Give a chance to other modules to alter the query. - drupal_alter('entity_query', $this); - $this->altered = TRUE; - - // Initialize the pager. - $this->initializePager(); - - // Execute the query using the correct callback. - $result = call_user_func($this->queryCallback(), $this); - - return $result; - } - - /** - * Determines the query callback to use for this entity query. - * - * @return - * A callback that can be used with call_user_func(). - */ - public function queryCallback() { - // Use the override from $this->executeCallback. It can be set either - // while building the query, or using hook_entity_query_alter(). - if (function_exists($this->executeCallback)) { - return $this->executeCallback; - } - // If there are no field conditions and sorts, and no execute callback - // then we default to querying entity tables in SQL. - if (empty($this->fields)) { - return array($this, 'propertyQuery'); - } - // If no override, find the storage engine to be used. - foreach ($this->fields as $field) { - if (!isset($storage)) { - $storage = $field['storage']['module']; - } - elseif ($storage != $field['storage']['module']) { - throw new EntityFieldQueryException(t("Can't handle more than one field storage engine")); - } - } - if ($storage) { - // Use hook_field_storage_query() from the field storage. - return $storage . '_field_storage_query'; - } - else { - throw new EntityFieldQueryException(t("Field storage engine not found.")); - } - } - - /** - * Queries entity tables in SQL for property conditions and sorts. - * - * This method is only used if there are no field conditions and sorts. - * - * @return - * See EntityFieldQuery::execute(). - */ - protected function propertyQuery() { - if (empty($this->entityConditions['entity_type'])) { - throw new EntityFieldQueryException(t('For this query an entity type must be specified.')); - } - - $entity_type = $this->entityConditions['entity_type']['value']; - $entity_info = entity_get_info($entity_type); - if (empty($entity_info['base table'])) { - throw new EntityFieldQueryException(t('Entity %entity has no base table.', array('%entity' => $entity_type))); - } - - $base_table = $entity_info['base table']; - $select_query = db_select($base_table); - $select_query->addExpression(':entity_type', 'entity_type', array(':entity_type' => $entity_type)); - $sql_field = $entity_info['entity keys']['id']; - - // If a data table is defined we need to join it and make sure that only one - // record per entity is returned. - $this->joinPropertyData($select_query, $entity_type, $base_table); - - // Process the property conditions. - $this->addPropertyConditions($select_query, $entity_type); - - // Process the six possible entity condition. - // The id field is always present in entity keys. - $id_map['entity_id'] = $sql_field; - $select_query->addField($base_table, $sql_field, 'entity_id'); - if (isset($this->entityConditions['entity_id'])) { - $this->addCondition($select_query, "$base_table.$sql_field", $this->entityConditions['entity_id']); - } - - // If there is a revision key defined, use it. - if (!empty($entity_info['entity keys']['revision'])) { - $sql_field = $entity_info['entity keys']['revision']; - $select_query->addField($base_table, $sql_field, 'revision_id'); - if (isset($this->entityConditions['revision_id'])) { - $this->addCondition($select_query, "$base_table.$sql_field", $this->entityConditions['revision_id']); - } - } - else { - $sql_field = 'revision_id'; - $select_query->addExpression('NULL', 'revision_id'); - } - $id_map['revision_id'] = $sql_field; - - // Handle bundles. - if (!empty($entity_info['entity keys']['bundle'])) { - $base_table_schema = drupal_get_schema($base_table); - $sql_field = $entity_info['entity keys']['bundle']; - $having = FALSE; - if (!empty($base_table_schema['fields'][$sql_field])) { - $select_query->addField($base_table, $sql_field, 'bundle'); - } - } - else { - $sql_field = 'bundle'; - $select_query->addExpression(':bundle', 'bundle', array(':bundle' => $entity_type)); - $having = TRUE; - } - - $id_map['bundle'] = $sql_field; - - if (isset($this->entityConditions['bundle'])) { - if (!empty($entity_info['entity keys']['bundle'])) { - $this->addCondition($select_query, "$base_table.$sql_field", $this->entityConditions['bundle'], $having); - } - else { - // This entity has no bundle, so invalidate the query. - $select_query->where('1 = 0'); - } - } - - foreach (array('uuid', 'langcode') as $key) { - if (isset($this->entityConditions[$key])) { - $sql_field = !empty($entity_info['entity keys'][$key]) ? $entity_info['entity keys'][$key] : $key; - if (isset($base_table_schema[$sql_field])) { - $this->addCondition($select_query, "$base_table.$sql_field", $this->entityConditions[$key]); - } - } - } - - // Order the query. - foreach ($this->order as $order) { - if ($order['type'] == 'entity') { - $key = $order['specifier']; - if (!isset($id_map[$key])) { - throw new EntityFieldQueryException(t('Do not know how to order on @key for @entity_type', array('@key' => $key, '@entity_type' => $entity_type))); - } - $select_query->orderBy($id_map[$key], $order['direction']); - } - elseif ($order['type'] == 'property') { - $this->addPropertyOrderBy($select_query, $entity_type, $order); - } - } - - return $this->finishQuery($select_query); - } - - /** - * Gets the total number of results and initialize a pager for the query. - * - * The pager can be disabled by either setting the pager limit to 0, or by - * setting this query to be a count query. - */ - function initializePager() { - if ($this->pager && !empty($this->pager['limit']) && !$this->count) { - $page = pager_find_page($this->pager['element']); - $count_query = clone $this; - $this->pager['total'] = $count_query->count()->execute(); - $this->pager['start'] = $page * $this->pager['limit']; - pager_default_initialize($this->pager['total'], $this->pager['limit'], $this->pager['element']); - $this->range($this->pager['start'], $this->pager['limit']); - } - } - - /** - * Finishes the query. - * - * Adds tags, metaData, range and returns the requested list or count. - * - * @param SelectQuery $select_query - * A SelectQuery which has entity_type, entity_id, revision_id and bundle - * fields added. - * @param $id_key - * Which field's values to use as the returned array keys. - * - * @return - * See EntityFieldQuery::execute(). - */ - function finishQuery($select_query, $id_key = 'entity_id') { - foreach ($this->tags as $tag) { - $select_query->addTag($tag); - } - foreach ($this->metaData as $key => $object) { - $select_query->addMetaData($key, $object); - } - $select_query->addMetaData('entity_field_query', $this); - if ($this->range) { - $select_query->range($this->range['start'], $this->range['length']); - } - if ($this->count) { - return $select_query->countQuery()->execute()->fetchField(); - } - $return = array(); - foreach ($select_query->execute() as $ids) { - if (!isset($ids->bundle)) { - $ids->bundle = NULL; - } - $return[$ids->entity_type][$ids->$id_key] = $ids; - $this->ordered_results[] = $ids; - } - return $return; - } - - /** - * Adds a condition to an already built SelectQuery (internal function). - * - * This is a helper for hook_entity_query() and hook_field_storage_query(). - * - * @param SelectQuery $select_query - * A SelectQuery object. - * @param $sql_field - * The name of the field. - * @param $condition - * A condition as described in EntityFieldQuery::fieldCondition() and - * EntityFieldQuery::entityCondition(). - * @param $having - * HAVING or WHERE. This is necessary because SQL can't handle WHERE - * conditions on aliased columns. - */ - public function addCondition(Select $select_query, $sql_field, $condition, $having = FALSE) { - $method = $having ? 'havingCondition' : 'condition'; - $like_prefix = ''; - switch ($condition['operator']) { - case 'CONTAINS': - $like_prefix = '%'; - case 'STARTS_WITH': - $select_query->$method($sql_field, $like_prefix . db_like($condition['value']) . '%', 'LIKE'); - break; - default: - $select_query->$method($sql_field, $condition['value'], $condition['operator']); - } - } - - /** - * Adds property conditions to a select query performing the needed joins. - * - * @param SelectQuery $select_query - * The SelectQuery the conditions should be applied to. - * @param $entity_type - * The entity type the query applies to. - */ - public function addPropertyConditions(Select $select_query, $entity_type) { - $entity_info = entity_get_info($entity_type); - $entity_base_table = $entity_info['base table']; - list($data_table, $data_table_schema) = $this->getPropertyDataSchema($entity_type); - - foreach ($this->propertyConditions as $property_condition) { - $column = $property_condition['column']; - // @todo Property conditions should always apply to the data table (if - // available), however UUIDs are used in load conditions and thus treated - // as properties, instead of being set as entity conditions. Remove this - // once we can reliably distinguish between properties and metadata living - // on the base table. - $table = !empty($data_table_schema['fields'][$column]) ? $data_table . '_' . $property_condition['langcode_group'] : $entity_base_table; - $this->addCondition($select_query, "$table.$column", $property_condition); - } - } - - /** - * Adds a property order by to the given select query. - * - * @param SelectQuery $select_query - * The SelectQuery the conditions should be applied to. - * @param $entity_type - * The entity type the query applies to. - * @param array $order - * An order array as defined in EntityFieldQuery::propertyOrderBy(). - */ - public function addPropertyOrderBy(Select $select_query, $entity_type, array $order) { - $entity_info = entity_get_info($entity_type); - list($data_table, $data_table_schema) = $this->getPropertyDataSchema($entity_type); - $specifier = $order['specifier']; - $table = !empty($data_table_schema['fields'][$specifier]) ? $data_table . '_' . $order['langcode_group'] : $entity_info['base table']; - $select_query->orderBy("$table.$specifier", $order['direction']); - } - - /** - * Joins the needed data tables based on the specified property conditions. - * - * @param SelectQuery $select_query - * A SelectQuery containing at least one table as specified by $base_table. - * @param $entity_type - * The entity type the query applies to. - * @param $base_table - * The name of the base table to join on. - * @param $base_id_key - * The primary id column name to use to join on the base table. - */ - public function joinPropertyData(Select $select_query, $entity_type, $base_table, $base_id_key = NULL) { - list($data_table, $data_table_schema) = $this->getPropertyDataSchema($entity_type); - - // If we have no data table there are no property meta conditions to handle. - if (!empty($data_table)) { - $entity_info = entity_get_info($entity_type); - $id_key = $entity_info['entity keys']['id']; - $base_id_key = !empty($base_id_key) ? $base_id_key : $id_key; - - foreach ($this->properties as $key => $property) { - // Every property needs a new join on the data table. - $table_alias = $data_table . '_' . $key; - $table_aliases[$key] = $table_alias; - $select_query->join($data_table, $table_alias, "$table_alias.$id_key = $base_table.$base_id_key"); - } - - // Ensure we return just one value. - $select_query->distinct(); - } - } - - /** - * Returns the data table schema for the given entity type. - * - * @param $entity_type - * The entity type the query applies to. - * - * @return array - * An array containing the table data name (or FALSE if none is defined) and - * its schema. - */ - protected function getPropertyDataSchema($entity_type) { - $entity_info = entity_get_info($entity_type); - - if (!empty($entity_info['data table'])) { - $data_table = $entity_info['data table']; - $data_table_schema = drupal_get_schema($data_table); - } - else { - $data_table = FALSE; - $data_table_schema = array(); - } - - return array($data_table, $data_table_schema); - } -} diff --git a/core/lib/Drupal/Core/Entity/EntityFieldQueryException.php b/core/lib/Drupal/Core/Entity/EntityFieldQueryException.php deleted file mode 100644 index bf792a5..0000000 --- a/core/lib/Drupal/Core/Entity/EntityFieldQueryException.php +++ /dev/null @@ -1,19 +0,0 @@ -create() the follow keys are supported: - * - queryable: Whether the field is queryable via EntityFieldQuery. + * - queryable: Whether the field is queryable via QueryInterface. * Defaults to TRUE if 'computed' is FALSE or not set, to FALSE otherwise. * - translatable: Whether the field is translatable. Defaults to FALSE. * - configurable: A boolean indicating whether the field is configurable diff --git a/core/lib/Drupal/Core/Entity/Query/ConditionBase.php b/core/lib/Drupal/Core/Entity/Query/ConditionBase.php new file mode 100644 index 0000000..fdc9028 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Query/ConditionBase.php @@ -0,0 +1,92 @@ +conjunction = $conjunction; + } + + public function getConjunction() { + return $this->conjunction; + } + + /** + * Implements Countable::count(). + * + * Returns the size of this conditional. The size of the conditional is the + * size of its conditional array minus one, because one element is the the + * conjunction. + */ + public function count() { + return count($this->conditions) - 1; + } + + public function condition($field, $value = NULL, $operator = NULL) { + $this->conditions[] = array( + 'field' => $field, + 'value' => $value, + 'operator' => $operator, + ); + + $this->changed = TRUE; + + return $this; + } + + public function isNull($field) { + return $this->condition($field, NULL, 'IS NULL'); + } + + public function isNotNull($field) { + return $this->condition($field, NULL, 'IS NOT NULL'); + } + + public function &conditions() { + return $this->conditions; + } + + function __clone() { + foreach ($this->conditions as $key => $condition) { + if ($condition['field'] instanceOf ConditionInterface) { + $this->conditions[$key]['field'] = clone($condition['field']); + } + } + } + +} diff --git a/core/lib/Drupal/Core/Entity/Query/ConditionInterface.php b/core/lib/Drupal/Core/Entity/Query/ConditionInterface.php new file mode 100644 index 0000000..3c12363 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Query/ConditionInterface.php @@ -0,0 +1,65 @@ +entityType = $entity_type; + $this->condition = $this->conditionFactory($conjunction); + } + + /** + * Implements Drupal\Core\Entity\Query\QueryInterface::getEntityType(). + */ + public function getEntityType() { + return $this->entityType; + } + + /** + * Implements Drupal\Core\Entity\Query\QueryInterface::condition(). + */ + public function condition($property, $value = NULL, $operator = NULL) { + $this->condition->condition($property, $value, $operator); + return $this; + } + + /** + * Implements Drupal\Core\Entity\Query\QueryInterface::range(). + */ + public function range($start = NULL, $length = NULL) { + $this->range = array( + 'start' => $start, + 'length' => $length, + ); + return $this; + } + + /** + * Implements Drupal\Core\Entity\Query\QueryInterface::andCondition(). + */ + public function andCondition() { + return $this->conditionFactory('and'); + } + + /** + * Implements Drupal\Core\Entity\Query\QueryInterface::orCondition(). + */ + public function orCondition() { + return $this->conditionFactory('or'); + } + + /** + * Implements Drupal\Core\Entity\Query\QueryInterface::orderBy(). + */ + public function orderBy($property, $direction = 'ASC') { + $this->sort[$property] = $direction; + return $this; + } + + /** + * Implements Drupal\Core\Entity\Query\QueryInterface::count(). + */ + public function count() { + $this->count = TRUE; + return $this; + } + + /** + * Implements Drupal\Core\Entity\Query\QueryInterface::skipAccessCheck(). + */ + public function skipAccessCheck() { + $this->accessCheck = FALSE; + return $this; + } + +} diff --git a/core/lib/Drupal/Core/Entity/Query/QueryException.php b/core/lib/Drupal/Core/Entity/Query/QueryException.php new file mode 100644 index 0000000..a1d54d6 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Query/QueryException.php @@ -0,0 +1,17 @@ +container = $container; + } + + /** + * @param string $entity_type + * @param string $conjunction + * @return QueryInterface + */ + public function get($entity_type, $conjunction = "AND") { + $storages = array(); + $storage_type = ''; + foreach (field_info_instances($entity_type) as $instances) { + foreach ($instances as $instance) { + $field = field_info_field_by_id($instance['field_id']); + $field_id = $field['id']; + $storage_type = $field['storage']['type']; + $storages[$storage_type][$field_id] = $field_id; + } + } + if ($storage_type) { + if (count($storages) > 1) { + throw new QueryException('Can not query across storage engines'); + } + $storage_info = field_info_storage_types($storage_type); + $module = $storage_info['module']; + } + else { + $module = 'field_sql_storage'; + } + return $this->container->get("entity.query.$module")->get($entity_type, $conjunction); + } + +} diff --git a/core/lib/Drupal/Core/Entity/Query/QueryInterface.php b/core/lib/Drupal/Core/Entity/Query/QueryInterface.php new file mode 100644 index 0000000..db24892 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Query/QueryInterface.php @@ -0,0 +1,78 @@ + array(), 'indexes' => array(), 'foreign keys' => array()); // 'columns' are hardcoded in the field type. $field['columns'] = $schema['columns']; + if (array_intersect(array_keys($field['columns']), field_common_columns())) { + throw new FieldException(t('Illegal field type columns.')); + } // 'foreign keys' are hardcoded in the field type. $field['foreign keys'] = $schema['foreign keys']; // 'indexes' can be both hardcoded in the field type, and specified in the @@ -873,31 +875,33 @@ function field_purge_batch($batch_size) { // Retrieve all deleted field instances. We cannot use field_info_instances() // because that function does not return deleted instances. $instances = field_read_instances(array('deleted' => 1), array('include_deleted' => 1)); - + $factory = drupal_container()->get('entity.query'); + $info = entity_get_info(); foreach ($instances as $instance) { + $entity_type = $instance['entity_type']; + $ids = (object) array( + 'entity_type' => $entity_type, + 'bundle' => $instance['bundle'], + ); // field_purge_data() will need the field array. $field = field_info_field_by_id($instance['field_id']); // Retrieve some entities. - $query = new EntityFieldQuery(); - $results = $query - ->fieldCondition($field) - ->entityCondition('bundle', $instance['bundle']) - ->deleted(TRUE) + $results = $factory->get($entity_type) + ->condition('id:' . $field['id'] . '.deleted', 1) + ->condition($info[$entity_type]['entity keys']['bundle'], $ids->bundle) ->range(0, $batch_size) ->execute(); if ($results) { - foreach ($results as $entity_type => $ids_array) { - $entities = array(); - foreach ($ids_array as $ids) { - $entities[$ids->entity_id] = _field_create_entity_from_ids($ids); - } - - field_attach_load($entity_type, $entities, FIELD_LOAD_CURRENT, array('field_id' => $field['id'], 'deleted' => 1)); - foreach ($entities as $entity) { - // Purge the data for the entity. - field_purge_data($entity_type, $entity, $field, $instance); - } + $entities = array(); + foreach ($results as $entity_id) { + $ids->entity_id = $entity_id; + $entities[$entity_id] = _field_create_entity_from_ids($ids); + } + field_attach_load($entity_type, $entities, FIELD_LOAD_CURRENT, array('field_id' => $field['id'], 'deleted' => 1)); + foreach ($entities as $entity) { + // Purge the data for the entity. + field_purge_data($entity_type, $entity, $field, $instance); } } else { diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 1c3c4fb..3d78652 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -4,8 +4,8 @@ * Attach custom data fields to Drupal entities. */ -use Drupal\Core\Entity\EntityFieldQuery; use Drupal\Core\Template\Attribute; +use Drupal\Core\Entity\Query\QueryFactory; /* * Load all public Field API functions. Drupal currently has no @@ -1075,16 +1075,21 @@ function field_get_items($entity_type, $entity, $field_name, $langcode = NULL) { * TRUE if the field has data for any entity; FALSE otherwise. */ function field_has_data($field) { - $query = new EntityFieldQuery(); - return (bool) $query - ->fieldCondition($field) - ->range(0, 1) - ->count() - // Neutralize the 'entity_field_access' query tag added by - // field_sql_storage_field_storage_query(). The result cannot depend on the - // access grants of the current user. - ->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT') - ->execute(); + $field = field_info_field_by_id($field['id']); + $factory = drupal_container()->get('entity.query'); + foreach($field['bundles'] as $entity_type => $bundle) { + $result = $factory->get($entity_type) + ->condition($field['field_name'] .'.delta', 0, '>=') + ->count() + ->skipAccessCheck() + ->range(0, 1) + ->execute(); + if ($result) { + return TRUE; + } + } + + return FALSE; } /** @@ -1317,9 +1322,6 @@ function theme_field($variables) { /** * Assembles a partial entity structure with initial IDs. * - * This can be used to create an entity based on the the ids object returned by - * EntityFieldQuery. - * * @param stdClass $ids * An object with the properties entity_type (required), entity_id (required), * revision_id (optional) and bundle (optional). @@ -1338,4 +1340,13 @@ function _field_create_entity_from_ids($ids) { $id_properties[$info['entity keys']['bundle']] = $ids->bundle; } return entity_create($ids->entity_type, $id_properties); - } +} + +/** + * A list of columns that can not be used as field type columns. + * + * @return array + */ +function field_common_columns() { + return array(/*'entity_type', 'bundle', 'entity_id', 'revision_id', */'deleted', 'langcode', 'delta'); +} diff --git a/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php b/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php index 84b6f06..f1404a5 100644 --- a/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php @@ -7,8 +7,6 @@ namespace Drupal\field\Tests; -use Drupal\Core\Entity\EntityFieldQuery; - /** * Unit test class for field bulk delete and batch purge functionality. */ @@ -50,7 +48,7 @@ protected function convertToPartialEntities($entities, $field_name) { foreach ($entities as $id => $entity) { // Re-create the entity with only the required keys, remove label as that // is not present when using _field_create_entity_from_ids(). - $partial_entities[$id] = field_test_create_entity($entity->ftid, $entity->ftvid, $entity->fttype, $entity->ftlabel); + $partial_entities[$id] = field_test_create_entity($entity->ftid, NULL, $entity->fttype, $entity->ftlabel); // Remove the label and set enforceIsNew to NULL to make sure that the // entity classes match the actual arguments. unset($partial_entities[$id]->ftlabel); @@ -113,7 +111,7 @@ function setUp() { // For each bundle, create an instance of each field, and 10 // entities with values for each field. - $id = 0; + $id = 1; $this->entity_type = 'test_entity'; foreach ($this->bundles as $bundle) { foreach ($this->fields as $field) { @@ -133,14 +131,15 @@ function setUp() { foreach ($this->fields as $field) { $entity->{$field['field_name']}[LANGUAGE_NOT_SPECIFIED] = $this->_generateTestFieldValues($field['cardinality']); } - - $this->entities[$id] = $entity; - // Also keep track of the entities per bundle. - $this->entities_by_bundles[$bundle][$id] = $entity; - field_attach_insert($this->entity_type, $entity); + $entity->save(); $id++; } } + $this->entities = entity_load_multiple($this->entity_type, range(1, $id)); + foreach ($this->entities as $entity) { + // Also keep track of the entities per bundle. + $this->entities_by_bundles[$entity->fttype][$entity->ftid] = $entity; + } } /** @@ -155,14 +154,13 @@ function setUp() { function testDeleteFieldInstance() { $bundle = reset($this->bundles); $field = reset($this->fields); + $field_name = $field['field_name']; // There are 10 entities of this bundle. - $query = new EntityFieldQuery(); - $found = $query - ->fieldCondition($field) - ->entityCondition('bundle', $bundle) + $found = drupal_container()->get('entity.query')->get('test_entity') + ->condition('fttype', $bundle) ->execute(); - $this->assertEqual(count($found['test_entity']), 10, 'Correct number of entities found before deleting'); + $this->assertEqual(count($found), 10, 'Correct number of entities found before deleting'); // Delete the instance. $instance = field_info_instance($this->entity_type, $field['field_name'], $bundle); @@ -174,27 +172,30 @@ function testDeleteFieldInstance() { $this->assertEqual($instances[0]['bundle'], $bundle, 'The deleted instance is for the correct bundle'); // There are 0 entities of this bundle with non-deleted data. - $query = new EntityFieldQuery(); - $found = $query - ->fieldCondition($field) - ->entityCondition('bundle', $bundle) + $found = drupal_container()->get('entity.query')->get('test_entity') + ->condition('fttype', $bundle) + ->condition("$field_name.deleted", 0) ->execute(); - $this->assertTrue(!isset($found['test_entity']), 'No entities found after deleting'); + $this->assertFalse($found, 'No entities found after deleting'); // There are 10 entities of this bundle when deleted fields are allowed, and // their values are correct. - $query = new EntityFieldQuery(); - $found = $query - ->fieldCondition($field) - ->entityCondition('bundle', $bundle) - ->deleted(TRUE) + $found = drupal_container()->get('entity.query')->get('test_entity') + ->condition('fttype', $bundle) + ->condition("$field_name.deleted", 1) + ->orderBy('ftid') ->execute(); - $entities = array(); - foreach ($found[$this->entity_type] as $ids) { - $entities[$ids->entity_id] = _field_create_entity_from_ids($ids); + $ids = (object) array( + 'entity_type' => 'test_entity', + 'bundle' => $bundle, + ); + $entities = array();; + foreach ($found as $entity_id) { + $ids->entity_id = $entity_id; + $entities[$entity_id] = _field_create_entity_from_ids($ids); } field_attach_load($this->entity_type, $entities, FIELD_LOAD_CURRENT, array('field_id' => $field['id'], 'deleted' => 1)); - $this->assertEqual(count($found['test_entity']), 10, 'Correct number of entities found after deleting'); + $this->assertEqual(count($found), 10, 'Correct number of entities found after deleting'); foreach ($entities as $id => $entity) { $this->assertEqual($this->entities[$id]->{$field['field_name']}, $entity->{$field['field_name']}, "Entity $id with deleted data loaded correctly"); } @@ -225,13 +226,11 @@ function testPurgeInstance() { field_purge_batch($batch_size); // There are $count deleted entities left. - $query = new EntityFieldQuery(); - $found = $query - ->fieldCondition($field) - ->entityCondition('bundle', $bundle) - ->deleted(TRUE) + $found = drupal_container()->get('entity.query')->get($this->entity_type) + ->condition('fttype', $bundle) + ->condition($field['field_name'] . '.deleted', 1) ->execute(); - $this->assertEqual($count ? count($found['test_entity']) : count($found), $count, 'Correct number of entities found after purging 2'); + $this->assertEqual(count($found), $count, 'Correct number of entities found after purging 2'); } // Check hooks invocations. diff --git a/core/modules/field/modules/field_sql_storage/field_sql_storage.module b/core/modules/field/modules/field_sql_storage/field_sql_storage.module index 26b2ba8..9809bf0 100644 --- a/core/modules/field/modules/field_sql_storage/field_sql_storage.module +++ b/core/modules/field/modules/field_sql_storage/field_sql_storage.module @@ -6,9 +6,6 @@ */ use Drupal\Core\Database\Database; -use Drupal\Core\Database\Query\Select; -use Drupal\Core\Entity\EntityFieldQuery; -use Drupal\Core\Entity\EntityFieldQueryException; use Drupal\field\FieldUpdateForbiddenException; /** @@ -85,7 +82,7 @@ function _field_sql_storage_revision_tablename($field) { * unique among all other fields. */ function _field_sql_storage_columnname($name, $column) { - return $name . '_' . $column; + return in_array($column, field_common_columns()) ? $column : $name . '_' . $column; } /** @@ -505,191 +502,6 @@ function field_sql_storage_field_storage_purge($entity_type, $entity, $field, $i } /** - * Implements hook_field_storage_query(). - */ -function field_sql_storage_field_storage_query(EntityFieldQuery $query) { - if ($query->age == FIELD_LOAD_CURRENT) { - $tablename_function = '_field_sql_storage_tablename'; - $id_key = 'entity_id'; - } - else { - $tablename_function = '_field_sql_storage_revision_tablename'; - $id_key = 'revision_id'; - } - $table_aliases = array(); - // Add tables for the fields used. - foreach ($query->fields as $key => $field) { - $tablename = $tablename_function($field); - // Every field needs a new table. - $table_alias = $tablename . $key; - $table_aliases[$key] = $table_alias; - if ($key) { - $select_query->join($tablename, $table_alias, "$table_alias.entity_type = $field_base_table.entity_type AND $table_alias.$id_key = $field_base_table.$id_key"); - } - else { - $select_query = db_select($tablename, $table_alias); - // Allow queries internal to the Field API to opt out of the access - // check, for situations where the query's results should not depend on - // the access grants for the current user. - if (!isset($query->tags['DANGEROUS_ACCESS_CHECK_OPT_OUT'])) { - $select_query->addTag('entity_field_access'); - } - $select_query->addMetaData('base_table', $tablename); - $select_query->fields($table_alias, array('entity_type', 'entity_id', 'revision_id', 'bundle')); - $field_base_table = $table_alias; - } - if ($field['cardinality'] != 1 || $field['translatable']) { - $select_query->distinct(); - } - } - - // Add field conditions. We need a fresh grouping cache. - drupal_static_reset('_field_sql_storage_query_field_conditions'); - _field_sql_storage_query_field_conditions($query, $select_query, $query->fieldConditions, $table_aliases, '_field_sql_storage_columnname'); - - // Add field meta conditions. - _field_sql_storage_query_field_conditions($query, $select_query, $query->fieldMetaConditions, $table_aliases, function ($field_name, $column) { return $column; }); - - if (isset($query->deleted)) { - $select_query->condition("$field_base_table.deleted", (int) $query->deleted); - } - - // Is there a need to sort the query by property? - $has_property_order = FALSE; - foreach ($query->order as $order) { - if ($order['type'] == 'property') { - $has_property_order = TRUE; - } - } - - $entity_type = FALSE; - $entity_base_table = FALSE; - if ($query->propertyConditions || $has_property_order) { - if (empty($query->entityConditions['entity_type']['value'])) { - throw new EntityFieldQueryException('Property conditions and orders must have an entity type defined.'); - } - $entity_type = $query->entityConditions['entity_type']['value']; - $entity_base_table = _field_sql_storage_query_join_entity($select_query, $entity_type, $field_base_table); - $query->entityConditions['entity_type']['operator'] = '='; - $query->joinPropertyData($select_query, $entity_type, $field_base_table, 'entity_id'); - $query->addPropertyConditions($select_query, $entity_type); - } - - // The entity base keys require joining the entity base table. - $entity_base_keys = array('langcode', 'uuid'); - foreach ($query->entityConditions as $key => $condition) { - $table = FALSE; - if (!in_array($key, $entity_base_keys)) { - $table = $field_base_table; - } - else { - // Join only if we did not already before. - if (empty($entity_base_table)) { - if (empty($query->entityConditions['entity_type']['value'])) { - throw new EntityFieldQueryException('Property conditions and orders must have an entity type defined.'); - } - $entity_type = $query->entityConditions['entity_type']['value']; - $entity_base_table = _field_sql_storage_query_join_entity($select_query, $entity_type, $field_base_table); - } - if (empty($entity_schema)) { - $entity_schema = drupal_get_schema($entity_base_table); - $entity_info = entity_get_info($entity_type); - } - $key = !empty($entity_info['entity keys'][$key]) ? $entity_info['entity keys'][$key] : $key; - if (!empty($entity_schema['fields'][$key])) { - $table = $entity_base_table; - } - } - // Apply the condition only if we have a valid table: entity conditions may - // refer to non existing columns. - if ($table) { - $query->addCondition($select_query, "$table.$key", $condition); - } - } - - // Order the query. - foreach ($query->order as $order) { - if ($order['type'] == 'entity') { - $key = $order['specifier']; - $select_query->orderBy("$field_base_table.$key", $order['direction']); - } - elseif ($order['type'] == 'field') { - $specifier = $order['specifier']; - $field = $specifier['field']; - $table_alias = $table_aliases[$specifier['index']]; - $sql_field = "$table_alias." . _field_sql_storage_columnname($field['field_name'], $specifier['column']); - $select_query->orderBy($sql_field, $order['direction']); - } - elseif ($order['type'] == 'property') { - $query->addPropertyOrderBy($select_query, $entity_type, $order); - } - } - - return $query->finishQuery($select_query, $id_key); -} - -/** - * Adds the base entity table to a field query object. - * - * @param SelectQuery $select_query - * A SelectQuery containing at least one table as specified by - * _field_sql_storage_tablename(). - * @param $entity_type - * The entity type for which the base table should be joined. - * @param $field_base_table - * Name of a table in $select_query. As only INNER JOINs are used, it does not - * matter which. - * - * @return - * The name of the entity base table that was joined in. - */ -function _field_sql_storage_query_join_entity(Select $select_query, $entity_type, $field_base_table) { - $entity_info = entity_get_info($entity_type); - $entity_base_table = $entity_info['base table']; - $entity_field = $entity_info['entity keys']['id']; - $select_query->join($entity_base_table, $entity_base_table, "$entity_base_table.$entity_field = $field_base_table.entity_id"); - return $entity_base_table; -} - -/** - * Adds field (meta) conditions to the given query objects respecting groupings. - * - * @param Drupal\Core\Entity\EntityFieldQuery $query - * The field query object to be processed. - * @param SelectQuery $select_query - * The SelectQuery that should get grouping conditions. - * @param condtions - * The conditions to be added. - * @param $table_aliases - * An associative array of table aliases keyed by field index. - * @param $column_callback - * A callback that should return the column name to be used for the field - * conditions. Accepts a field name and a field column name as parameters. - */ -function _field_sql_storage_query_field_conditions(EntityFieldQuery $query, Select $select_query, $conditions, $table_aliases, $column_callback) { - $groups = &drupal_static(__FUNCTION__, array()); - foreach ($conditions as $key => $condition) { - $table_alias = $table_aliases[$key]; - $field = $condition['field']; - // Add the specified condition. - $sql_field = "$table_alias." . $column_callback($field['field_name'], $condition['column']); - $query->addCondition($select_query, $sql_field, $condition); - // Add delta / langcode group conditions. - foreach (array('delta', 'langcode') as $column) { - if (isset($condition[$column . '_group'])) { - $group_name = $condition[$column . '_group']; - if (!isset($groups[$column][$group_name])) { - $groups[$column][$group_name] = $table_alias; - } - else { - $select_query->where("$table_alias.$column = " . $groups[$column][$group_name] . ".$column"); - } - } - } - } -} - -/** * Implements hook_field_storage_delete_revision(). * * This function actually deletes the data from the database. diff --git a/core/modules/field/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Condition.php b/core/modules/field/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Condition.php new file mode 100644 index 0000000..a0d549e --- /dev/null +++ b/core/modules/field/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Condition.php @@ -0,0 +1,66 @@ + array(), 'field' => array()); + + /** + * @param \Drupal\Core\Database\Query\ConditionInterface $sqlQuery + */ + public function compile($conditionContainer) { + // If this is not the top level condition group then the sql query is + // added to the $conditionContainer object by this function itself. The SQL query + // object is only necessary to pass to Query::getField() so that it can + // join tables as necessary. conditions need to be added to the $conditionContainer + // object to keep grouping. + $sqlQuery = $conditionContainer instanceof SelectInterface ? $conditionContainer : $conditionContainer->sqlQuery; + foreach ($this->conditions as $condition) { + if ($condition['field'] instanceOf ConditionInterface) { + $sqlCondition = new SqlCondition($condition['field']->getConjunction()); + // Add the SQL query to the object before calling this method again. + $sqlCondition->sqlQuery = $sqlQuery; + $condition['field']->compile($sqlCondition); + $sqlQuery->condition($sqlCondition); + } + else { + $field = Query::getField($sqlQuery, $this->tables, $condition['field']); + $this->translateCondition($condition); + $conditionContainer->condition($field, $condition['value'], $condition['operator']); + } + } + } + + protected function translateCondition(&$condition) { + switch ($condition['operator']) { + case 'STARTS_WITH': + $condition['value'] .= '%'; + $condition['operator'] = 'LIKE'; + break; + case 'CONTAINS': + $condition['value'] = '%' . $condition['value'] . '%'; + $condition['operator'] = 'LIKE'; + break; + case 'ENDS_WITH': + $condition['value'] = '%' . $condition['value']; + $condition['operator'] = 'LIKE'; + break; + } + } +} diff --git a/core/modules/field/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Query.php b/core/modules/field/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Query.php new file mode 100644 index 0000000..37d6712 --- /dev/null +++ b/core/modules/field/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Query.php @@ -0,0 +1,139 @@ + array(), 'field' => array()); + + /** + * @var \Drupal\Core\Database\Connection + */ + protected $connection; + + /** + * @param string $entity_type + * @param string $conjunction + * @param * @param \Symfony\Component\DependencyInjection\ContainerInterface $container + */ + public function __construct($entity_type, $conjunction, $connection) { + parent::__construct($entity_type, $conjunction); + $this->connection = $connection; + } + + public function conditionFactory($conjunction = 'AND') { + return new Condition($conjunction); + } + + /** + * @param $headers + * @return mixed + */ + public function tableSort(&$headers) { + // TODO: Implement tableSort() method. + } + + + /** + * @return mixed + */ + public function execute() { + $entity_info = entity_get_info($this->entityType); + if (!isset($entity_info['base table'])) { + throw new QueryException("No base table, nothing to query."); + } + $base_table = $entity_info['base table']; + $this->tables['entity'][$base_table] = 'base_table'; + $tables = array(); + if (isset($entity_info['data table'])) { + $tables[$entity_info['data table']] = drupal_get_schema($entity_info['data table']); + } + $tables[$base_table] = drupal_get_schema($base_table); + $sqlQuery = $this->connection->select($base_table, 'base_table', array('conjunction' => $this->condition->getConjunction())); + $id_field = $entity_info['entity keys']['id']; + $sqlQuery->addField('base_table', $id_field); + if ($this->accessCheck) { + $sqlQuery->addTag($this->entityType . '_access'); + } + $sqlQuery->addTag('entity_query'); + $sqlQuery->addTag('entity_query_' . $this->entityType); + $sqlQuery->addMetaData('entity_tables', $tables); + $sqlQuery->addMetaData('entity_id_field', $id_field); + $this->condition->compile($sqlQuery); + foreach ($this->sort as $property => $direction) { + $field = Query::getField($sqlQuery, $this->tables, $property); + $sqlQuery->orderBy($field, $direction); + } + if ($this->range) { + $sqlQuery->range($this->range['start'], $this->range['length']); + } + if ($this->count) { + return $sqlQuery->countQuery()->execute()->fetchField(); + } + return $sqlQuery->execute()->fetchCol(); + } + + static function getField(SelectInterface $sqlQuery, array &$tables, $property) { + $parts = explode('.', $property); + if (count($parts) == 1) { + $sql_column = $property; + $table = Query::ensureEntityTable($sqlQuery, $tables['entity'], $property); + } + else { + list($field_name, $column) = $parts; + $sql_column = _field_sql_storage_columnname($field_name, $column); + $table = Query::ensureFieldTable($sqlQuery, $tables['field'], $field_name); + } + return "$table.$sql_column"; + } + + + static function ensureEntityTable(SelectInterface $sqlQuery, array &$tables, $property) { + $entity_tables = $sqlQuery->getMetaData('entity_tables'); + if (!$entity_tables) { + throw new QueryException('Can not query entity properties without an entity type.'); + } + foreach ($entity_tables as $table => $schema) { + if (isset($schema['fields'][$property])) { + if (!isset($tables[$table])) { + $id_field = $sqlQuery->getMetaData('entity_id_field'); + $tables[$table] = $sqlQuery->join($table, NULL, "%alias.$id_field = base_table.$id_field"); + } + return $tables[$table]; + } + } + throw new QueryException(format_string('@property not found', array('@property' => $property))); + } + + static function ensureFieldTable(SelectInterface $sqlQuery, array &$tables, $field_name) { + if (!isset($tables[$field_name])) { + $field = field_info_field($field_name); + // This is field_purge_batch() passing in a field id. + if (!$field && substr($field_name, 0, 3) == 'id:') { + $field = field_info_field_by_id(substr($field_name, 3)); + } + if (!$field) { + throw new QueryException(format_string('field @field_name not found', array('@field_name' => $field_name))); + } + $table = _field_sql_storage_tablename($field); + $id_field = $sqlQuery->getMetaData('entity_id_field'); + $tables[$field_name] = $sqlQuery->join($table, NULL, "%alias.entity_id = base_table.$id_field"); + } + return $tables[$field_name]; + } + +} diff --git a/core/modules/field/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/QueryFactory.php b/core/modules/field/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/QueryFactory.php new file mode 100644 index 0000000..25e0705 --- /dev/null +++ b/core/modules/field/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/QueryFactory.php @@ -0,0 +1,21 @@ +connection = $connection; + } + + function get($entity_type, $conjunction) { + return new Query($entity_type, $conjunction, $this->connection); + } +} diff --git a/core/modules/field/modules/field_sql_storage/lib/Drupal/field_sql_storage/FieldSqlStorageBundle.php b/core/modules/field/modules/field_sql_storage/lib/Drupal/field_sql_storage/FieldSqlStorageBundle.php new file mode 100644 index 0000000..23c75e1 --- /dev/null +++ b/core/modules/field/modules/field_sql_storage/lib/Drupal/field_sql_storage/FieldSqlStorageBundle.php @@ -0,0 +1,17 @@ +register('entity.query.field_sql_storage', 'Drupal\field_sql_storage\Entity\QueryFactory') + ->addArgument(new Reference('database')); + } + +} diff --git a/core/modules/field/modules/options/options.module b/core/modules/field/modules/options/options.module index 31652e3..08fbf27 100644 --- a/core/modules/field/modules/options/options.module +++ b/core/modules/field/modules/options/options.module @@ -6,7 +6,7 @@ */ use Drupal\field\FieldUpdateForbiddenException; -use Drupal\Core\Entity\EntityFieldQuery; +use Drupal\Core\Entity\Query\QueryFactory; /** * Implements hook_help(). @@ -384,12 +384,19 @@ function options_field_update_forbid($field, $prior_field, $has_data) { */ function _options_values_in_use($field, $values) { if ($values) { - $query = new EntityFieldQuery(); - $found = $query - ->fieldCondition($field['field_name'], 'value', $values) - ->range(0, 1) - ->execute(); - return !empty($found); + $field = field_info_field_by_id($field['id']); + $factory = drupal_container()->get('entity.query'); + foreach($field['bundles'] as $entity_type => $bundle) { + $result = $factory->get($entity_type) + ->condition($field['field_name'] .'.value', $values) + ->count() + ->skipAccessCheck() + ->range(0, 1) + ->execute(); + if ($result) { + return TRUE; + } + } } return FALSE; diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc index cbc2246..0f4e932 100644 --- a/core/modules/file/file.field.inc +++ b/core/modules/file/file.field.inc @@ -930,6 +930,30 @@ function file_field_formatter_view($entity_type, $entity, $field, $instance, $la } /** + * Determine whether a field references files stored in {file_managed}. + * + * @param array $field + * A field array. + * + * @return + * The field column if the field references {file_managed}.fid, typically + * fid, FALSE if it doesn't. + */ + +function file_field_find_file_reference_column($field) { + foreach ($field['foreign keys'] as $data) { + if ($data['table'] == 'file_managed') { + foreach ($data['columns'] as $field_column => $column) { + if ($column == 'fid') { + return $field_column; + } + } + } + } + return FALSE; +} + +/** * Returns HTML for a file attachments table. * * @param $variables diff --git a/core/modules/file/file.module b/core/modules/file/file.module index 0f6cb64..9b79e0f 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -5,7 +5,6 @@ * Defines a "managed_file" Form API field and a "file" field for Field module. */ -use Drupal\Core\Entity\EntityFieldQuery; use Drupal\file\File; use Drupal\Core\Template\Attribute; use Symfony\Component\HttpFoundation\JsonResponse; @@ -116,7 +115,7 @@ function file_entity_info() { * @see hook_file_load() * @see file_load() * @see entity_load() - * @see Drupal\Core\Entity\EntityFieldQuery + * @see Drupal\Core\Entity\Query\EntityQueryInterface */ function file_load_multiple(array $fids = NULL) { return entity_load_multiple('file', $fids); @@ -780,7 +779,7 @@ function file_file_download($uri, $field_type = 'file') { // an image preview on a node/add form) in which case, allow download by the // file's owner. if (empty($references) && ($file->status == FILE_STATUS_PERMANENT || $file->uid != $user->uid)) { - return; + return; } // Default to allow access. @@ -792,34 +791,13 @@ function file_file_download($uri, $field_type = 'file') { // and no reference denied access, access is granted as well. If at least one // reference denied access, access is denied. foreach ($references as $field_name => $field_references) { - foreach ($field_references as $entity_type => $type_references) { - foreach ($type_references as $id => $reference) { - // Try to load $entity and $field. - $entity = entity_load($entity_type, $id); + foreach ($field_references as $entity_type => $entities) { + foreach ($entities as $entity) { $field = field_info_field($field_name); - - // Load the field item that references the file. - $match = FALSE; - if ($entity) { - // Load all fields items for that entity. - $field_items = field_get_items($entity_type, $entity, $field_name); - - // Try to find the field item that references the given file. - foreach ($field_items as $item) { - if ($file->fid == $item['fid']) { - $match = TRUE; - break; - } - } - } - - // Check that $entity and $field were loaded successfully, a field - // item that references the file exists and check if access to that - // field is not disallowed. If any of these checks fail, stop checking - // access for this reference. - if (empty($entity) || empty($field) || !$match || !field_access('view', $field, $entity_type, $entity)) { + // Check if access to this field is not disallowed. + if (!field_access('view', $field, $entity_type, $entity)) { $denied = TRUE; - break; + continue; } // Invoke hook and collect grants/denies for download access. @@ -1667,27 +1645,83 @@ function file_icon_map(File $file) { * FIELD_LOAD_CURRENT to retrieve references only in the current revisions. * @param $field_type * (optional) The name of a field type. If given, limits the reference check - * to fields of the given type. + * to fields of the given type. If both $field and $field_type is given but + * $field is not the same type as $field_type, an empty array will be + * returned. * * @return - * An integer value. + * A multidimensional array. The keys are field_name, entity_type, + * entity_id and the value is an entity referencing this file. */ function file_get_file_references(File $file, $field = NULL, $age = FIELD_LOAD_REVISION, $field_type = 'file') { - $references = drupal_static(__FUNCTION__, array()); - $fields = isset($field) ? array($field['field_name'] => $field) : field_info_fields(); - - foreach ($fields as $field_name => $file_field) { - if ((empty($field_type) || $file_field['type'] == $field_type) && !isset($references[$field_name])) { - // Get each time this file is used within a field. - $query = new EntityFieldQuery(); - $query - ->fieldCondition($file_field, 'fid', $file->fid) - ->age($age); - $references[$field_name] = $query->execute(); + $references = &drupal_static(__FUNCTION__, array()); + $field_columns = &drupal_static(__FUNCTION__ . ':field_columns', array()); + + // Fill the static cache, disregard $field and $field_type for now. + if (!isset($references[$file->fid][$age])) { + $references[$file->fid][$age] = array(); + $usage_list = file_usage_list($file); + $file_usage_list = isset($usage_list['file']) ? $usage_list['file'] : array(); + foreach ($file_usage_list as $entity_type => $entity_ids) { + $entity_info = entity_get_info($entity_type); + // The usage table contains usage of every revision. If we are looking + // for every revision or the entity does not support revisions then + // every usage is already a match. + $match_entity_type = $age == FIELD_LOAD_REVISION || !isset($entity_info['entity keys']['revision']); + $entities = entity_load_multiple($entity_type, $entity_ids); + foreach ($entities as $entity) { + $bundle = $entity->bundle(); + // We need to find file fields for this entity type and bundle. + if (!isset($file_fields[$entity_type][$bundle])) { + $file_fields[$entity_type][$bundle] = array(); + // This contains the possible field names. + $instances = field_info_instances($entity_type, $bundle); + foreach ($instances as $field_name => $instance) { + $current_field = field_info_field($field_name); + // If this is the first time this field type is seen, check + // whether it references files. + if (!isset($field_columns[$current_field['type']])) { + $field_columns[$current_field['type']] = file_field_find_file_reference_column($current_field); + } + // If the field type does reference files then record it. + if ($field_columns[$current_field['type']]) { + $file_fields[$entity_type][$bundle][$field_name] = $field_columns[$current_field['type']]; + } + } + } + foreach ($file_fields[$entity_type][$bundle] as $field_name => $field_column) { + $match = $match_entity_type; + // If we didn't match yet then iterate over the field items to find + // the referenced file. This will fail if the usage checked is in a + // non-current revision because field items are from the current + // revision. + if (!$match && ($field_items = field_get_items($entity_type, $entity, $field_name))) { + foreach ($field_items as $item) { + if ($file->fid == $item[$field_column]) { + $match = TRUE; + break; + } + } + } + if ($match) { + $references[$file->fid][$age][$field_name][$entity_type][$entity->id()] = $entity; + } + } + } } } - - return isset($field) ? $references[$field['field_name']] : array_filter($references); + $return = $references[$file->fid][$age]; + // Filter the static cache down to the requested entries. The usual static + // cache is very small so this will be very fast. + if ($field || $field_type) { + foreach ($return as $field_name => $data) { + $current_field = field_info_field($field_name); + if (($field_type && $current_field['type'] != $field_type) || ($field && $field['id'] != $current_field['id'])) { + unset($return[$field_name]); + } + } + } + return $return; } /** diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 2dd1b57..0c9c353 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -1044,7 +1044,7 @@ function node_invoke($node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) { * An array of node entities indexed by nid. * * @see entity_load_multiple() - * @see Drupal\Core\Entity\EntityFieldQuery + * @see Drupal\Core\Entity\Query\EntityQueryInterface */ function node_load_multiple(array $nids = NULL, $reset = FALSE) { return entity_load_multiple('node', $nids, $reset); @@ -3264,34 +3264,6 @@ function node_access_view_all_nodes($account = NULL) { * 'update' and 'delete'). */ function node_query_node_access_alter(AlterableInterface $query) { - _node_query_node_access_alter($query, 'node'); -} - -/** - * Implements hook_query_TAG_alter(). - * - * This function implements the same functionality as - * node_query_node_access_alter() for the SQL field storage engine. Node access - * conditions are added for field values belonging to nodes only. - */ -function node_query_entity_field_access_alter(AlterableInterface $query) { - _node_query_node_access_alter($query, 'entity'); -} - -/** - * Helper for node access functions. - * - * @param $query - * The query to add conditions to. - * @param $type - * Either 'node' or 'entity' depending on what sort of query it is. See - * node_query_node_access_alter() and node_query_entity_field_access_alter() - * for more. - * - * @see node_query_node_access_alter() - * @see node_query_entity_field_access_alter() - */ -function _node_query_node_access_alter($query, $type) { global $user; // Read meta-data from query, if provided. @@ -3340,27 +3312,6 @@ function _node_query_node_access_alter($query, $type) { // the node_access table. $grants = node_access_grants($op, $account); - if ($type == 'entity') { - // The original query looked something like: - // @code - // SELECT nid FROM sometable s - // INNER JOIN node_access na ON na.nid = s.nid - // WHERE ($node_access_conditions) - // @endcode - // - // Our query will look like: - // @code - // SELECT entity_type, entity_id - // FROM field_data_something s - // LEFT JOIN node_access na ON s.entity_id = na.nid - // WHERE (entity_type = 'node' AND $node_access_conditions) OR (entity_type <> 'node') - // @endcode - // - // So instead of directly adding to the query object, we need to collect - // all of the node access conditions in a separate db_and() object and - // then add it to the query at the end. - $node_conditions = db_and(); - } foreach ($tables as $nalias => $tableinfo) { $table = $tableinfo['table']; if (!($table instanceof SelectInterface) && $table == $base_table) { @@ -3387,37 +3338,11 @@ function _node_query_node_access_alter($query, $type) { $subquery->condition('na.grant_' . $op, 1, '>='); $field = 'nid'; // Now handle entities. - if ($type == 'entity') { - // Set a common alias for entities. - $base_alias = $nalias; - $field = 'entity_id'; - } $subquery->where("$nalias.$field = na.nid"); - // For an entity query, attach the subquery to entity conditions. - if ($type == 'entity') { - $node_conditions->exists($subquery); - } - // Otherwise attach it to the node query itself. - else { - $query->exists($subquery); - } + $query->exists($subquery); } } - - if ($type == 'entity' && count($subquery->conditions())) { - // All the node access conditions are only for field values belonging to - // nodes. - $node_conditions->condition("$base_alias.entity_type", 'node'); - $or = db_or(); - $or->condition($node_conditions); - // If the field value belongs to a non-node entity type then this function - // does not do anything with it. - $or->condition("$base_alias.entity_type", 'node', '<>'); - // Add the compiled set of rules to the query. - $query->condition($or); - } - } /** diff --git a/core/modules/node/tests/modules/node_access_test/node_access_test.module b/core/modules/node/tests/modules/node_access_test/node_access_test.module index a503217..2b6ed07 100644 --- a/core/modules/node/tests/modules/node_access_test/node_access_test.module +++ b/core/modules/node/tests/modules/node_access_test/node_access_test.module @@ -7,8 +7,6 @@ * a special 'node test view' permission. */ -use Drupal\Core\Entity\EntityFieldQuery; - use Drupal\node\Node; /** @@ -152,12 +150,13 @@ function node_access_test_page() { function node_access_entity_test_page() { $output = ''; try { - $query = new EntityFieldQuery; - $result = $query->fieldCondition('body', 'value', 'A', 'STARTS_WITH')->execute(); - if (!empty($result['node'])) { - $output .= '

Yes, ' . count($result['node']) . ' nodes

'; + $result = drupal_container()->get('entity.query')->get('node') + ->condition('body.value', 'A', 'STARTS_WITH') + ->execute(); + if (!empty($result)) { + $output .= '

Yes, ' . count($result) . ' nodes

'; $output .= ''; diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldQueryTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldQueryTest.php deleted file mode 100644 index 305c298..0000000 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldQueryTest.php +++ /dev/null @@ -1,1595 +0,0 @@ - 'Entity query', - 'description' => 'Test the EntityFieldQuery class.', - 'group' => 'Entity API', - ); - } - - function setUp() { - parent::setUp(); - - field_test_create_bundle('bundle1'); - field_test_create_bundle('bundle2'); - field_test_create_bundle('test_bundle'); - field_test_create_bundle('test_entity_bundle'); - - $instances = array(); - $this->fields = array(); - $this->field_names[0] = $field_name = drupal_strtolower($this->randomName() . '_field_name'); - $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 4); - $field = field_create_field($field); - $this->fields[0] = $field; - $instance = array( - 'field_name' => $field_name, - 'entity_type' => '', - 'bundle' => '', - 'label' => $this->randomName() . '_label', - 'description' => $this->randomName() . '_description', - 'weight' => mt_rand(0, 127), - 'settings' => array( - 'test_instance_setting' => $this->randomName(), - ), - 'widget' => array( - 'type' => 'test_field_widget', - 'label' => 'Test Field', - 'settings' => array( - 'test_widget_setting' => $this->randomName(), - ) - ) - ); - - $instances[0] = $instance; - - // Add an instance to that bundle. - $instances[0]['bundle'] = 'bundle1'; - $instances[0]['entity_type'] = 'test_entity_bundle_key'; - field_create_instance($instances[0]); - $instances[0]['bundle'] = 'bundle2'; - field_create_instance($instances[0]); - $instances[0]['bundle'] = $instances[0]['entity_type'] = 'test_entity_bundle'; - field_create_instance($instances[0]); - - $this->field_names[1] = $field_name = drupal_strtolower($this->randomName() . '_field_name'); - $field = array('field_name' => $field_name, 'type' => 'shape', 'cardinality' => 4); - $field = field_create_field($field); - $this->fields[1] = $field; - $instance = array( - 'field_name' => $field_name, - 'entity_type' => '', - 'bundle' => '', - 'label' => $this->randomName() . '_label', - 'description' => $this->randomName() . '_description', - 'weight' => mt_rand(0, 127), - 'settings' => array( - 'test_instance_setting' => $this->randomName(), - ), - 'widget' => array( - 'type' => 'test_field_widget', - 'label' => 'Test Field', - 'settings' => array( - 'test_widget_setting' => $this->randomName(), - ) - ) - ); - - $instances[1] = $instance; - - // Add a field instance to the bundles. - $instances[1]['bundle'] = 'bundle1'; - $instances[1]['entity_type'] = 'test_entity_bundle_key'; - field_create_instance($instances[1]); - $instances[1]['bundle'] = $instances[1]['entity_type'] = 'test_entity_bundle'; - field_create_instance($instances[1]); - - $this->instances = $instances; - // Write entity base table if there is one. - $entities = array(); - - // Create entities which have a 'bundle key' defined. - for ($i = 1; $i < 7; $i++) { - $entity = entity_create('test_entity_bundle_key', array()); - $entity->ftid = $i; - $entity->ftvid = $i; - $entity->fttype = ($i < 5) ? 'bundle1' : 'bundle2'; - - $entity->{$this->field_names[0]}[LANGUAGE_NOT_SPECIFIED][0]['value'] = $i; - $entity->enforceIsNew(); - $entity->save(); - } - - $entity = entity_create('test_entity_bundle', array('ftid' => 5)); - $entity->{$this->field_names[1]}[LANGUAGE_NOT_SPECIFIED][0]['shape'] = 'square'; - $entity->{$this->field_names[1]}[LANGUAGE_NOT_SPECIFIED][0]['color'] = 'red'; - $entity->{$this->field_names[1]}[LANGUAGE_NOT_SPECIFIED][1]['shape'] = 'circle'; - $entity->{$this->field_names[1]}[LANGUAGE_NOT_SPECIFIED][1]['color'] = 'blue'; - $entity->enforceIsNew(); - $entity->save(); - - $instances[2] = $instance; - $instances[2]['bundle'] = 'test_bundle'; - $instances[2]['field_name'] = $this->field_names[0]; - $instances[2]['entity_type'] = 'test_entity'; - field_create_instance($instances[2]); - - // Create entities with support for revisions. - for ($i = 1; $i < 5; $i++) { - $entity = field_test_create_entity($i, $i); - $entity->{$this->field_names[0]}[LANGUAGE_NOT_SPECIFIED][0]['value'] = $i; - $entity->save(); - } - - // Add two revisions to an entity. - for ($i = 100; $i < 102; $i++) { - $entity = field_test_create_entity(4); - $entity->ftvid = $i; - $entity->{$this->field_names[0]}[LANGUAGE_NOT_SPECIFIED][0]['value'] = $i; - - drupal_write_record('test_entity', $entity, 'ftid'); - drupal_write_record('test_entity_revision', $entity); - - db_update('test_entity') - ->fields(array('ftvid' => $entity->ftvid)) - ->condition('ftid', $entity->ftid) - ->execute(); - - field_attach_update('test_entity', $entity); - } - } - - /** - * Tests EntityFieldQuery. - */ - function testEntityFieldQuery() { - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle') - ->entityCondition('entity_id', '5'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle', 5), - ), 'Test query on an entity type with a generated bundle.'); - - // Test entity_type condition. - $query = new EntityFieldQuery(); - $query->entityCondition('entity_type', 'test_entity_bundle_key'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test entity entity_type condition.'); - - // Test entity_id condition. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->entityCondition('entity_id', '3'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 3), - ), 'Test entity entity_id condition.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('ftid', '3'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 3), - ), 'Test entity entity_id condition and entity_id property condition.'); - - // Test bundle condition. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->entityCondition('bundle', 'bundle1'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - ), 'Test entity bundle condition: bundle1.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->entityCondition('bundle', 'bundle2'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test entity bundle condition: bundle2.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('fttype', 'bundle2'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test entity bundle condition and bundle property condition.'); - - // Test revision_id condition. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity') - ->entityCondition('revision_id', '3'); - $this->assertEntityFieldQuery($query, array( - array('test_entity', 3), - ), 'Test entity revision_id condition.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity') - ->propertyCondition('ftvid', '3'); - $this->assertEntityFieldQuery($query, array( - array('test_entity', 3), - ), 'Test entity revision_id condition and revision_id property condition.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity') - ->fieldCondition($this->fields[0], 'value', 100, '>=') - ->age(FIELD_LOAD_REVISION); - $this->assertEntityFieldQuery($query, array( - array('test_entity', 100), - array('test_entity', 101), - ), 'Test revision age.'); - - // Test that fields attached to the non-revision supporting entity - // 'test_entity_bundle_key' are reachable in FIELD_LOAD_REVISION. - $query = new EntityFieldQuery(); - $query - ->fieldCondition($this->fields[0], 'value', 100, '<') - ->age(FIELD_LOAD_REVISION); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - array('test_entity', 1), - array('test_entity', 2), - array('test_entity', 3), - array('test_entity', 4), - ), 'Test that fields are reachable from FIELD_LOAD_REVISION even for non-revision entities.'); - - // Test entity sort by entity_id. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->entityOrderBy('entity_id', 'ASC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test sort entity entity_id in ascending order.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->entityOrderBy('entity_id', 'DESC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 1), - ), 'Test sort entity entity_id in descending order.', TRUE); - - // Test entity sort by entity_id, with a field condition. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->entityOrderBy('entity_id', 'ASC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test sort entity entity_id in ascending order, with a field condition.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->propertyOrderBy('ftid', 'DESC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 1), - ), 'Test sort entity entity_id property in descending order, with a field condition.', TRUE); - - // Test property sort by entity id. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyOrderBy('ftid', 'ASC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test sort entity entity_id property in ascending order.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyOrderBy('ftid', 'DESC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 1), - ), 'Test sort entity entity_id property in descending order.', TRUE); - - // Test property sort by entity id, with a field condition. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->propertyOrderBy('ftid', 'ASC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test sort entity entity_id property in ascending order, with a field condition.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->propertyOrderBy('ftid', 'DESC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 1), - ), 'Test sort entity entity_id property in descending order, with a field condition.', TRUE); - - // Test entity sort by bundle. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->entityOrderBy('bundle', 'ASC') - ->propertyOrderBy('ftid', 'DESC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 5), - ), 'Test sort entity bundle in ascending order, property in descending order.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->entityOrderBy('bundle', 'DESC') - ->propertyOrderBy('ftid', 'ASC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - ), 'Test sort entity bundle in descending order, property in ascending order.', TRUE); - - // Test entity sort by bundle, with a field condition. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->entityOrderBy('bundle', 'ASC') - ->propertyOrderBy('ftid', 'DESC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 5), - ), 'Test sort entity bundle in ascending order, property in descending order, with a field condition.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->entityOrderBy('bundle', 'DESC') - ->propertyOrderBy('ftid', 'ASC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - ), 'Test sort entity bundle in descending order, property in ascending order, with a field condition.', TRUE); - - // Test entity sort by bundle, field. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->entityOrderBy('bundle', 'ASC') - ->fieldOrderBy($this->fields[0], 'value', 'DESC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 5), - ), 'Test sort entity bundle in ascending order, field in descending order.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->entityOrderBy('bundle', 'DESC') - ->fieldOrderBy($this->fields[0], 'value', 'ASC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - ), 'Test sort entity bundle in descending order, field in ascending order.', TRUE); - - // Test entity sort by revision_id. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity') - ->entityOrderBy('revision_id', 'ASC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity', 1), - array('test_entity', 2), - array('test_entity', 3), - array('test_entity', 4), - ), 'Test sort entity revision_id in ascending order.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity') - ->entityOrderBy('revision_id', 'DESC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity', 4), - array('test_entity', 3), - array('test_entity', 2), - array('test_entity', 1), - ), 'Test sort entity revision_id in descending order.', TRUE); - - // Test entity sort by revision_id, with a field condition. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->entityOrderBy('revision_id', 'ASC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity', 1), - array('test_entity', 2), - array('test_entity', 3), - array('test_entity', 4), - ), 'Test sort entity revision_id in ascending order, with a field condition.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->entityOrderBy('revision_id', 'DESC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity', 4), - array('test_entity', 3), - array('test_entity', 2), - array('test_entity', 1), - ), 'Test sort entity revision_id in descending order, with a field condition.', TRUE); - - // Test property sort by revision_id. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity') - ->propertyOrderBy('ftvid', 'ASC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity', 1), - array('test_entity', 2), - array('test_entity', 3), - array('test_entity', 4), - ), 'Test sort entity revision_id property in ascending order.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity') - ->propertyOrderBy('ftvid', 'DESC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity', 4), - array('test_entity', 3), - array('test_entity', 2), - array('test_entity', 1), - ), 'Test sort entity revision_id property in descending order.', TRUE); - - // Test property sort by revision_id, with a field condition. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->propertyOrderBy('ftvid', 'ASC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity', 1), - array('test_entity', 2), - array('test_entity', 3), - array('test_entity', 4), - ), 'Test sort entity revision_id property in ascending order, with a field condition.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->propertyOrderBy('ftvid', 'DESC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity', 4), - array('test_entity', 3), - array('test_entity', 2), - array('test_entity', 1), - ), 'Test sort entity revision_id property in descending order, with a field condition.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldOrderBy($this->fields[0], 'value', 'ASC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test sort field in ascending order without field condition.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldOrderBy($this->fields[0], 'value', 'DESC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 1), - ), 'Test sort field in descending order without field condition.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->fieldOrderBy($this->fields[0], 'value', 'ASC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test sort field in ascending order.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->fieldOrderBy($this->fields[0], 'value', 'DESC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 1), - ), 'Test sort field in descending order.', TRUE); - - // Test "in" operation with entity entity_type condition and entity_id - // property condition. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('ftid', array(1, 3, 4), 'IN'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - ), 'Test "in" operation with entity entity_type condition and entity_id property condition.'); - - // Test "in" operation with entity entity_type condition and entity_id - // property condition. Sort in descending order by entity_id. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('ftid', array(1, 3, 4), 'IN') - ->propertyOrderBy('ftid', 'DESC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 1), - ), 'Test "in" operation with entity entity_type condition and entity_id property condition. Sort entity_id in descending order.', TRUE); - - // Test query count - $query = new EntityFieldQuery(); - $query_count = $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->count() - ->execute(); - $this->assertEqual($query_count, 6, 'Test query count on entity condition.'); - - $query = new EntityFieldQuery(); - $query_count = $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('ftid', '1') - ->count() - ->execute(); - $this->assertEqual($query_count, 1, 'Test query count on entity and property condition.'); - - $query = new EntityFieldQuery(); - $query_count = $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('ftid', '4', '>') - ->count() - ->execute(); - $this->assertEqual($query_count, 2, 'Test query count on entity and property condition with operator.'); - - $query = new EntityFieldQuery(); - $query_count = $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 3, '=') - ->count() - ->execute(); - $this->assertEqual($query_count, 1, 'Test query count on field condition.'); - - // First, test without options. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('fttype', 'und', 'CONTAINS'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test the "contains" operation on a property.'); - - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[1], 'shape', 'uar', 'CONTAINS'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle', 5), - ), 'Test the "contains" operation on a field.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('ftid', 1, '='); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - ), 'Test the "equal to" operation on a property.'); - - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[0], 'value', 3, '='); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 3), - array('test_entity', 3), - ), 'Test the "equal to" operation on a field.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('ftid', 3, '<>'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test the "not equal to" operation on a property.'); - - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[0], 'value', 3, '<>'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - array('test_entity', 1), - array('test_entity', 2), - array('test_entity', 4), - ), 'Test the "not equal to" operation on a field.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('ftid', 2, '<'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - ), 'Test the "less than" operation on a property.'); - - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[0], 'value', 2, '<'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity', 1), - ), 'Test the "less than" operation on a field.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('ftid', 2, '<='); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - ), 'Test the "less than or equal to" operation on a property.'); - - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[0], 'value', 2, '<='); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity', 1), - array('test_entity', 2), - ), 'Test the "less than or equal to" operation on a field.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('ftid', 4, '>'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test the "greater than" operation on a property.'); - - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[0], 'value', 2, '>'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - array('test_entity', 3), - array('test_entity', 4), - ), 'Test the "greater than" operation on a field.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('ftid', 4, '>='); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test the "greater than or equal to" operation on a property.'); - - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[0], 'value', 3, '>='); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - array('test_entity', 3), - array('test_entity', 4), - ), 'Test the "greater than or equal to" operation on a field.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('ftid', array(3, 4), 'NOT IN'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test the "not in" operation on a property.'); - - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[0], 'value', array(3, 4, 100, 101), 'NOT IN'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - array('test_entity', 1), - array('test_entity', 2), - ), 'Test the "not in" operation on a field.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('ftid', array(3, 4), 'IN'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - ), 'Test the "in" operation on a property.'); - - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[0], 'value', array(2, 3), 'IN'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity', 2), - array('test_entity', 3), - ), 'Test the "in" operation on a field.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('ftid', array(1, 3), 'BETWEEN'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - ), 'Test the "between" operation on a property.'); - - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[0], 'value', array(1, 3), 'BETWEEN'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity', 1), - array('test_entity', 2), - array('test_entity', 3), - ), 'Test the "between" operation on a field.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('fttype', 'bun', 'STARTS_WITH'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test the "starts_with" operation on a property.'); - - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[1], 'shape', 'squ', 'STARTS_WITH'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle', 5), - ), 'Test the "starts_with" operation on a field.'); - - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[0], 'value', 3); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 3), - array('test_entity', 3), - ), 'Test omission of an operator with a single item.'); - - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[0], 'value', array(2, 3)); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity', 2), - array('test_entity', 3), - ), 'Test omission of an operator with multiple items.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyCondition('ftid', 1, '>') - ->fieldCondition($this->fields[0], 'value', 4, '<'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - ), 'Test entity, property and field conditions.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->entityCondition('bundle', 'bundle', 'STARTS_WITH') - ->propertyCondition('ftid', 4) - ->fieldCondition($this->fields[0], 'value', 4); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 4), - ), 'Test entity condition with "starts_with" operation, and property and field conditions.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyOrderBy('ftid', 'ASC') - ->range(0, 2); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - ), 'Test limit on a property.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>=') - ->fieldOrderBy($this->fields[0], 'value', 'ASC') - ->range(0, 2); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - ), 'Test limit on a field.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyOrderBy('ftid', 'ASC') - ->range(4, 6); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test offset on a property.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->fieldOrderBy($this->fields[0], 'value', 'ASC') - ->range(2, 4); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test offset on a field.', TRUE); - - for ($i = 6; $i < 10; $i++) { - $entity = entity_create('test_entity_bundle', array('ftid' => $i)); - $entity->{$this->field_names[0]}[LANGUAGE_NOT_SPECIFIED][0]['value'] = $i - 5; - $entity->enforceIsNew(); - $entity->save(); - } - - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[0], 'value', 2, '>'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - array('test_entity', 3), - array('test_entity', 4), - array('test_entity_bundle', 8), - array('test_entity_bundle', 9), - ), 'Select a field across multiple entities.'); - - $query = new EntityFieldQuery(); - $query - ->fieldCondition($this->fields[1], 'shape', 'square') - ->fieldCondition($this->fields[1], 'color', 'blue'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle', 5), - ), 'Test without a delta group.'); - - $query = new EntityFieldQuery(); - $query - ->fieldCondition($this->fields[1], 'shape', 'square', '=', 'group') - ->fieldCondition($this->fields[1], 'color', 'blue', '=', 'group'); - $this->assertEntityFieldQuery($query, array(), 'Test with a delta group.'); - - // Test query on a deleted field. - field_attach_delete_bundle('test_entity_bundle_key', 'bundle1'); - field_attach_delete_bundle('test_entity', 'test_bundle'); - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[0], 'value', '3'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle', 8), - ), 'Test query on a field after deleting field from some entities.'); - - field_attach_delete_bundle('test_entity_bundle', 'test_entity_bundle'); - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[0], 'value', '3'); - $this->assertEntityFieldQuery($query, array(), 'Test query on a field after deleting field from all entities.'); - - $query = new EntityFieldQuery(); - $query - ->fieldCondition($this->fields[0], 'value', '3') - ->deleted(TRUE); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 3), - array('test_entity_bundle', 8), - array('test_entity', 3), - ), 'Test query on a deleted field with deleted option set to TRUE.'); - - $pass = FALSE; - $query = new EntityFieldQuery(); - try { - $query->execute(); - } - catch (EntityFieldQueryException $exception) { - $pass = ($exception->getMessage() == t('For this query an entity type must be specified.')); - } - $this->assertTrue($pass, 'Cannot query the universe.'); - } - - /** - * Tests querying translatable fields. - */ - function testEntityFieldQueryTranslatable() { - - // Make a test field translatable AND cardinality one. - $this->fields[0]['translatable'] = TRUE; - $this->fields[0]['cardinality'] = 1; - field_update_field($this->fields[0]); - field_test_entity_info_translatable('test_entity', TRUE); - - // Create more items with different languages. - $entity = entity_load('test_entity', 1); - - // Set fields in two languages with one field value. - foreach (array(LANGUAGE_NOT_SPECIFIED, 'en') as $langcode) { - $entity->{$this->field_names[0]}[$langcode][0]['value'] = 1234; - } - - field_attach_update('test_entity', $entity); - - // Look up number of results when querying a single entity with multilingual - // field values. - $query = new EntityFieldQuery(); - $query_count = $query - ->entityCondition('entity_type', 'test_entity') - ->entityCondition('bundle', 'test_bundle') - ->entityCondition('entity_id', '1') - ->fieldCondition($this->fields[0]) - ->count() - ->execute(); - - $this->assertEqual($query_count, 1, 'Count on translatable cardinality one field is correct.'); - } - - - /** - * Tests field meta conditions. - */ - function testEntityFieldQueryMetaConditions() { - // Make a test field translatable. - $this->fields[0]['translatable'] = TRUE; - field_update_field($this->fields[0]); - field_test_entity_info_translatable('test_entity', TRUE); - - // Create more items with different languages. - $entity = entity_load('test_entity', 1); - $j = 0; - - foreach (array(LANGUAGE_NOT_SPECIFIED, 'en') as $langcode) { - for ($i = 0; $i < 4; $i++) { - $entity->{$this->field_names[0]}[$langcode][$i]['value'] = $i + $j; - } - $j += 4; - } - - field_attach_update('test_entity', $entity); - - // Test delta field meta condition. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity', '=') - ->fieldDeltaCondition($this->fields[0], 0, '>'); - $this->assertEntityFieldQuery($query, array( - array('test_entity', 1), - ), 'Test with a delta meta condition.'); - - // Test language field meta condition. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity', '=') - ->fieldLanguageCondition($this->fields[0], LANGUAGE_NOT_SPECIFIED, '<>'); - $this->assertEntityFieldQuery($query, array( - array('test_entity', 1), - ), 'Test with a language meta condition.'); - - // Test delta grouping. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity', '=') - ->fieldCondition($this->fields[0], 'value', 0, '=', 'group') - ->fieldDeltaCondition($this->fields[0], 1, '<', 'group'); - $this->assertEntityFieldQuery($query, array( - array('test_entity', 1), - ), 'Test with a grouped delta meta condition.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity', '=') - ->fieldCondition($this->fields[0], 'value', 0, '=', 'group') - ->fieldDeltaCondition($this->fields[0], 1, '>=', 'group'); - $this->assertEntityFieldQuery($query, array(), 'Test with a grouped delta meta condition (empty result set).'); - - // Test language grouping. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity', '=') - ->fieldCondition($this->fields[0], 'value', 0, '=', NULL, 'group') - ->fieldLanguageCondition($this->fields[0], 'en', '<>', NULL, 'group'); - $this->assertEntityFieldQuery($query, array( - array('test_entity', 1), - ), 'Test with a grouped language meta condition.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity', '=') - ->fieldCondition($this->fields[0], 'value', 0, '=', NULL, 'group') - ->fieldLanguageCondition($this->fields[0], LANGUAGE_NOT_SPECIFIED, '<>', NULL, 'group'); - $this->assertEntityFieldQuery($query, array(), 'Test with a grouped language meta condition (empty result set).'); - - // Test delta and language grouping. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity', '=') - ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language') - ->fieldDeltaCondition($this->fields[0], 1, '<', 'delta', 'language') - ->fieldLanguageCondition($this->fields[0], 'en', '<>', 'delta', 'language'); - $this->assertEntityFieldQuery($query, array( - array('test_entity', 1), - ), 'Test with a grouped delta + language meta condition.'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity', '=') - ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language') - ->fieldDeltaCondition($this->fields[0], 1, '>=', 'delta', 'language') - ->fieldLanguageCondition($this->fields[0], 'en', '<>', 'delta', 'language'); - $this->assertEntityFieldQuery($query, array(), 'Test with a grouped delta + language meta condition (empty result set, delta condition unsatisifed).'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity', '=') - ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language') - ->fieldDeltaCondition($this->fields[0], 1, '<', 'delta', 'language') - ->fieldLanguageCondition($this->fields[0], LANGUAGE_NOT_SPECIFIED, '<>', 'delta', 'language'); - $this->assertEntityFieldQuery($query, array(), 'Test with a grouped delta + language meta condition (empty result set, language condition unsatisifed).'); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity', '=') - ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language') - ->fieldDeltaCondition($this->fields[0], 1, '>=', 'delta', 'language') - ->fieldLanguageCondition($this->fields[0], LANGUAGE_NOT_SPECIFIED, '<>', 'delta', 'language'); - $this->assertEntityFieldQuery($query, array(), 'Test with a grouped delta + language meta condition (empty result set, both conditions unsatisifed).'); - - // Test grouping with another field to ensure that grouping cache is reset - // properly. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle', '=') - ->fieldCondition($this->fields[1], 'shape', 'circle', '=', 'delta', 'language') - ->fieldCondition($this->fields[1], 'color', 'blue', '=', 'delta', 'language') - ->fieldDeltaCondition($this->fields[1], 1, '=', 'delta', 'language') - ->fieldLanguageCondition($this->fields[1], LANGUAGE_NOT_SPECIFIED, '=', 'delta', 'language'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle', 5), - ), 'Test grouping cache.'); - } - - /** - * Tests the routing feature of EntityFieldQuery. - */ - function testEntityFieldQueryRouting() { - // Entity-only query. - $query = new EntityFieldQuery(); - $query->entityCondition('entity_type', 'test_entity_bundle_key'); - $this->assertIdentical($query->queryCallback(), array($query, 'propertyQuery'), 'Entity-only queries are handled by the propertyQuery handler.'); - - // Field-only query. - $query = new EntityFieldQuery(); - $query->fieldCondition($this->fields[0], 'value', '3'); - $this->assertIdentical($query->queryCallback(), 'field_sql_storage_field_storage_query', 'Pure field queries are handled by the Field storage handler.'); - - // Mixed entity and field query. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', '3'); - $this->assertIdentical($query->queryCallback(), 'field_sql_storage_field_storage_query', 'Mixed queries are handled by the Field storage handler.'); - - // Overriding with $query->executeCallback. - $query = new EntityFieldQuery(); - $query->entityCondition('entity_type', 'test_entity_bundle_key'); - $query->executeCallback = 'field_test_dummy_field_storage_query'; - $this->assertEntityFieldQuery($query, array( - array('user', 1), - ), 'executeCallback can override the query handler.'); - - // Overriding with $query->executeCallback via hook_entity_query_alter(). - $query = new EntityFieldQuery(); - $query->entityCondition('entity_type', 'test_entity_bundle_key'); - // Add a flag that will be caught by field_test_entity_query_alter(). - $query->alterMyExecuteCallbackPlease = TRUE; - $this->assertEntityFieldQuery($query, array( - array('user', 1), - ), 'executeCallback can override the query handler when set in a hook_entity_query_alter().'); - - // Mixed-storage queries. - $query = new EntityFieldQuery(); - $query - ->fieldCondition($this->fields[0], 'value', '3') - ->fieldCondition($this->fields[1], 'shape', 'squ', 'STARTS_WITH'); - // Alter the storage of the field. - $query->fields[1]['storage']['module'] = 'dummy_storage'; - try { - $query->queryCallback(); - } - catch (EntityFieldQueryException $exception) { - $pass = ($exception->getMessage() == t("Can't handle more than one field storage engine")); - } - $this->assertTrue($pass, 'Cannot query across field storage engines.'); - } - - /** - * Tests the pager integration of EntityFieldQuery. - */ - function testEntityFieldQueryPager() { - // Test pager in propertyQuery - $_GET['page'] = '0,1'; - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyOrderBy('ftid', 'ASC') - ->pager(3, 0); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - ), 'Test pager integration in propertyQuery: page 1.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyOrderBy('ftid', 'ASC') - ->pager(3, 1); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test pager integration in propertyQuery: page 2.', TRUE); - - // Test pager in field storage - $_GET['page'] = '0,1'; - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->propertyOrderBy('ftid', 'ASC') - ->pager(2, 0); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - ), 'Test pager integration in field storage: page 1.', TRUE); - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->propertyOrderBy('ftid', 'ASC') - ->pager(2, 1); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - ), 'Test pager integration in field storage: page 2.', TRUE); - - unset($_GET['page']); - } - - /** - * Tests disabling the pager in EntityFieldQuery. - */ - function testEntityFieldQueryDisablePager() { - // Test enabling a pager and then disabling it. - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->propertyOrderBy('ftid', 'ASC') - ->pager(1) - ->pager(0); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'All test entities are listed when the pager is enabled and then disabled.', TRUE); - } - - /** - * Tests the TableSort integration of EntityFieldQuery. - */ - function testEntityFieldQueryTableSort() { - // Test TableSort in propertyQuery - $_GET['sort'] = 'asc'; - $_GET['order'] = 'Id'; - $header = array( - 'id' => array('data' => 'Id', 'type' => 'property', 'specifier' => 'ftid'), - 'type' => array('data' => 'Type', 'type' => 'entity', 'specifier' => 'bundle'), - ); - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->tableSort($header); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test TableSort by property: ftid ASC in propertyQuery.', TRUE); - - $_GET['sort'] = 'desc'; - $_GET['order'] = 'Id'; - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->tableSort($header); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 1), - ), 'Test TableSort by property: ftid DESC in propertyQuery.', TRUE); - - $_GET['sort'] = 'asc'; - $_GET['order'] = 'Type'; - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->tableSort($header); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test TableSort by entity: bundle ASC in propertyQuery.', TRUE); - - $_GET['sort'] = 'desc'; - $_GET['order'] = 'Type'; - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->tableSort($header); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - ), 'Test TableSort by entity: bundle DESC in propertyQuery.', TRUE); - - // Test TableSort in field storage - $_GET['sort'] = 'asc'; - $_GET['order'] = 'Id'; - $header = array( - 'id' => array('data' => 'Id', 'type' => 'property', 'specifier' => 'ftid'), - 'type' => array('data' => 'Type', 'type' => 'entity', 'specifier' => 'bundle'), - 'field' => array('data' => 'Field', 'type' => 'field', 'specifier' => array('field' => $this->field_names[0], 'column' => 'value')), - ); - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->tableSort($header); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test TableSort by property: ftid ASC in field storage.', TRUE); - - $_GET['sort'] = 'desc'; - $_GET['order'] = 'Id'; - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->tableSort($header); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 1), - ), 'Test TableSort by property: ftid DESC in field storage.', TRUE); - - $_GET['sort'] = 'asc'; - $_GET['order'] = 'Type'; - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->tableSort($header) - ->entityOrderBy('entity_id', 'DESC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 5), - ), 'Test TableSort by entity: bundle ASC in field storage.', TRUE); - - $_GET['sort'] = 'desc'; - $_GET['order'] = 'Type'; - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->tableSort($header) - ->entityOrderBy('entity_id', 'ASC'); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - ), 'Test TableSort by entity: bundle DESC in field storage.', TRUE); - - $_GET['sort'] = 'asc'; - $_GET['order'] = 'Field'; - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->tableSort($header); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 1), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 6), - ), 'Test TableSort by field ASC.', TRUE); - - $_GET['sort'] = 'desc'; - $_GET['order'] = 'Field'; - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($this->fields[0], 'value', 0, '>') - ->tableSort($header); - $this->assertEntityFieldQuery($query, array( - array('test_entity_bundle_key', 6), - array('test_entity_bundle_key', 5), - array('test_entity_bundle_key', 4), - array('test_entity_bundle_key', 3), - array('test_entity_bundle_key', 2), - array('test_entity_bundle_key', 1), - ), 'Test TableSort by field DESC.', TRUE); - - unset($_GET['sort']); - unset($_GET['order']); - } - - /** - * Tests EntityFieldQuery access on non-node entities. - */ - function testEntityFieldQueryAccess() { - // Test as a user with ability to bypass node access. - $privileged_user = $this->drupalCreateUser(array('bypass node access', 'access content')); - $this->drupalLogin($privileged_user); - $this->drupalGet('entity-query-access/test/' . $this->fields[0]['field_name']); - $this->assertText('Found entity', 'Returned access response with entities.'); - $this->drupalLogout(); - - // Test as a user that does not have ability to bypass node access or view - // all nodes. - $regular_user = $this->drupalCreateUser(array('access content')); - $this->drupalLogin($regular_user); - $this->drupalGet('entity-query-access/test/' . $this->fields[0]['field_name']); - $this->assertText('Found entity', 'Returned access response with entities.'); - $this->drupalLogout(); - } - - /** - * Fetches the results of an EntityFieldQuery and compares. - * - * @param $query - * An EntityFieldQuery to run. - * @param $intended_results - * A list of results, every entry is again a list, first being the entity - * type, the second being the entity_id. - * @param $message - * The message to be displayed as the result of this test. - * @param $ordered - * If FALSE then the result of EntityFieldQuery will match - * $intended_results even if the order is not the same. If TRUE then order - * should match too. - */ - function assertEntityFieldQuery($query, $intended_results, $message, $ordered = FALSE) { - $results = array(); - try { - foreach ($query->execute() as $entity_type => $entity_ids) { - foreach ($entity_ids as $entity_id => $stub_entity) { - $results[] = array($entity_type, $entity_id); - } - } - if (!isset($ordered) || !$ordered) { - sort($results); - sort($intended_results); - } - if (!$this->assertEqual($results, $intended_results, $message)) { - debug($results, $message); - } - } - catch (Exception $e) { - $this->fail('Exception thrown: '. $e->getMessage()); - } - } - - /** - * Tests EFQ table prefixing with multiple conditions and an altered join. - * - * @see field_test_query_efq_table_prefixing_test_alter() - */ - function testTablePrefixing() { - $query = new EntityFieldQuery(); - $query = $query - ->entityCondition('entity_type', 'test_entity') - ->entityCondition('bundle', 'test_bundle') - ->entityCondition('entity_id', '1') - ->addTag('efq_table_prefixing_test'); - - $expected = array(array('test_entity', 1)); - - $this->assertEntityFieldQuery($query, $expected, 'An EntityFieldQuery returns the expected results when altered with an additional join on the base table.'); - } - -} diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php index e0b25fb..e457e0f 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php @@ -7,10 +7,8 @@ namespace Drupal\system\Tests\Entity; -use Exception; use InvalidArgumentException; -use Drupal\Core\Entity\EntityFieldQuery; use Drupal\Core\Language\Language; use Drupal\simpletest\WebTestBase; @@ -271,35 +269,42 @@ function testMultilingualProperties() { // Test property conditions and orders with multiple languages in the same // query. - $query = new EntityFieldQuery(); - $query->entityCondition('entity_type', 'entity_test'); - $query->entityCondition('langcode', $default_langcode); - $query->propertyCondition('user_id', $properties[$default_langcode]['user_id'], NULL, 'original'); - $query->propertyCondition('name', $properties[$default_langcode]['name'], NULL, 'original'); - $query->propertyLanguageCondition($default_langcode, NULL, 'original'); - $query->propertyCondition('name', $properties[$langcode]['name'], NULL, 'translation'); - $query->propertyLanguageCondition($langcode, NULL, 'translation'); - $query->propertyOrderBy('name', 'ASC', 'original'); - $result = $query->execute(); + $query = drupal_container()->get('entity.query')->get('entity_test'); + $default_langcode_group = $query->andCondition() + ->condition('langcode', $default_langcode) + ->condition('user_id', $properties[$default_langcode]['user_id']) + ->condition('name', $properties[$default_langcode]['name']); + $langcode_group = $query->andCondition() + ->condition('langcode', $langcode) + ->condition('name', $properties[$langcode]['name']); + $result = $query + ->condition('langcode', $default_langcode) + ->condition($default_langcode_group) + ->condition($langcode_group) + ->execute(); $this->assertEqual(count($result), 1, 'One entity loaded by name and uid using different language meta conditions.'); // Test mixed property and field conditions. - $entity = entity_load('entity_test', key($result['entity_test']), TRUE); + $entity = entity_load('entity_test', reset($result), TRUE); $field_value = $this->randomString(); $entity->getTranslation($langcode)->set($this->field_name, array(array('value' => $field_value))); $entity->save(); - $query = new EntityFieldQuery(); - $query->entityCondition('entity_type', 'entity_test'); - $query->entityCondition('langcode', $default_langcode); - $query->propertyCondition('user_id', $properties[$default_langcode]['user_id'], NULL, 'original'); - $query->propertyCondition('name', $properties[$default_langcode]['name'], NULL, 'original'); - $query->propertyLanguageCondition($default_langcode, NULL, 'original'); - $query->propertyCondition('name', $properties[$langcode]['name'], NULL, 'translation'); - $query->propertyLanguageCondition($langcode, NULL, 'translation'); - $query->fieldCondition($this->field_name, 'value', $field_value, NULL, NULL, 'translation'); - $query->fieldLanguageCondition($this->field_name, $langcode, NULL, NULL, 'translation'); - $query->propertyOrderBy('name', 'ASC', 'original'); - $result = $query->execute(); + $query = drupal_container()->get('entity.query')->get('entity_test'); + $default_langcode_group = $query->andCondition() + ->condition('langcode', $default_langcode) + ->condition('user_id', $properties[$default_langcode]['user_id']) + ->condition('name', $properties[$default_langcode]['name']); + $langcode_group = $query->andCondition() + ->condition('langcode', $langcode) + ->condition('name', $properties[$langcode]['name']) + ->condition("$this->field_name.value", $field_value) + ->condition("$this->field_name.langcode", $langcode); + $result = $query + ->condition('langcode', $default_langcode) + ->condition($default_langcode_group) + ->condition($langcode_group) + ->execute(); $this->assertEqual(count($result), 1, 'One entity loaded by name, uid and field value using different language meta conditions.'); } + } diff --git a/core/modules/system/tests/modules/entity_query_access_test/entity_query_access_test.info b/core/modules/system/tests/modules/entity_query_access_test/entity_query_access_test.info deleted file mode 100644 index 369b204..0000000 --- a/core/modules/system/tests/modules/entity_query_access_test/entity_query_access_test.info +++ /dev/null @@ -1,7 +0,0 @@ -name = "Entity query access test" -description = "Support module for checking entity query results." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE - diff --git a/core/modules/system/tests/modules/entity_query_access_test/entity_query_access_test.module b/core/modules/system/tests/modules/entity_query_access_test/entity_query_access_test.module deleted file mode 100644 index 2a4fd75..0000000 --- a/core/modules/system/tests/modules/entity_query_access_test/entity_query_access_test.module +++ /dev/null @@ -1,57 +0,0 @@ - "Retrieve a sample of entity query access data", - 'page callback' => 'entity_query_access_test_sample_query', - 'page arguments' => array(2), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - - return $items; -} - -/** - * Returns the results from an example EntityFieldQuery. - */ -function entity_query_access_test_sample_query($field_name) { - global $user; - - // Simulate user does not have access to view all nodes. - $access = &drupal_static('node_access_view_all_nodes'); - $access[$user->uid] = FALSE; - - $query = new EntityFieldQuery(); - $query - ->entityCondition('entity_type', 'test_entity_bundle_key') - ->fieldCondition($field_name, 'value', 0, '>') - ->entityOrderBy('entity_id', 'ASC'); - $results = array( - 'items' => array(), - 'title' => t('EntityFieldQuery results'), - ); - foreach ($query->execute() as $entity_type => $entity_ids) { - foreach ($entity_ids as $entity_id => $entity_stub) { - $results['items'][] = format_string('Found entity of type @entity_type with id @entity_id', array('@entity_type' => $entity_type, '@entity_id' => $entity_id)); - } - } - if (count($results['items']) > 0) { - $output = theme('item_list', $results); - } - else { - $output = 'No results found with EntityFieldQuery.'; - } - return $output; -} diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php index 67e4b9b..fbb735d 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php @@ -8,10 +8,9 @@ namespace Drupal\entity_test; use PDO; - +use Drupal\Core\Entity\Query\QueryInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\DatabaseStorageControllerNG; -use Drupal\Core\Entity\EntityFieldQuery; /** * Defines the controller class for the test entity. @@ -24,7 +23,7 @@ class EntityTestStorageController extends DatabaseStorageControllerNG { /** * Overrides Drupal\Core\Entity\DatabaseStorageController::buildPropertyQuery(). */ - protected function buildPropertyQuery(EntityFieldQuery $entity_query, array $values) { + protected function buildPropertyQuery(QueryInterface $entity_query, array $values) { // @todo We should not be using a condition to specify whether conditions // apply to the default language or not. We need to move this to a // separate parameter during the following API refactoring. diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorageController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorageController.php index c77cad5..64b7a06 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorageController.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorageController.php @@ -8,6 +8,7 @@ namespace Drupal\taxonomy; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\Query\QueryInterface; use Drupal\Core\Entity\DatabaseStorageController; /** @@ -57,9 +58,9 @@ protected function buildQuery($ids, $revision_id = FALSE) { /** * Overrides Drupal\Core\Entity\DatabaseStorageController::buildPropertyQuery(). */ - protected function buildPropertyQuery(\Drupal\Core\Entity\EntityFieldQuery $entity_query, array $values) { + protected function buildPropertyQuery(QueryInterface $entity_query, array $values) { if (isset($values['name'])) { - $entity_query->propertyCondition('name', $values['name'], 'LIKE'); + $entity_query->condition('name', $values['name'], 'LIKE'); unset($values['name']); } parent::buildPropertyQuery($entity_query, $values); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/EfqTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/EfqTest.php index 7726dbf..61d4f4b 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/EfqTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/EfqTest.php @@ -7,16 +7,16 @@ namespace Drupal\taxonomy\Tests; -use Drupal\Core\Entity\EntityFieldQuery; +use Drupal\Core\Entity\Query\QueryFactory; /** - * Tests the functionality of EntityFieldQuery for taxonomy entities. + * Tests the functionality of EntityQueryInterface for taxonomy entities. */ class EfqTest extends TaxonomyTestBase { public static function getInfo() { return array( - 'name' => 'Taxonomy EntityFieldQuery', - 'description' => 'Verifies operation of a taxonomy-based EntityFieldQuery.', + 'name' => 'Taxonomy entity query', + 'description' => 'Verifies operation of a taxonomy-based Entity Query.', 'group' => 'Taxonomy', ); } @@ -29,7 +29,7 @@ function setUp() { } /** - * Tests that a basic taxonomy EntityFieldQuery works. + * Tests that a basic taxonomy entity query works. */ function testTaxonomyEfq() { $terms = array(); @@ -37,16 +37,17 @@ function testTaxonomyEfq() { $term = $this->createTerm($this->vocabulary); $terms[$term->tid] = $term; } - $query = new EntityFieldQuery(); - $query->entityCondition('entity_type', 'taxonomy_term'); - $result = $query->execute(); - $result = $result['taxonomy_term']; - asort($result); - $this->assertEqual(array_keys($terms), array_keys($result), 'Taxonomy terms were retrieved by EntityFieldQuery.'); - - $first_result = reset($result); - $term = _field_create_entity_from_ids($first_result); - $this->assertEqual($term->tid, $first_result->entity_id, 'Taxonomy term can be created based on the IDs'); + $result = drupal_container()->get('entity.query')->get('taxonomy_term')->execute(); + sort($result); + $this->assertEqual(array_keys($terms), $result, 'Taxonomy terms were retrieved by entity query.'); + $tid = reset($result); + $ids = (object) array( + 'entity_type' => 'taxonomy_term', + 'entity_id' => $tid, + 'bundle' => $this->vocabulary->machine_name, + ); + $term = _field_create_entity_from_ids($ids); + $this->assertEqual($term->tid, $tid, 'Taxonomy term can be created based on the IDs'); // Create a second vocabulary and five more terms. $vocabulary2 = $this->createVocabulary(); @@ -56,12 +57,18 @@ function testTaxonomyEfq() { $terms2[$term->tid] = $term; } - $query = new EntityFieldQuery(); - $query->entityCondition('entity_type', 'taxonomy_term'); - $query->entityCondition('bundle', $vocabulary2->machine_name); - $result = $query->execute(); - $result = $result['taxonomy_term']; - asort($result); - $this->assertEqual(array_keys($terms2), array_keys($result), format_string('Taxonomy terms from the %name vocabulary were retrieved by EntityFieldQuery.', array('%name' => $vocabulary2->name))); + $result = drupal_container()->get('entity.query')->get('taxonomy_term') + ->condition('vid', $vocabulary2->vid) + ->execute(); + sort($result); + $this->assertEqual(array_keys($terms2), $result, format_string('Taxonomy terms from the %name vocabulary were retrieved by entity query.', array('%name' => $vocabulary2->name))); + $tid = reset($result); + $ids = (object) array( + 'entity_type' => 'taxonomy_term', + 'entity_id' => $tid, + 'bundle' => $vocabulary2->machine_name, + ); + $term = _field_create_entity_from_ids($ids); + $this->assertEqual($term->tid, $tid, 'Taxonomy term can be created based on the IDs'); } } diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index faa04dd..5d7446a 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -1030,7 +1030,7 @@ function taxonomy_term_load_multiple_by_name($name, $vocabulary = NULL) { * database access if loaded again during the same page request. * * @see entity_load_multiple() - * @see Drupal\Core\Entity\EntityFieldQuery + * @see Drupal\Core\Entity\Query\EntityQueryInterface * * @param array $tids * (optional) An array of entity IDs. If omitted, all entities are loaded. @@ -1677,38 +1677,6 @@ function taxonomy_taxonomy_term_delete(Term $term) { */ /** - * Implements hook_entity_query_alter(). - * - * Converts EntityFieldQuery instances on taxonomy terms that have an entity - * condition on term bundles (vocabulary machine names). Since the vocabulary - * machine name is not present in the {taxonomy_term_data} table itself, we have - * to convert the bundle condition into a property condition of vocabulary IDs - * to match against {taxonomy_term_data}.vid. - */ -function taxonomy_entity_query_alter($query) { - $conditions = &$query->entityConditions; - - // Alter only taxonomy term queries with bundle conditions. - if (isset($conditions['entity_type']) && $conditions['entity_type']['value'] == 'taxonomy_term' && isset($conditions['bundle'])) { - // Convert vocabulary machine names to vocabulary IDs. - $vocabulary_data = taxonomy_vocabulary_get_names(); - $vids = array(); - if (is_array($conditions['bundle']['value'])) { - foreach ($conditions['bundle']['value'] as $vocabulary_machine_name) { - $vids[] = $vocabulary_data[$vocabulary_machine_name]->vid; - } - } - else { - $vocabulary_machine_name = $conditions['bundle']['value']; - $vids = $vocabulary_data[$vocabulary_machine_name]->vid; - } - - $query->propertyCondition('vid', $vids, $conditions['bundle']['operator']); - unset($conditions['bundle']); - } -} - -/** * Implements hook_library_info(). */ function taxonomy_library_info() { diff --git a/core/modules/user/user.module b/core/modules/user/user.module index e42002b..67d833a 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -289,7 +289,7 @@ function user_external_load($authname) { * @see user_load() * @see user_load_by_mail() * @see user_load_by_name() - * @see Drupal\Core\Entity\EntityFieldQuery + * @see Drupal\Core\Entity\Query\EntitydQueryInterface */ function user_load_multiple(array $uids = NULL, $reset = FALSE) { return entity_load_multiple('user', $uids, $reset);