summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--includes/entity.inc104
-rw-r--r--includes/pager.inc2
-rw-r--r--modules/simpletest/tests/entity_query.test232
-rw-r--r--modules/system/system.api.php7
4 files changed, 343 insertions, 2 deletions
diff --git a/includes/entity.inc b/includes/entity.inc
index 7f9a78da7..1c3e7494e 100644
--- a/includes/entity.inc
+++ b/includes/entity.inc
@@ -419,6 +419,16 @@ class EntityFieldQuery {
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
@@ -462,6 +472,15 @@ class EntityFieldQuery {
public $range = array();
/**
+ * The query pager data.
+ *
+ * @var array
+ *
+ * @see EntityFieldQuery::pager()
+ */
+ public $pager = array();
+
+ /**
* Query behavior for deleted data.
*
* TRUE to return only deleted data, FALSE to return only non-deleted data,
@@ -799,6 +818,68 @@ class EntityFieldQuery {
}
/**
+ * Enable 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 EntityFieldQuery
+ * The called object.
+ */
+ public function pager($limit = 10, $element = NULL) {
+ if (!isset($element)) {
+ $element = PagerDefault::$maxElement++;
+ }
+ elseif ($element >= PagerDefault::$maxElement) {
+ PagerDefault::$maxElement = $element + 1;
+ }
+
+ $this->pager = array(
+ 'limit' => $limit,
+ 'element' => $element,
+ );
+ return $this;
+ }
+
+ /**
+ * Enable sortable tables for this query.
+ *
+ * @param $headers
+ * An EFQ Header array based on which the order clause is added to the query.
+ *
+ * @return 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
@@ -911,6 +992,10 @@ class EntityFieldQuery {
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);
@@ -966,7 +1051,6 @@ class EntityFieldQuery {
throw new EntityFieldQueryException(t('For this query an entity type must be specified.'));
}
$entity_type = $this->entityConditions['entity_type']['value'];
- unset($this->entityConditions['entity_type']);
$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)));
@@ -1039,6 +1123,24 @@ class EntityFieldQuery {
}
/**
+ * Get the total number of results and initialize a pager for the query.
+ *
+ * This query can be detected by checking for ($this->pager && $this->count),
+ * which allows a driver to return 0 from the count query and disable
+ * the pager.
+ */
+ function initializePager() {
+ if ($this->pager && !$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.
diff --git a/includes/pager.inc b/includes/pager.inc
index 0e417f656..69c2759b3 100644
--- a/includes/pager.inc
+++ b/includes/pager.inc
@@ -20,7 +20,7 @@ class PagerDefault extends SelectQueryExtender {
*
* @var int
*/
- static protected $maxElement = 0;
+ static $maxElement = 0;
/**
* The number of elements per page to allow.
diff --git a/modules/simpletest/tests/entity_query.test b/modules/simpletest/tests/entity_query.test
index 7aa4963ab..4a319d7f6 100644
--- a/modules/simpletest/tests/entity_query.test
+++ b/modules/simpletest/tests/entity_query.test
@@ -1105,6 +1105,238 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase {
}
/**
+ * 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),
+ ), t('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),
+ ), t('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),
+ ), t('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),
+ ), t('Test pager integration in field storage: page 2.'), TRUE);
+
+ unset($_GET['page']);
+ }
+
+ /**
+ * 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),
+ ), t('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),
+ ), t('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),
+ ), t('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),
+ ), t('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),
+ ), t('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),
+ ), t('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),
+ ), t('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),
+ ), t('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),
+ ), t('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),
+ ), t('Test TableSort by field DESC.'), TRUE);
+
+ unset($_GET['sort']);
+ unset($_GET['order']);
+ }
+
+ /**
* Fetches the results of an EntityFieldQuery and compares.
*
* @param $query
diff --git a/modules/system/system.api.php b/modules/system/system.api.php
index e7a5f06b6..8a67cddff 100644
--- a/modules/system/system.api.php
+++ b/modules/system/system.api.php
@@ -350,6 +350,13 @@ function hook_entity_delete($entity, $type) {
* 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.
+ *
+ * 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
+ * all elements. This query can be detected by checking for
+ * ($query->pager && $query->count), allowing the driver to return 0 from
+ * the count query and disable the pager.
*/
function hook_entity_query_alter($query) {
$query->executeCallback = 'my_module_query_callback';