diff --git a/core/lib/Drupal/Core/Entity/Query/QueryInterface.php b/core/lib/Drupal/Core/Entity/Query/QueryInterface.php index 630d1767396ca1c31a91615f09fca79d6adc09a2..9c05dfa4d6f231ffe73c8d14c48dc9653822c6f7 100644 --- a/core/lib/Drupal/Core/Entity/Query/QueryInterface.php +++ b/core/lib/Drupal/Core/Entity/Query/QueryInterface.php @@ -35,13 +35,18 @@ public function getEntityTypeId(); * * @param $field * Name of the field being queried. It must contain a field name, optionally - * followed by a column name. The column can be "entity" for reference - * fields and that can be followed similarly by a field name and so on. Some - * examples: + * followed by a column name. The column can be the reference property, + * usually "entity", for reference fields and that can be followed + * similarly by a field name and so on. Additionally, the target entity type + * can be specified by appending the ":target_entity_type_id" to "entity". + * Some examples: * - nid * - tags.value * - tags + * - tags.entity.name + * - tags.entity:taxonomy_term.name * - uid.entity.name + * - uid.entity:user.name * "tags" "is the same as "tags.value" as value is the default column. * If two or more conditions have the same field names they apply to the * same delta within that field. In order to limit the condition to a diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php index 09cea061bddbb6c48470b227abfc30a7b31c0294..e813cde38592f12d1d5883c013ff925ab5bb7ae3 100644 --- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php @@ -7,6 +7,8 @@ use Drupal\Core\Entity\Query\QueryException; use Drupal\Core\Entity\Sql\SqlEntityStorageInterface; use Drupal\Core\Entity\Sql\TableMappingInterface; +use Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface; +use Drupal\Core\TypedData\DataReferenceDefinitionInterface; /** * Adds tables and fields to the SQL entity query. @@ -254,10 +256,20 @@ public function addField($field, $type, $langcode) { $relationship_specifier = $specifiers[$key + 1]; $next_index_prefix = $relationship_specifier; } + $entity_type_id = NULL; + // Relationship specifier can also contain the entity type ID, i.e. + // entity:node, entity:user or entity:taxonomy. + if (strpos($relationship_specifier, ':') !== FALSE) { + list($relationship_specifier, $entity_type_id) = explode(':', $relationship_specifier, 2); + } // Check for a valid relationship. - if (isset($propertyDefinitions[$relationship_specifier]) && $field_storage->getPropertyDefinition('entity')->getDataType() == 'entity_reference' ) { - // If it is, use the entity type. - $entity_type_id = $propertyDefinitions[$relationship_specifier]->getTargetDefinition()->getEntityTypeId(); + if (isset($propertyDefinitions[$relationship_specifier]) && $propertyDefinitions[$relationship_specifier] instanceof DataReferenceDefinitionInterface) { + // If it is, use the entity type if specified already, otherwise use + // the definition. + $target_definition = $propertyDefinitions[$relationship_specifier]->getTargetDefinition(); + if (!$entity_type_id && $target_definition instanceof EntityDataDefinitionInterface) { + $entity_type_id = $target_definition->getEntityTypeId(); + } $entity_type = $this->entityManager->getDefinition($entity_type_id); $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id); // Add the new entity base table using the table and sql column. diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryRelationshipTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryRelationshipTest.php index 8465438207069e92e0f67468e47f032f12515a63..60dc13c67b59298676d2fa1a729160a77943b4f9 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryRelationshipTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryRelationshipTest.php @@ -1,6 +1,8 @@ queryResults = $this->factory->get('entity_test') ->condition("user_id.entity.name", $this->accounts[0]->getUsername()) @@ -154,6 +156,56 @@ public function testQuery() { ->condition("$this->fieldName.entity.name", $this->terms[0]->name->value, '<>') ->execute(); $this->assertResults(array(1, 2)); + // 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:user.name", $this->accounts[0]->getUsername()) + ->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:user.name", $this->accounts[0]->getUsername(), '<>') + ->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:user.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:user.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:taxonomy_term.name", $this->terms[0]->name->value) + ->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.target_id.entity:taxonomy_term.name", $this->terms[0]->name->value) + ->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:taxonomy_term.name", $this->terms[0]->name->value, '<>') + ->execute(); + $this->assertResults(array(1, 2)); + } + + /** + * Tests the invalid specifier in the query relationship. + */ + public function testInvalidSpecifier() { + $this->setExpectedException(PluginNotFoundException::class); + $this->factory + ->get('taxonomy_term') + ->condition('langcode.language.foo', 'bar') + ->execute(); } /**