mirror of
https://github.com/e107inc/e107.git
synced 2025-08-03 13:17:24 +02:00
Merge pull request #3031 from Deltik/fix-3029
Implemented custom sorting for e_tree_model
This commit is contained in:
13
e107_handlers/admin_ui.php
Normal file → Executable file
13
e107_handlers/admin_ui.php
Normal file → Executable file
@@ -6167,7 +6167,7 @@ class e_admin_ui extends e_admin_controller_ui
|
||||
$this->_model = new e_admin_model();
|
||||
$this->_model->setModelTable($this->table)
|
||||
->setFieldIdName($this->pid)
|
||||
->setUrl($this->url)
|
||||
->setUrl($this->url)
|
||||
->setValidationRules($this->validationRules)
|
||||
->setDbTypes($this->fieldTypes)
|
||||
->setFieldInputTypes($this->fieldInputTypes)
|
||||
@@ -6188,10 +6188,10 @@ class e_admin_ui extends e_admin_controller_ui
|
||||
$this->_tree_model = new e_admin_tree_model();
|
||||
$this->_tree_model->setModelTable($this->table)
|
||||
->setFieldIdName($this->pid)
|
||||
->setUrl($this->url)
|
||||
->setUrl($this->url)
|
||||
->setMessageStackName('admin_ui_tree_'.$this->table)
|
||||
->setParams(array('model_class' => 'e_admin_model',
|
||||
'model_message_stack' => 'admin_ui_model_'.$this->table ,
|
||||
'model_message_stack' => 'admin_ui_model_'.$this->table,
|
||||
'db_query' => $this->listQry,
|
||||
// Information necessary for PHP-based tree sort
|
||||
'sort_parent' => $this->getSortParent(),
|
||||
@@ -6484,9 +6484,6 @@ class e_admin_form_ui extends e_form
|
||||
// if going through confirm screen - no JS confirm
|
||||
$controller->setFieldAttr('options', 'noConfirm', $controller->deleteConfirmScreen);
|
||||
|
||||
$this->listTotal = $tree[$id]->getTotal();
|
||||
|
||||
|
||||
$fields = $controller->getFields();
|
||||
|
||||
// checks dispatcher acess/perms for create/edit/delete access in list mode.
|
||||
@@ -6916,7 +6913,7 @@ class e_admin_form_ui extends e_form
|
||||
|
||||
|
||||
|
||||
// FIXME - use e_form::batchoptions(), nice way of buildig batch dropdown - news administration show_batch_options()
|
||||
// FIXME - use e_form::batchoptions(), nice way of building batch dropdown - news administration show_batch_options()
|
||||
|
||||
/**
|
||||
* @param array $options array of flags for copy, delete, url, featurebox, batch
|
||||
@@ -7017,7 +7014,7 @@ class e_admin_form_ui extends e_form
|
||||
|
||||
$text .= "
|
||||
|
||||
<div id='admin-ui-list-total-records' class='span6 col-md-6 right'><span>".e107::getParser()->lanVars(LAN_UI_TOTAL_RECORDS,number_format($this->listTotal))."</span></div>
|
||||
<div id='admin-ui-list-total-records' class='span6 col-md-6 right'><span>".e107::getParser()->lanVars(LAN_UI_TOTAL_RECORDS,number_format($this->getController()->getTreeModel()->getTotal()))."</span></div>
|
||||
</div>
|
||||
";
|
||||
|
||||
|
105
e107_handlers/model_class.php
Normal file → Executable file
105
e107_handlers/model_class.php
Normal file → Executable file
@@ -3339,14 +3339,17 @@ class e_tree_model extends e_front_model
|
||||
|
||||
// Workaround: Parse and modify db_query param for simulated pagination
|
||||
$this->prepareSimulatedPagination();
|
||||
// Workaround: Parse and modify db_query param for simulated custom ordering
|
||||
$this->prepareSimulatedCustomOrdering();
|
||||
|
||||
if($sql->gen($this->getParam('db_query'), $this->getParam('db_debug') ? true : false))
|
||||
{
|
||||
$rows = self::flatTreeFromArray($sql->rows(),
|
||||
$this->getParam('primary_field'),
|
||||
$this->getParam('sort_parent'),
|
||||
$this->getParam('sort_field')
|
||||
);
|
||||
$rows_tree = self::arrayToTree($sql->rows,
|
||||
$this->getParam('primary_field'),
|
||||
$this->getParam('sort_parent'));
|
||||
$rows = self::flattenTree($rows_tree,
|
||||
$this->getParam('sort_field'),
|
||||
$this->getParam('sort_order'));
|
||||
|
||||
// Simulated pagination
|
||||
$rows = array_splice($rows,
|
||||
@@ -3384,20 +3387,6 @@ class e_tree_model extends e_front_model
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Depth-first sort a relational array with a parent field and a sort order field
|
||||
* @param array $rows Relational array with a parent field and a sort order field
|
||||
* @param string $primary_field The field name of the primary key (matches children to parents)
|
||||
* @param string $sort_parent The field name whose value is the parent ID
|
||||
* @param string $sort_field The field name whose value is the sort order in the current tree node
|
||||
* @return array Input array sorted depth-first as if it were a tree
|
||||
*/
|
||||
private static function flatTreeFromArray($rows, $primary_field, $sort_parent, $sort_field)
|
||||
{
|
||||
$rows_tree = self::arrayToTree($rows, $primary_field, $sort_parent);
|
||||
return self::flattenTree($rows_tree, $sort_field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a relational array with a parent field and a sort order field to a tree
|
||||
* @param array $rows Relational array with a parent field and a sort order field
|
||||
@@ -3448,11 +3437,13 @@ class e_tree_model extends e_front_model
|
||||
/**
|
||||
* Flattens a tree into a depth-first array, sorting each node by a field's values
|
||||
* @param array $tree Tree with child nodes under the "_children" key
|
||||
* @param string $sort_field The field name whose value is the sort order in the current tree node
|
||||
* @param mixed $sort_field The field name (string) or field names (array) whose value
|
||||
* is or values are the sort order in the current tree node
|
||||
* @param int $sort_order Desired sorting direction: 1 if ascending, -1 if descending
|
||||
* @param int $depth The depth that this level of recursion is entering
|
||||
* @return array One-dimensional array in depth-first order with depth indicated by the "_depth" key
|
||||
*/
|
||||
private static function flattenTree($tree, $sort_field = null, $depth = 0)
|
||||
private static function flattenTree($tree, $sort_field = null, $sort_order = 1, $depth = 0)
|
||||
{
|
||||
$flat = array();
|
||||
|
||||
@@ -3465,18 +3456,40 @@ class e_tree_model extends e_front_model
|
||||
$flat[] = $item;
|
||||
if(is_array($children))
|
||||
{
|
||||
uasort($children, function($node1, $node2)
|
||||
uasort($children, function($node1, $node2) use ($sort_field, $sort_order)
|
||||
{
|
||||
if(intval($node1[$sort_field]) === intval($node2[$sort_field])) return 0;
|
||||
return intval($node1[$sort_field]) < intval($node2[$sort_field]) ? -1 : 1;
|
||||
return self::multiFieldCmp($node1, $node2, $sort_field, $sort_order);
|
||||
});
|
||||
$flat = array_merge($flat, self::flattenTree($children, $sort_field, $depth+1));
|
||||
$flat = array_merge($flat, self::flattenTree($children, $sort_field, $sort_order, $depth+1));
|
||||
}
|
||||
}
|
||||
|
||||
return $flat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Naturally compares two associative arrays given multiple sort keys and a reverse order flag
|
||||
* @param array $row1 Associative array to compare to $row2
|
||||
* @param array $row2 Associative array to compare to $row1
|
||||
* @param mixed $sort_field Key (string) or keys (array) to compare
|
||||
* the values of in both $row1 and $row2
|
||||
* @param int $sort_order -1 to reverse the sorting order or 1 to keep the order as ascending
|
||||
* @return int -1 if $row1 is less than $row2
|
||||
* 0 if $row1 is equal to $row2
|
||||
* 1 if $row1 is greater than $row2
|
||||
*/
|
||||
private static function multiFieldCmp($row1, $row2, $sort_field, $sort_order = 1)
|
||||
{
|
||||
if (is_array($sort_field))
|
||||
$field = array_shift($sort_field);
|
||||
$cmp = strnatcmp((string) $row1[$field], (string) $row2[$field]);
|
||||
if ($sort_order === -1 || $sort_order === 1) $cmp *= $sort_order;
|
||||
if ($cmp === 0 && count($sort_field) >= 1)
|
||||
return self::multiFieldCmp($row1, $row2, $sort_field, $sort_order);
|
||||
return $cmp;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Resiliently counts the results from the last SQL query in the given resource
|
||||
*
|
||||
@@ -3516,13 +3529,13 @@ class e_tree_model extends e_front_model
|
||||
private function prepareSimulatedPagination()
|
||||
{
|
||||
$db_query = $this->getParam('db_query');
|
||||
$db_query = preg_replace_callback("/LIMIT ([\d]+)[ ]*(,|OFFSET){0,1}[ ]*([\d]*)/", function($matches)
|
||||
$db_query = preg_replace_callback("/LIMIT ([\d]+)[ ]*(?:,|OFFSET){0,1}[ ]*([\d]*)/i", function($matches)
|
||||
{
|
||||
// Offset and count
|
||||
if (isset($matches[3]))
|
||||
if (isset($matches[2]))
|
||||
{
|
||||
$this->setParam('db_limit_offset', $matches[1]);
|
||||
$this->setParam('db_limit_count', $matches[3]);
|
||||
$this->setParam('db_limit_count', $matches[2]);
|
||||
}
|
||||
// Count only
|
||||
else
|
||||
@@ -3535,6 +3548,42 @@ class e_tree_model extends e_front_model
|
||||
$this->setParam('db_query', $db_query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Workaround: Parse and modify query to prepare for simulation of custom ordering
|
||||
*
|
||||
* XXX: Not compliant with all forms of ORDER BY clauses
|
||||
* XXX: Does not support quoted identifiers (`identifier`)
|
||||
* XXX: Does not support mixed sort orders (identifier1 ASC, identifier2 DESC)
|
||||
*
|
||||
* This is a hack to enable custom ordering of tree models when
|
||||
* flattening the tree.
|
||||
*
|
||||
* Implemented out of necessity under
|
||||
* https://github.com/e107inc/e107/issues/3029
|
||||
*
|
||||
* @returns null
|
||||
*/
|
||||
private function prepareSimulatedCustomOrdering()
|
||||
{
|
||||
$db_query = $this->getParam('db_query');
|
||||
$db_query = preg_replace_callback('/ORDER BY (?:.+\.)*[\.]*([A-Za-z0-9$_,]+)[ ]*(ASC|DESC)*/i', function($matches)
|
||||
{
|
||||
if (isset($matches[1]))
|
||||
{
|
||||
$current_sort_field = $this->getParam('sort_field');
|
||||
if (!empty($current_sort_field))
|
||||
{
|
||||
$matches[1] = $current_sort_field.",".$matches[1];
|
||||
}
|
||||
$this->setParam('sort_field', array_map('trim', explode(',', $matches[1])));
|
||||
}
|
||||
if (isset($matches[2]))
|
||||
$this->setParam('sort_order',
|
||||
(0 === strcasecmp($matches[2], 'DESC') ? -1 : 1)
|
||||
);
|
||||
}, $db_query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get single model instance from the collection
|
||||
* @param integer $node_id
|
||||
|
Reference in New Issue
Block a user