diff --git a/core/lib/Drupal/Core/Entity/Query/QueryBase.php b/core/lib/Drupal/Core/Entity/Query/QueryBase.php index 38deabd8db1e576588ed80207cb3e503f4a9ba63..d84a266013bbf58a8b037be2b9f1d5ff58d94cd0 100644 --- a/core/lib/Drupal/Core/Entity/Query/QueryBase.php +++ b/core/lib/Drupal/Core/Entity/Query/QueryBase.php @@ -108,6 +108,13 @@ abstract class QueryBase implements QueryInterface { */ protected $allRevisions = FALSE; + /** + * Flag indicating whether to query the latest revision. + * + * @var bool + */ + protected $latestRevision = FALSE; + /** * The query pager data. * @@ -252,6 +259,16 @@ public function accessCheck($access_check = TRUE) { */ public function currentRevision() { $this->allRevisions = FALSE; + $this->latestRevision = FALSE; + return $this; + } + + /** + * {@inheritdoc} + */ + public function latestRevision() { + $this->allRevisions = TRUE; + $this->latestRevision = TRUE; return $this; } @@ -260,6 +277,7 @@ public function currentRevision() { */ public function allRevisions() { $this->allRevisions = TRUE; + $this->latestRevision = FALSE; return $this; } diff --git a/core/lib/Drupal/Core/Entity/Query/QueryInterface.php b/core/lib/Drupal/Core/Entity/Query/QueryInterface.php index df375207bda85a2117ca51be3feab2983e4a1387..d4b0608e80753774681b84ced30eb326db6c9752 100644 --- a/core/lib/Drupal/Core/Entity/Query/QueryInterface.php +++ b/core/lib/Drupal/Core/Entity/Query/QueryInterface.php @@ -254,6 +254,17 @@ public function orConditionGroup(); */ public function currentRevision(); + /** + * Queries the latest revision. + * + * The latest revision is the most recent revision of an entity. This will be + * either the default revision, or a pending revision if one exists and it is + * newer than the default. + * + * @return $this + */ + public function latestRevision(); + /** * Queries all the revisions. * diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Query.php b/core/lib/Drupal/Core/Entity/Query/Sql/Query.php index cd245ef7b5284511a979f00ea05ef217b661783d..50b81bba91d358148fff24803d36d8e068fe1bdb 100644 --- a/core/lib/Drupal/Core/Entity/Query/Sql/Query.php +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Query.php @@ -119,6 +119,14 @@ protected function prepare() { // entity id. $this->sqlFields["base_table.$id_field"] = ['base_table', $id_field]; } + + // Add a self-join to the base revision table if we're querying only the + // latest revisions. + if ($this->latestRevision && $revision_field) { + $this->sqlQuery->leftJoin($base_table, 'base_table_2', "base_table.$id_field = base_table_2.$id_field AND base_table.$revision_field < base_table_2.$revision_field"); + $this->sqlQuery->isNull("base_table_2.$id_field"); + } + if ($this->accessCheck) { $this->sqlQuery->addTag($this->entityTypeId . '_access'); } diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryTest.php index ec33f67e87ee7975385c2fa8a7137f137028dd5f..ebb307284ccacad838f339c294c02c3931925544 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryTest.php @@ -318,6 +318,16 @@ public function testEntityQuery() { // Now we get everything. $assert = [4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', 10 => '10', 11 => '11', 12 => '12', 20 => '12', 13 => '13', 21 => '13', 14 => '14', 22 => '14', 15 => '15', 23 => '15']; $this->assertIdentical($results, $assert); + + // Check that a query on the latest revisions without any condition returns + // the correct results. + $results = $this->factory->get('entity_test_mulrev') + ->latestRevision() + ->sort('id') + ->sort('revision_id') + ->execute(); + $expected = [1 => '1', 2 => '2', 3 => '3', 16 => '4', 17 => '5', 18 => '6', 19 => '7', 8 => '8', 9 => '9', 10 => '10', 11 => '11', 20 => '12', 21 => '13', 22 => '14', 23 => '15']; + $this->assertSame($expected, $results); } /** @@ -936,6 +946,54 @@ public function testPendingRevisions() { ->allRevisions() ->execute(); $this->assertEqual($result, [16 => '14']); + + // Add another pending revision on the same entity and repeat the checks. + $entity->setNewRevision(TRUE); + $entity->isDefaultRevision(FALSE); + $entity->{$this->figures}->setValue([ + 'color' => 'red', + 'shape' => 'square' + ]); + $entity->save(); + + // A non-revisioned entity query should still return entity 14. + $result = $this->factory->get('entity_test_mulrev') + ->condition('id', [14], 'IN') + ->execute(); + $this->assertCount(1, $result); + $this->assertSame([14 => '14'], $result); + + // Now check an entity query on the latest revision. + $result = $this->factory->get('entity_test_mulrev') + ->condition('id', [14], 'IN') + ->latestRevision() + ->execute(); + $this->assertCount(1, $result); + $this->assertSame([17 => '14'], $result); + + // Verify that field conditions on the default and pending revision still + // work as expected. + $result = $this->factory->get('entity_test_mulrev') + ->condition('id', [14], 'IN') + ->condition("$this->figures.color", $current_values[0]['color']) + ->execute(); + $this->assertSame([14 => '14'], $result); + + // Now there are two revisions with same value for the figure color. + $result = $this->factory->get('entity_test_mulrev') + ->condition('id', [14], 'IN') + ->condition("$this->figures.color", 'red') + ->allRevisions() + ->execute(); + $this->assertSame([16 => '14', 17 => '14'], $result); + + // Check that querying for the latest revision returns the correct one. + $result = $this->factory->get('entity_test_mulrev') + ->condition('id', [14], 'IN') + ->condition("$this->figures.color", 'red') + ->latestRevision() + ->execute(); + $this->assertSame([17 => '14'], $result); } /**