summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/lib/Drupal/Core/Config/Entity/Query/Query.php1
-rw-r--r--core/lib/Drupal/Core/Entity/Query/QueryBase.php22
-rw-r--r--core/lib/Drupal/Core/Entity/Query/QueryInterface.php33
-rw-r--r--core/lib/Drupal/Core/Entity/Query/Sql/Query.php14
-rw-r--r--core/lib/Drupal/Core/Entity/Query/Sql/Tables.php11
-rw-r--r--core/modules/system/src/Tests/Entity/EntityQueryTest.php78
-rw-r--r--core/tests/Drupal/Tests/Core/Entity/Query/Sql/QueryTest.php64
7 files changed, 173 insertions, 50 deletions
diff --git a/core/lib/Drupal/Core/Config/Entity/Query/Query.php b/core/lib/Drupal/Core/Config/Entity/Query/Query.php
index b8660aa..2ad4db5 100644
--- a/core/lib/Drupal/Core/Config/Entity/Query/Query.php
+++ b/core/lib/Drupal/Core/Config/Entity/Query/Query.php
@@ -8,7 +8,6 @@
namespace Drupal\Core\Config\Entity\Query;
use Drupal\Core\Config\ConfigFactoryInterface;
-use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Query\QueryBase;
use Drupal\Core\Entity\Query\QueryInterface;
diff --git a/core/lib/Drupal/Core/Entity/Query/QueryBase.php b/core/lib/Drupal/Core/Entity/Query/QueryBase.php
index 4465efd..6f3d6aa 100644
--- a/core/lib/Drupal/Core/Entity/Query/QueryBase.php
+++ b/core/lib/Drupal/Core/Entity/Query/QueryBase.php
@@ -8,7 +8,6 @@
namespace Drupal\Core\Entity\Query;
use Drupal\Core\Database\Query\PagerSelectExtender;
-use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
/**
@@ -110,12 +109,9 @@ abstract class QueryBase implements QueryInterface {
/**
* Flag indicating whether to query the current revision or all revisions.
*
- * Can be either EntityStorageInterface::FIELD_LOAD_CURRENT or
- * EntityStorageInterface::FIELD_LOAD_REVISION.
- *
- * @var string
+ * @var bool
*/
- protected $age = EntityStorageInterface::FIELD_LOAD_CURRENT;
+ protected $allRevisions = FALSE;
/**
* The query pager data.
@@ -257,10 +253,18 @@ abstract class QueryBase implements QueryInterface {
}
/**
- * Implements \Drupal\Core\Entity\Query\QueryInterface::age().
+ * {@inheritdoc}
+ */
+ public function currentRevision() {
+ $this->allRevisions = FALSE;
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
*/
- public function age($age = EntityStorageInterface::FIELD_LOAD_CURRENT) {
- $this->age = $age;
+ public function allRevisions() {
+ $this->allRevisions = TRUE;
return $this;
}
diff --git a/core/lib/Drupal/Core/Entity/Query/QueryInterface.php b/core/lib/Drupal/Core/Entity/Query/QueryInterface.php
index 0fad28c..2ddbd5d 100644
--- a/core/lib/Drupal/Core/Entity/Query/QueryInterface.php
+++ b/core/lib/Drupal/Core/Entity/Query/QueryInterface.php
@@ -7,7 +7,6 @@
namespace Drupal\Core\Entity\Query;
-use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Database\Query\AlterableInterface;
/**
@@ -166,24 +165,6 @@ interface QueryInterface extends AlterableInterface {
public function accessCheck($access_check = TRUE);
/**
- * 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
- * - EntityStorageInterface::FIELD_LOAD_CURRENT (default): Query
- * the most recent revisions only,
- * - EntityStorageInterface::FIELD_LOAD_REVISION: Query all
- * revisions.
- *
- * @return \Drupal\Core\Entity\Query\QueryInterface
- * The called object.
- */
- public function age($age = EntityStorageInterface::FIELD_LOAD_CURRENT);
-
- /**
* Execute the query.
*
* @return int|array
@@ -244,4 +225,18 @@ interface QueryInterface extends AlterableInterface {
*/
public function orConditionGroup();
+ /**
+ * Queries the current revision.
+ *
+ * @return $this
+ */
+ public function currentRevision();
+
+ /**
+ * Queries all the revisions.
+ *
+ * @return $this
+ */
+ public function allRevisions();
+
}
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Query.php b/core/lib/Drupal/Core/Entity/Query/Sql/Query.php
index cf8eb39..6269617 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/Query.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/Query.php
@@ -9,7 +9,6 @@ namespace Drupal\Core\Entity\Query\Sql;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Query\SelectInterface;
-use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Query\QueryBase;
use Drupal\Core\Entity\Query\QueryException;
@@ -100,8 +99,15 @@ class Query extends QueryBase implements QueryInterface {
* Returns the called object.
*/
protected function prepare() {
- if (!$base_table = $this->entityType->getBaseTable()) {
- throw new QueryException("No base table, invalid query.");
+ if ($this->allRevisions) {
+ if (!$base_table = $this->entityType->getRevisionTable()) {
+ throw new QueryException("No revision table for " . $this->entityTypeId . ", invalid query.");
+ }
+ }
+ else {
+ if (!$base_table = $this->entityType->getBaseTable()) {
+ throw new QueryException("No base table for " . $this->entityTypeId . ", invalid query.");
+ }
}
$simple_query = TRUE;
if ($this->entityType->getDataTable()) {
@@ -146,7 +152,7 @@ class Query extends QueryBase implements QueryInterface {
}
// This now contains first the table containing entity properties and
// last the entity base table. They might be the same.
- $this->sqlQuery->addMetaData('age', $this->age);
+ $this->sqlQuery->addMetaData('all_revisions', $this->allRevisions);
$this->sqlQuery->addMetaData('simple_query', $simple_query);
return $this;
}
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
index 6e1ecf1..31925b4 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
@@ -8,7 +8,6 @@
namespace Drupal\Core\Entity\Query\Sql;
use Drupal\Core\Database\Query\SelectInterface;
-use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\Query\QueryException;
use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
@@ -68,7 +67,7 @@ class Tables implements TablesInterface {
*/
public function addField($field, $type, $langcode) {
$entity_type_id = $this->sqlQuery->getMetaData('entity_type');
- $age = $this->sqlQuery->getMetaData('age');
+ $all_revisions = $this->sqlQuery->getMetaData('all_revisions');
// This variable ensures grouping works correctly. For example:
// ->condition('tags', 2, '>')
// ->condition('tags', 20, '<')
@@ -89,7 +88,7 @@ class Tables implements TablesInterface {
for ($key = 0; $key <= $count; $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 (($revision_key = $entity_type->getKey('revision')) && $age == EntityStorageInterface::FIELD_LOAD_CURRENT) {
+ if (($revision_key = $entity_type->getKey('revision')) && $all_revisions) {
// This contains the relevant SQL field to be used when joining entity
// tables.
$entity_id_field = $revision_key;
@@ -158,11 +157,11 @@ class Tables implements TablesInterface {
// finds the property first. The data table is preferred, which is why
// it gets added before the base table.
$entity_tables = array();
- if ($data_table = $entity_type->getDataTable()) {
+ if ($data_table = $all_revisions ? $entity_type->getRevisionDataTable() : $entity_type->getDataTable()) {
$this->sqlQuery->addMetaData('simple_query', FALSE);
$entity_tables[$data_table] = $this->getTableMapping($data_table, $entity_type_id);
}
- $entity_base_table = $entity_type->getBaseTable();
+ $entity_base_table = $all_revisions ? $entity_type->getRevisionTable() : $entity_type->getBaseTable();
$entity_tables[$entity_base_table] = $this->getTableMapping($entity_base_table, $entity_type_id);
$sql_column = $specifier;
@@ -265,7 +264,7 @@ class Tables implements TablesInterface {
$entity_type_id = $this->sqlQuery->getMetaData('entity_type');
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
$table_mapping = $this->entityManager->getStorage($entity_type_id)->getTableMapping();
- $table = $this->sqlQuery->getMetaData('age') == EntityStorageInterface::FIELD_LOAD_CURRENT ? $table_mapping->getDedicatedDataTableName($field) : $table_mapping->getDedicatedRevisionTableName($field);
+ $table = !$this->sqlQuery->getMetaData('all_revisions') ? $table_mapping->getDedicatedDataTableName($field) : $table_mapping->getDedicatedRevisionTableName($field);
if ($field->getCardinality() != 1) {
$this->sqlQuery->addMetaData('simple_query', FALSE);
}
diff --git a/core/modules/system/src/Tests/Entity/EntityQueryTest.php b/core/modules/system/src/Tests/Entity/EntityQueryTest.php
index f66ab4e..a46eb14 100644
--- a/core/modules/system/src/Tests/Entity/EntityQueryTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityQueryTest.php
@@ -8,7 +8,6 @@
namespace Drupal\system\Tests\Entity;
use Drupal\Component\Utility\Unicode;
-use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\entity_test\Entity\EntityTestMulRev;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
@@ -225,11 +224,26 @@ class EntityQueryTest extends EntityUnitTestBase {
->sort('id')
->execute();
$entities = entity_load_multiple('entity_test_mulrev', $ids);
+ $first_entity = reset($entities);
+ $old_name = $first_entity->name->value;
foreach ($entities as $entity) {
$entity->setNewRevision();
$entity->getTranslation('tr')->$greetings->value = 'xsiemax';
+ $entity->name->value .= 'x';
$entity->save();
}
+ // We changed the entity names, so the current revision should not match.
+ $this->queryResults = $this->factory->get('entity_test_mulrev')
+ ->condition('name.value', $old_name)
+ ->execute();
+ $this->assertResult();
+ // Only if all revisions are queried, we find the old revision.
+ $this->queryResults = $this->factory->get('entity_test_mulrev')
+ ->condition('name.value', $old_name)
+ ->allRevisions()
+ ->sort('revision_id')
+ ->execute();
+ $this->assertRevisionResult(array($first_entity->id()), array($first_entity->id()));
// When querying current revisions, this string is no longer found.
$this->queryResults = $this->factory->get('entity_test_mulrev')
->condition("$greetings.value", 'merhaba')
@@ -237,21 +251,18 @@ class EntityQueryTest extends EntityUnitTestBase {
$this->assertResult();
$this->queryResults = $this->factory->get('entity_test_mulrev')
->condition("$greetings.value", 'merhaba')
- ->age(EntityStorageInterface::FIELD_LOAD_REVISION)
+ ->allRevisions()
->sort('revision_id')
->execute();
- // Bit 2 needs to be set.
- // The keys must be 16-23 because the first batch stopped at 15 so the
- // second started at 16 and eight entities were saved.
- $assert = $this->assertRevisionResult(range(16, 23), array(4, 5, 6, 7, 12, 13, 14, 15));
+ // The query only matches the original revisions.
+ $this->assertRevisionResult(array(4, 5, 6, 7, 12, 13, 14, 15), array(4, 5, 6, 7, 12, 13, 14, 15));
$results = $this->factory->get('entity_test_mulrev')
->condition("$greetings.value", 'siema', 'CONTAINS')
->sort('id')
->execute();
- // This is the same as the previous one because xsiemax replaced merhaba
- // but also it contains the entities that siema originally but not
- // merhaba.
- $assert = array_slice($assert, 0, 4, TRUE) + array(8 => '8', 9 => '9', 10 => '10', 11 => '11') + array_slice($assert, 4, 4, TRUE);
+ // This matches both the original and new current revisions, multiple
+ // revisions are returned for some entities.
+ $assert = array(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->assertIdentical($results, $assert);
$results = $this->factory->get('entity_test_mulrev')
->condition("$greetings.value", 'siema', 'STARTS_WITH')
@@ -269,10 +280,12 @@ class EntityQueryTest extends EntityUnitTestBase {
$this->assertIdentical($results, array_slice($assert, 4, 8, TRUE));
$results = $this->factory->get('entity_test_mulrev')
->condition("$greetings.value", 'a', 'ENDS_WITH')
- ->age(EntityStorageInterface::FIELD_LOAD_REVISION)
+ ->allRevisions()
->sort('id')
+ ->sort('revision_id')
->execute();
// Now we get everything.
+ $assert = array(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);
}
@@ -706,4 +719,47 @@ class EntityQueryTest extends EntityUnitTestBase {
$this->assertEqual($term1->id(), reset($ids));
}
+ /**
+ * Test forward-revisions.
+ */
+ public function testForwardRevisions() {
+ // Ensure entity 14 is returned.
+ $result = \Drupal::entityQuery('entity_test_mulrev')
+ ->condition('id', [14], 'IN')
+ ->execute();
+ $this->assertEqual(count($result), 1);
+
+ // Set a revision on entity 14 that isn't the current default.
+ $entity = EntityTestMulRev::load(14);
+ $current_values = $entity->{$this->figures}->getValue();
+
+ $entity->setNewRevision(TRUE);
+ $entity->isDefaultRevision(FALSE);
+ $entity->{$this->figures}->setValue([
+ 'color' => 'red',
+ 'shape' => 'square'
+ ]);
+ $entity->save();
+
+ // Entity query should still return entity 14.
+ $result = \Drupal::entityQuery('entity_test_mulrev')
+ ->condition('id', [14], 'IN')
+ ->execute();
+ $this->assertEqual(count($result), 1);
+
+ // Verify that field conditions on the default and forward revision are
+ // work as expected.
+ $result = \Drupal::entityQuery('entity_test_mulrev')
+ ->condition('id', [14], 'IN')
+ ->condition("$this->figures.color", $current_values[0]['color'])
+ ->execute();
+ $this->assertEqual($result, [14 => '14']);
+ $result = $this->factory->get('entity_test_mulrev')
+ ->condition('id', [14], 'IN')
+ ->condition("$this->figures.color", 'red')
+ ->allRevisions()
+ ->execute();
+ $this->assertEqual($result, [16 => '14']);
+ }
+
}
diff --git a/core/tests/Drupal/Tests/Core/Entity/Query/Sql/QueryTest.php b/core/tests/Drupal/Tests/Core/Entity/Query/Sql/QueryTest.php
new file mode 100644
index 0000000..1a05383
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Entity/Query/Sql/QueryTest.php
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Entity\Query\Sql\QueryTest.
+ */
+
+namespace Drupal\Tests\Core\Entity\Query\Sql;
+
+use Drupal\Core\Entity\EntityType;
+use Drupal\Tests\UnitTestCase;
+use Drupal\Core\Entity\Query\Sql\Query;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Entity\Query\Sql\Query
+ * @group Entity
+ */
+class QueryTest extends UnitTestCase {
+
+ /**
+ * The query object.
+ *
+ * @var \Drupal\Core\Entity\Query\Sql\Query
+ */
+ protected $query;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+ $entity_type = new EntityType(['id' => 'example_entity_query']);
+ $conjunction = 'AND';
+ $connection = $this->getMockBuilder('Drupal\Core\Database\Connection')->disableOriginalConstructor()->getMock();
+ $namespaces = ['Drupal\Core\Entity\Query\Sql'];
+
+ $this->query = new Query($entity_type, $conjunction, $connection, $namespaces);
+ }
+
+ /**
+ * Tests entity query for entity type without base table.
+ *
+ * @covers ::prepare
+ *
+ * @expectedException \Drupal\Core\Entity\Query\QueryException
+ * @expectedExceptionMessage No base table for example_entity_query, invalid query.
+ */
+ public function testNoBaseTable() {
+ $this->query->execute();
+ }
+
+ /**
+ * Tests revision entity query for entity type without revision table.
+ *
+ * @covers ::prepare
+ *
+ * @expectedException \Drupal\Core\Entity\Query\QueryException
+ * @expectedExceptionMessage No revision table for example_entity_query, invalid query.
+ */
+ public function testNoRevisionTable() {
+ $this->query->allRevisions()->execute();
+ }
+
+}