Commit 0c33e863 authored by webchick's avatar webchick

#885014 by bojanz, chx: Fixed add missing pager and tablesort query support to EntityFieldQuery.

parent 4575c982
......@@ -418,6 +418,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.
*
......@@ -461,6 +471,15 @@ class EntityFieldQuery {
*/
public $range = array();
/**
* The query pager data.
*
* @var array
*
* @see EntityFieldQuery::pager()
*/
public $pager = array();
/**
* Query behavior for deleted data.
*
......@@ -798,6 +817,68 @@ public function range($start = NULL, $length = NULL) {
return $this;
}
/**
* 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.
*
......@@ -911,6 +992,10 @@ public function addMetaData($key, $object) {
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 @@ protected function propertyQuery() {
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)));
......@@ -1038,6 +1122,24 @@ protected function propertyQuery() {
return $this->finishQuery($select_query);
}
/**
* 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.
*
......
......@@ -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.
......
......@@ -1104,6 +1104,238 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase {
$this->assertTrue($pass, t('Cannot query across field storage engines.'));
}
/**
* 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.
*
......
......@@ -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';
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment