diff -u b/core/modules/field/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Tables.php b/core/modules/field/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Tables.php --- b/core/modules/field/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Tables.php +++ b/core/modules/field/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Tables.php @@ -61,14 +61,14 @@ function addField($field, $type, $langcode) { $entity_type = $this->sqlQuery->getMetaData('entity_type'); $age = $this->sqlQuery->getMetaData('age'); - // This variables ensures grouping works correctly. For example: + // This variable ensures grouping works correctly. For example: // ->condition('tags', 2, '>') // ->condition('tags', 20, '<') // ->condition('node_reference.nid.entity.tags', 2) // The first two should use the same table but the last one needs to be a - // new table. So for the first two, the table array index will be '0.tags' - // while the third will be '1.tags'. - $index_prefix = 0; + // new table. So for the first two, the table array index will be 'tags' + // while the third will be 'node_reference.nid.tags'. + $index_prefix = ''; $specifiers = explode('.', $field); $base_table = 'base_table'; $count = count($specifiers) - 1; @@ -77,11 +77,6 @@ $propertyDefinitions = array(); $entity_info = entity_get_info($entity_type); for ($key = 0; $key <= $count; $key ++) { - // This can either be the name of an entity property (non-configurable - // field), a field API field (a configurable field) or an indication - // that the next specifier will be on another entity (here it'll be - // called relationship field). - $specifier = $specifiers[$key]; // If there is revision support and only the current revision is being // queried then use the revision id. Otherwise, the entity id will do. if (!empty($entity_info['entity_keys']['revision']) && $age == FIELD_LOAD_CURRENT) { @@ -96,13 +91,19 @@ $entity_id_field = $entity_info['entity_keys']['id']; $field_id_field = 'entity_id'; } - // field_purge_batch() is passing in id:$field_id. + // This can either be the name of an entity property (non-configurable + // field), a field API field (a configurable field). + $specifier = $specifiers[$key]; + // First, check for field API fields by trying to retrieve the field specified. + // Normally it is a field name, but field_purge_batch() is passing in + // id:$field_id so check that first. if (substr($specifier, 0, 3) == 'id:') { $field = field_info_field_by_id(substr($specifier, 3)); } else { $field = field_info_field($specifier); } + // If we managed to retrieve the field, process it. if ($field) { // Find the field column. $column = FALSE; @@ -136,15 +137,18 @@ // exception anyways so no need to do it here. $column = $propertyDefinitions[$relationship_specifier]['settings']['id source']; } + // Prepare the next index prefix. + $next_index_prefix = "$relationship_specifier.$column"; } } else { // If this is the last specifier, default to value. $column = 'value'; } - $table = $this->ensureFieldTable("$index_prefix.", $field, $type, $langcode, $base_table, $entity_id_field, $field_id_field); + $table = $this->ensureFieldTable($index_prefix, $field, $type, $langcode, $base_table, $entity_id_field, $field_id_field); $sql_column = _field_sql_storage_columnname($field['field_name'], $column); } + // This is an entity property (non-configurable field). else { // ensureEntityTable() decides whether an entity property will be // queried from the data table or the base table based on where it @@ -157,7 +161,7 @@ } $entity_tables[$entity_info['base_table']] = drupal_get_schema($entity_info['base_table']); $sql_column = $specifier; - $table = $this->ensureEntityTable("$index_prefix.", $specifier, $type, $langcode, $base_table, $entity_id_field, $entity_tables); + $table = $this->ensureEntityTable($index_prefix, $specifier, $type, $langcode, $base_table, $entity_id_field, $entity_tables); } // If there are more specifiers to come, it's a relationship. if ($key < $count) { @@ -176,6 +180,7 @@ $entity = entity_create($entity_type, $values); $propertyDefinitions = $entity->$specifier->getPropertyDefinitions(); $relationship_specifier = $specifiers[$key + 1]; + $next_index_prefix = $relationship_specifier; } // Check for a valid relationship. if (isset($propertyDefinitions[$relationship_specifier]['constraints']['entity type']) && isset($propertyDefinitions[$relationship_specifier]['settings']['id source'])) { @@ -185,9 +190,9 @@ // Add the new entity base table using the table and sql column. $join_condition= '%alias.' . $entity_info['entity_keys']['id'] . " = $table.$sql_column"; $base_table = $this->sqlQuery->leftJoin($entity_info['base_table'], NULL, $join_condition); - $index_prefix++; $propertyDefinitions = array(); $key++; + $index_prefix .= "$next_index_prefix."; } else { throw new QueryException(format_string('Invalid specifier @next.', array('@next' => $next))); diff -u b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php --- b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php @@ -69,15 +69,15 @@ ); } - /** - * Overrides \Drupal\simpletest\WebTestBase::setUp(). - */ protected function setUp() { parent::setUp(); + // We want a taxonomy term reference field. It needs a vocabulary, terms, + // a field and an instance. First, create the vocabulary. $vocabulary = entity_create('taxonomy_vocabulary', array( 'machine_name' => strtolower($this->randomName()), )); $vocabulary->save(); + // Second, create the field. $this->fieldName = strtolower($this->randomName()); $field = array( 'field_name' => $this->fieldName, @@ -85,12 +85,14 @@ ); $field['settings']['allowed_values']['vocabulary'] = $vocabulary->machine_name; field_create_field($field); + // Third, create the instance. $instance = array( 'entity_type' => 'entity_test', 'field_name' => $this->fieldName, 'bundle' => 'entity_test', ); field_create_instance($instance); + // Create two terms and also two accounts. for ($i = 0; $i <= 1; $i++) { $term = entity_create('taxonomy_term', array( 'name' => $this->randomName(), @@ -100,6 +102,9 @@ $this->terms[] = $term; $this->accounts[] = $this->drupalCreateUser(); } + // Create three entity_test entities, the 0th entity will point to the + // 0th account and 0th term, the 1st and 2nd entity will point to the + // 1st account and 1st term. for ($i = 0; $i <= 2; $i++) { $entity = entity_create('entity_test', array()); $entity->name->value = $this->randomName(); @@ -116,36 +121,53 @@ * Tests querying. */ public function testQuery() { + // This returns the 0th entity as that's only one pointing to the 0th + // account. $this->queryResults = $this->factory->get('entity_test') ->condition("user_id.entity.name", $this->accounts[0]->name) ->execute(); $this->assertResults(array(0)); + // This returns the 1st and 2nd entity as those point to the 1st account. $this->queryResults = $this->factory->get('entity_test') ->condition("user_id.entity.name", $this->accounts[0]->name, '<>') ->execute(); $this->assertResults(array(1, 2)); + // This returns all three entities because all of them point to an + // account. $this->queryResults = $this->factory->get('entity_test') ->exists("user_id.entity.name") ->execute(); $this->assertResults(array(0, 1, 2)); + // This returns no entities because all of them point to an account. $this->queryResults = $this->factory->get('entity_test') ->notExists("user_id.entity.name") ->execute(); $this->assertEqual(count($this->queryResults), 0); + // This returns the 0th entity as that's only one pointing to the 0th + // term (test without specifying the field column). $this->queryResults = $this->factory->get('entity_test') ->condition("$this->fieldName.entity.name", $this->terms[0]->name) ->execute(); $this->assertResults(array(0)); + // This returns the 0th entity as that's only one pointing to the 0th + // term (test with specifying the column name). $this->queryResults = $this->factory->get('entity_test') ->condition("$this->fieldName.tid.entity.name", $this->terms[0]->name) ->execute(); $this->assertResults(array(0)); + // This returns the 1st and 2nd entity as those point to the 1st term. $this->queryResults = $this->factory->get('entity_test') ->condition("$this->fieldName.entity.name", $this->terms[0]->name, '<>') ->execute(); $this->assertResults(array(1, 2)); } + /** + * Assert the results. + * + * @param array $expected + * A list of indexes in the $this->entities array. + */ protected function assertResults($expected) { $this->assertEqual(count($this->queryResults), count($expected)); foreach ($expected as $key) {