aboutsummaryrefslogtreecommitdiffstats
path: root/phpBB/includes
diff options
context:
space:
mode:
authorJoas Schilling <nickvergessen@gmx.de>2013-04-16 23:08:35 +0200
committerJoas Schilling <nickvergessen@gmx.de>2013-04-16 23:08:35 +0200
commit0d5efcc1d569b3907eebfacef84ac0be3fd11143 (patch)
tree3782c4532095aad2d5cf3778123a71255ce7876e /phpBB/includes
parenta1183a58894967bfec7da01c5004138e4daeb583 (diff)
downloadforums-0d5efcc1d569b3907eebfacef84ac0be3fd11143.tar
forums-0d5efcc1d569b3907eebfacef84ac0be3fd11143.tar.gz
forums-0d5efcc1d569b3907eebfacef84ac0be3fd11143.tar.bz2
forums-0d5efcc1d569b3907eebfacef84ac0be3fd11143.tar.xz
forums-0d5efcc1d569b3907eebfacef84ac0be3fd11143.zip
[ticket/11495] Add abstract implementation of the interface
PHPBB3-11495
Diffstat (limited to 'phpBB/includes')
-rw-r--r--phpBB/includes/nestedset/base.php632
-rw-r--r--phpBB/includes/nestedset/item/base.php82
2 files changed, 714 insertions, 0 deletions
diff --git a/phpBB/includes/nestedset/base.php b/phpBB/includes/nestedset/base.php
new file mode 100644
index 0000000000..7f4691b7e0
--- /dev/null
+++ b/phpBB/includes/nestedset/base.php
@@ -0,0 +1,632 @@
+<?php
+/**
+*
+* @package Nested Set
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
+{
+ /** @var phpbb_db_driver*/
+ protected $db;
+
+ /** @var String */
+ protected $table_name;
+
+ /** @var String */
+ protected $item_class = 'phpbb_nestedset_item_base';
+
+ /**
+ * Column names in the table
+ * @var array
+ */
+ protected $table_columns = array(
+ 'item_id' => 'item_id',
+ 'left_id' => 'left_id',
+ 'right_id' => 'right_id',
+ 'parent_id' => 'parent_id',
+ 'item_parents' => 'item_parents',
+ );
+
+ /**
+ * Additional SQL restrictions
+ * Allows to have multiple nested sets in one table
+ * @var String
+ */
+ protected $sql_where = '';
+
+ /**
+ * List of item properties to be cached in $item_parents
+ * @var array
+ */
+ protected $item_basic_data = array('*');
+
+ /**
+ * Delete an item from the nested set (also deletes the rows form the table)
+ *
+ * Also deletes all subitems from the nested set
+ *
+ * @param string $operator SQL operator that needs to be prepended to sql_where,
+ * if it is not empty.
+ * @param string $column_prefix Prefix that needs to be prepended to column names
+ * @return bool True if the item was deleted
+ */
+ public function get_sql_where($operator = 'AND', $column_prefix = '')
+ {
+ return (!$this->sql_where) ? '' : $operator . ' ' . sprintf($this->sql_where, $column_prefix);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function insert(array $additional_data)
+ {
+ $item_data = array_merge($additional_data, array(
+ $this->table_columns['parent_id'] => 0,
+ $this->table_columns['left_id'] => 0,
+ $this->table_columns['right_id'] => 0,
+ $this->table_columns['item_parents'] => '',
+ ));
+
+ unset($item_data[$this->table_columns['item_id']]);
+
+ $sql = 'INSERT INTO ' . $this->table_name . ' ' . $this->db->sql_build_array('INSERT', $item_data);
+ $this->db->sql_query($sql);
+
+ $item_data[$this->table_columns['item_id']] = (int) $this->db->sql_nextid();
+
+ $item = new $this->item_class($item_data);
+
+ return array_merge($item_data, $this->add($item));
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function add(phpbb_nestedset_item_interface $item)
+ {
+ $sql = 'SELECT MAX(' . $this->table_columns['right_id'] . ') AS ' . $this->table_columns['right_id'] . '
+ FROM ' . $this->table_name . '
+ ' . $this->get_sql_where('WHERE');
+ $result = $this->db->sql_query($sql);
+ $current_max_right_id = (int) $this->db->sql_fetchfield($this->table_columns['right_id']);
+ $this->db->sql_freeresult($result);
+
+ $update_item_data = array(
+ $this->table_columns['parent_id'] => 0,
+ $this->table_columns['left_id'] => $current_max_right_id + 1,
+ $this->table_columns['right_id'] => $current_max_right_id + 2,
+ $this->table_columns['item_parents'] => '',
+ );
+
+ $sql = 'UPDATE ' . $this->table_name . '
+ SET ' . $this->db->sql_build_array('UPDATE', $update_item_data) . '
+ WHERE ' . $this->table_columns['item_id'] . ' = ' . $item->get_item_id();
+ $this->db->sql_query($sql);
+
+ return $update_item_data;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function remove(phpbb_nestedset_item_interface $item)
+ {
+ if ($item->has_children())
+ {
+ $items = array_keys($this->get_branch_data($item, 'children'));
+ }
+ else
+ {
+ $items = array($item->get_item_id());
+ }
+
+ $this->remove_subset($items, $item);
+
+ return $items;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function delete(phpbb_nestedset_item_interface $item)
+ {
+ $removed_items = $this->remove($item);
+
+ $sql = 'DELETE FROM ' . $this->table_name . '
+ WHERE ' . $this->db->sql_in_set($this->table_columns['item_id'], $removed_items) . '
+ ' . $this->get_sql_where('AND');
+ $this->db->sql_query($sql);
+
+ return $removed_items;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function move(phpbb_nestedset_item_interface $item, $delta)
+ {
+ if ($delta == 0)
+ {
+ return false;
+ }
+
+ $action = ($delta > 0) ? 'move_up' : 'move_down';
+ $delta = abs($delta);
+
+ /**
+ * Fetch all the siblings between the item's current spot
+ * and where we want to move it to. If there are less than $delta
+ * siblings between the current spot and the target then the
+ * item will move as far as possible
+ */
+ $sql = 'SELECT ' . implode(', ', $this->table_columns) . '
+ FROM ' . $this->table_name . '
+ WHERE ' . $this->table_columns['parent_id'] . ' = ' . $item->get_parent_id() . '
+ ' . $this->get_sql_where() . '
+ AND ';
+
+ if ($action == 'move_up')
+ {
+ $sql .= $this->table_columns['right_id'] . ' < ' . $item->get_right_id() . ' ORDER BY ' . $this->table_columns['right_id'] . ' DESC';
+ }
+ else
+ {
+ $sql .= $this->table_columns['left_id'] . ' > ' . $item->get_left_id() . ' ORDER BY ' . $this->table_columns['left_id'] . ' ASC';
+ }
+
+ $result = $this->db->sql_query_limit($sql, $delta);
+
+ $target = null;
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $target = new $this->item_class($row);
+ }
+ $this->db->sql_freeresult($result);
+
+ if (is_null($target))
+ {
+ // The item is already on top or bottom
+ return false;
+ }
+
+ /**
+ * $left_id and $right_id define the scope of the items that are affected by the move.
+ * $diff_up and $diff_down are the values to substract or add to each item's left_id
+ * and right_id in order to move them up or down.
+ * $move_up_left and $move_up_right define the scope of the items that are moving
+ * up. Other items in the scope of ($left_id, $right_id) are considered to move down.
+ */
+ if ($action == 'move_up')
+ {
+ $left_id = $target->get_left_id();
+ $right_id = $item->get_right_id();
+
+ $diff_up = $item->get_left_id() - $target->get_left_id();
+ $diff_down = $item->get_right_id() + 1 - $item->get_left_id();
+
+ $move_up_left = $item->get_left_id();
+ $move_up_right = $item->get_right_id();
+ }
+ else
+ {
+ $left_id = $item->get_left_id();
+ $right_id = $target->get_right_id();
+
+ $diff_up = $item->get_right_id() + 1 - $item->get_left_id();
+ $diff_down = $target->get_right_id() - $item->get_right_id();
+
+ $move_up_left = $item->get_right_id() + 1;
+ $move_up_right = $target->get_right_id();
+ }
+
+ // Now do the dirty job
+ $sql = 'UPDATE ' . $this->table_name . '
+ SET ' . $this->table_columns['left_id'] . ' = ' . $this->table_columns['left_id'] . ' + CASE
+ WHEN ' . $this->table_columns['left_id'] . " BETWEEN {$move_up_left} AND {$move_up_right} THEN -{$diff_up}
+ ELSE {$diff_down}
+ END,
+ " . $this->table_columns['right_id'] . ' = ' . $this->table_columns['right_id'] . ' + CASE
+ WHEN ' . $this->table_columns['right_id'] . " BETWEEN {$move_up_left} AND {$move_up_right} THEN -{$diff_up}
+ ELSE {$diff_down}
+ END,
+ " . $this->table_columns['item_parents'] . " = ''
+ WHERE
+ " . $this->table_columns['left_id'] . " BETWEEN {$left_id} AND {$right_id}
+ AND " . $this->table_columns['right_id'] . " BETWEEN {$left_id} AND {$right_id}
+ " . $this->get_sql_where();
+ $this->db->sql_query($sql);
+
+ return true;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function move_down(phpbb_nestedset_item_interface $item)
+ {
+ return $this->move($item, -1);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function move_up(phpbb_nestedset_item_interface $item)
+ {
+ return $this->move($item, 1);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function move_children(phpbb_nestedset_item_interface $current_parent, phpbb_nestedset_item_interface $new_parent)
+ {
+ if (!$current_parent->has_children() || !$current_parent->get_item_id() || $current_parent->get_item_id() == $new_parent->get_item_id())
+ {
+ return false;
+ }
+
+ $move_items = array_keys($this->get_branch_data($current_parent, 'children', true, false));
+
+ if (in_array($new_parent->get_item_id(), $move_items))
+ {
+ throw new phpbb_nestedset_exception('INVALID_PARENT');
+ }
+
+ $diff = sizeof($move_items) * 2;
+ $sql_exclude_moved_items = $this->db->sql_in_set($this->table_columns['item_id'], $move_items, true);
+
+ $this->db->sql_transaction('begin');
+
+ $this->remove_subset($move_items, $current_parent, false);
+
+ if ($new_parent->get_item_id())
+ {
+ // Retrieve new-parent again, it may have been changed...
+ $sql = 'SELECT *
+ FROM ' . $this->table_name . '
+ WHERE ' . $this->table_columns['item_id'] . ' = ' . $new_parent->get_item_id();
+ $result = $this->db->sql_query($sql);
+ $parent_data = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if (!$parent_data)
+ {
+ $this->db->sql_transaction('rollback');
+ throw new phpbb_nestedset_exception('INVALID_PARENT');
+ }
+
+ $new_parent = new $this->item_class($parent_data);
+
+ $new_right_id = $this->prepare_adding_subset($move_items, $new_parent);
+
+ if ($new_right_id > $current_parent->get_right_id())
+ {
+ $diff = ' + ' . ($new_right_id - $current_parent->get_right_id());
+ }
+ else
+ {
+ $diff = ' - ' . abs($new_right_id - $current_parent->get_right_id());
+ }
+ }
+ else
+ {
+ $sql = 'SELECT MAX(' . $this->table_columns['right_id'] . ') AS ' . $this->table_columns['right_id'] . '
+ FROM ' . $this->table_name . '
+ WHERE ' . $sql_exclude_moved_items . '
+ ' . $this->get_sql_where('AND');
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ $diff = ' + ' . ($row[$this->table_columns['right_id']] - $current_parent->get_left_id());
+ }
+
+ $sql = 'UPDATE ' . $this->table_name . '
+ SET ' . $this->table_columns['left_id'] . ' = ' . $this->table_columns['left_id'] . $diff . ',
+ ' . $this->table_columns['right_id'] . ' = ' . $this->table_columns['right_id'] . $diff . ',
+ ' . $this->table_columns['parent_id'] . ' = ' . $this->db->sql_case($this->table_columns['parent_id'] . ' = ' . $current_parent->get_item_id(), $new_parent->get_item_id(), $this->table_columns['parent_id']) . ',
+ ' . $this->table_columns['item_parents'] . " = ''
+ WHERE " . $this->db->sql_in_set($this->table_columns['item_id'], $move_items) . '
+ ' . $this->get_sql_where('AND');
+ $this->db->sql_query($sql);
+
+ $this->db->sql_transaction('commit');
+
+ return true;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function set_parent(phpbb_nestedset_item_interface $item, phpbb_nestedset_item_interface $new_parent)
+ {
+ $move_items = array_keys($this->get_branch_data($item, 'children'));
+
+ if (in_array($new_parent->get_item_id(), $move_items))
+ {
+ throw new phpbb_nestedset_exception('INVALID_PARENT');
+ }
+
+ $diff = sizeof($move_items) * 2;
+ $sql_exclude_moved_items = $this->db->sql_in_set($this->table_columns['item_id'], $move_items, true);
+
+ $this->db->sql_transaction('begin');
+
+ $this->remove_subset($move_items, $item, false);
+
+ if ($new_parent->get_item_id())
+ {
+ // Retrieve new-parent again, it may have been changed...
+ $sql = 'SELECT *
+ FROM ' . $this->table_name . '
+ WHERE ' . $this->table_columns['item_id'] . ' = ' . $new_parent->get_item_id();
+ $result = $this->db->sql_query($sql);
+ $parent_data = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if (!$parent_data)
+ {
+ $this->db->sql_transaction('rollback');
+ throw new phpbb_nestedset_exception('INVALID_PARENT');
+ }
+
+ $new_parent = new $this->item_class($parent_data);
+
+ $new_right_id = $this->prepare_adding_subset($move_items, $new_parent);
+
+ if ($new_right_id > $item->get_right_id())
+ {
+ $diff = ' + ' . ($new_right_id - $item->get_right_id() - 1);
+ }
+ else
+ {
+ $diff = ' - ' . abs($new_right_id - $item->get_right_id() - 1);
+ }
+ }
+ else
+ {
+ $sql = 'SELECT MAX(' . $this->table_columns['right_id'] . ') AS ' . $this->table_columns['right_id'] . '
+ FROM ' . $this->table_name . '
+ WHERE ' . $sql_exclude_moved_items . '
+ ' . $this->get_sql_where('AND');
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ $diff = ' + ' . ($row[$this->table_columns['right_id']] - $item->get_left_id() + 1);
+ }
+
+ $sql = 'UPDATE ' . $this->table_name . '
+ SET ' . $this->table_columns['left_id'] . ' = ' . $this->table_columns['left_id'] . $diff . ',
+ ' . $this->table_columns['right_id'] . ' = ' . $this->table_columns['right_id'] . $diff . ',
+ ' . $this->table_columns['parent_id'] . ' = ' . $this->db->sql_case($this->table_columns['item_id'] . ' = ' . $item->get_item_id(), $new_parent->get_item_id(), $this->table_columns['parent_id']) . ',
+ ' . $this->table_columns['item_parents'] . " = ''
+ WHERE " . $this->db->sql_in_set($this->table_columns['item_id'], $move_items) . '
+ ' . $this->get_sql_where('AND');
+ $this->db->sql_query($sql);
+
+ $this->db->sql_transaction('commit');
+
+ return true;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function get_branch_data(phpbb_nestedset_item_interface $item, $type = 'all', $order_desc = true, $include_item = true)
+ {
+ switch ($type)
+ {
+ case 'parents':
+ $condition = 'i1.' . $this->table_columns['left_id'] . ' BETWEEN i2.' . $this->table_columns['left_id'] . ' AND i2.' . $this->table_columns['right_id'] . '';
+ break;
+
+ case 'children':
+ $condition = 'i2.' . $this->table_columns['left_id'] . ' BETWEEN i1.' . $this->table_columns['left_id'] . ' AND i1.' . $this->table_columns['right_id'] . '';
+ break;
+
+ default:
+ $condition = 'i2.' . $this->table_columns['left_id'] . ' BETWEEN i1.' . $this->table_columns['left_id'] . ' AND i1.' . $this->table_columns['right_id'] . '
+ OR i1.' . $this->table_columns['left_id'] . ' BETWEEN i2.' . $this->table_columns['left_id'] . ' AND i2.' . $this->table_columns['right_id'];
+ break;
+ }
+
+ $rows = array();
+
+ $sql = 'SELECT i2.*
+ FROM ' . $this->table_name . ' i1
+ LEFT JOIN ' . $this->table_name . " i2
+ ON (($condition) " . $this->get_sql_where('AND', 'i2.') . ')
+ WHERE i1.' . $this->table_columns['item_id'] . ' = ' . $item->get_item_id() . '
+ ' . $this->get_sql_where('AND', 'i1.') . '
+ ORDER BY i2.' . $this->table_columns['left_id'] . ' ' . ($order_desc ? 'ASC' : 'DESC');
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if (!$include_item && $item->get_item_id() === (int) $row[$this->table_columns['item_id']])
+ {
+ continue;
+ }
+
+ $rows[$row[$this->table_columns['item_id']]] = $row;
+ }
+ $this->db->sql_freeresult($result);
+
+ return $rows;
+ }
+
+ /**
+ * Get base information of parent items
+ *
+ * Data is cached in the item_parents column in the item table
+ *
+ * @inheritdoc
+ */
+ public function get_parent_data(phpbb_nestedset_item_interface $item)
+ {
+ $parents = array();
+ if ($item->get_parent_id())
+ {
+ if (!$item->get_item_parents_data())
+ {
+ $sql = 'SELECT ' . implode(', ', $this->item_basic_data) . '
+ FROM ' . $this->table_name . '
+ WHERE ' . $this->table_columns['left_id'] . ' < ' . $item->get_left_id() . '
+ AND ' . $this->table_columns['right_id'] . ' > ' . $item->get_right_id() . '
+ ' . $this->get_sql_where('AND') . '
+ ORDER BY ' . $this->table_columns['left_id'] . ' ASC';
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $parents[$row[$this->table_columns['item_id']]] = $row;
+ }
+ $this->db->sql_freeresult($result);
+
+ $item_parents = serialize($parents);
+
+ $sql = 'UPDATE ' . $this->table_name . '
+ SET ' . $this->table_columns['item_parents'] . " = '" . $this->db->sql_escape($item_parents) . "'
+ WHERE " . $this->table_columns['parent_id'] . ' = ' . $item->get_parent_id();
+ $this->db->sql_query($sql);
+ }
+ else
+ {
+ $parents = unserialize($item->get_item_parents_data());
+ }
+ }
+
+ return $parents;
+ }
+
+ /**
+ * Remove a subset from the nested set
+ *
+ * @param array $subset_items Subset of items to remove
+ * @param phpbb_nestedset_item_interface $bounding_item Item containing the right bound of the subset
+ * @param bool $set_subset_zero Should the parent, left and right id of the item be set to 0, or kept unchanged?
+ * @return null
+ */
+ protected function remove_subset(array $subset_items, phpbb_nestedset_item_interface $bounding_item, $set_subset_zero = true)
+ {
+ $diff = sizeof($subset_items) * 2;
+ $sql_subset_items = $this->db->sql_in_set($this->table_columns['item_id'], $subset_items);
+ $sql_not_subset_items = $this->db->sql_in_set($this->table_columns['item_id'], $subset_items, true);
+
+ $sql_is_parent = $this->table_columns['left_id'] . ' <= ' . $bounding_item->get_right_id() . '
+ AND ' . $this->table_columns['right_id'] . ' >= ' . $bounding_item->get_right_id();
+
+ $sql_is_right = $this->table_columns['left_id'] . ' > ' . $bounding_item->get_right_id();
+
+ $set_left_id = $this->db->sql_case($sql_is_right, $this->table_columns['left_id'] . ' - ' . $diff, $this->table_columns['left_id']);
+ $set_right_id = $this->db->sql_case($sql_is_parent . ' OR ' . $sql_is_right, $this->table_columns['right_id'] . ' - ' . $diff, $this->table_columns['right_id']);
+
+ if ($set_subset_zero)
+ {
+ $set_left_id = $this->db->sql_case($sql_subset_items, 0, $set_left_id);
+ $set_right_id = $this->db->sql_case($sql_subset_items, 0, $set_right_id);
+ }
+
+ $sql = 'UPDATE ' . $this->table_name . '
+ SET ' . $this->table_columns['left_id'] . ' = ' . $set_left_id . ',
+ ' . $this->table_columns['right_id'] . ' = ' . $set_right_id . ',
+ ' . (($set_subset_zero) ? $this->table_columns['parent_id'] . ' = ' . $this->db->sql_case($sql_subset_items, 0, $this->table_columns['parent_id']) . ',' : '') . '
+ ' . $this->table_columns['item_parents'] . " = ''
+ " . ((!$set_subset_zero) ? ' WHERE ' . $sql_not_subset_items . ' ' . $this->get_sql_where('AND') : $this->get_sql_where('WHERE'));
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * Add a subset to the nested set
+ *
+ * @param array $subset_items Subset of items to add
+ * @param phpbb_nestedset_item_interface $new_parent Item containing the right bound of the new parent
+ * @return int New right id of the parent item
+ */
+ protected function prepare_adding_subset(array $subset_items, phpbb_nestedset_item_interface $new_parent)
+ {
+ $diff = sizeof($subset_items) * 2;
+ $sql_not_subset_items = $this->db->sql_in_set($this->table_columns['item_id'], $subset_items, true);
+
+ $set_left_id = $this->db->sql_case($this->table_columns['left_id'] . ' > ' . $new_parent->get_right_id(), $this->table_columns['left_id'] . ' + ' . $diff, $this->table_columns['left_id']);
+ $set_right_id = $this->db->sql_case($this->table_columns['right_id'] . ' >= ' . $new_parent->get_right_id(), $this->table_columns['right_id'] . ' + ' . $diff, $this->table_columns['right_id']);
+
+ $sql = 'UPDATE ' . $this->table_name . '
+ SET ' . $this->table_columns['left_id'] . ' = ' . $set_left_id . ',
+ ' . $this->table_columns['right_id'] . ' = ' . $set_right_id . ',
+ ' . $this->table_columns['item_parents'] . " = ''
+ WHERE " . $sql_not_subset_items . '
+ ' . $this->get_sql_where('AND');
+ $this->db->sql_query($sql);
+
+ return $new_parent->get_right_id() + $diff;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function recalculate_nested_set($new_id, $parent_id = 0, $reset_ids = false)
+ {
+ if ($reset_ids)
+ {
+ $sql = 'UPDATE ' . $this->table_name . '
+ SET ' . $this->db->sql_build_array('UPDATE', array(
+ $this->table_columns['left_id'] => 0,
+ $this->table_columns['right_id'] => 0,
+ $this->table_columns['item_parents'] => '',
+ )) . '
+ ' . $this->get_sql_where('WHERE');
+ $this->db->sql_query($sql);
+ }
+
+ $sql = 'SELECT *
+ FROM ' . $this->table_name . '
+ WHERE ' . $this->table_columns['parent_id'] . ' = ' . (int) $parent_id . '
+ ' . $this->get_sql_where('AND') . '
+ ORDER BY ' . $this->table_columns['left_id'] . ', ' . $this->table_columns['item_id'] . ' ASC';
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ // First we update the left_id for this module
+ if ($row[$this->table_columns['left_id']] != $new_id)
+ {
+ $sql = 'UPDATE ' . $this->table_name . '
+ SET ' . $this->db->sql_build_array('UPDATE', array(
+ $this->table_columns['left_id'] => $new_id,
+ $this->table_columns['item_parents'] => '',
+ )) . '
+ WHERE ' . $this->table_columns['item_id'] . ' = ' . $row[$this->table_columns['item_id']];
+ $this->db->sql_query($sql);
+ }
+ $new_id++;
+
+ // Then we go through any children and update their left/right id's
+ $new_id = $this->recalculate_nested_set($new_id, $row[$this->table_columns['item_id']]);
+
+ // Then we come back and update the right_id for this module
+ if ($row[$this->table_columns['right_id']] != $new_id)
+ {
+ $sql = 'UPDATE ' . $this->table_name . '
+ SET ' . $this->db->sql_build_array('UPDATE', array($this->table_columns['right_id'] => $new_id)) . '
+ WHERE ' . $this->table_columns['item_id'] . ' = ' . $row[$this->table_columns['item_id']];
+ $this->db->sql_query($sql);
+ }
+ $new_id++;
+ }
+ $this->db->sql_freeresult($result);
+
+ return $new_id;
+ }
+}
diff --git a/phpBB/includes/nestedset/item/base.php b/phpBB/includes/nestedset/item/base.php
new file mode 100644
index 0000000000..c3a7600827
--- /dev/null
+++ b/phpBB/includes/nestedset/item/base.php
@@ -0,0 +1,82 @@
+<?php
+/**
+*
+* @package Nested Set
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+abstract class phpbb_nestedset_item_base implements phpbb_nestedset_item_interface
+{
+ /** @var int */
+ protected $item_id;
+
+ /** @var int */
+ protected $parent_id;
+
+ /** @var string */
+ protected $item_parents_data;
+
+ /** @var int */
+ protected $left_id;
+
+ /** @var int */
+ protected $right_id;
+
+ /**
+ * @inheritdoc
+ */
+ public function get_item_id()
+ {
+ return (int) $this->item_id;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function get_parent_id()
+ {
+ return (int) $this->parent_id;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function get_item_parents_data()
+ {
+ return (string) $this->item_parents_data;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function get_left_id()
+ {
+ return (int) $this->left_id;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function get_right_id()
+ {
+ return (int) $this->right_id;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function has_children()
+ {
+ return $this->right_id - $this->left_id > 1;
+ }
+}