summaryrefslogtreecommitdiff
path: root/core/libraries/ORM_MPTT.php
diff options
context:
space:
mode:
Diffstat (limited to 'core/libraries/ORM_MPTT.php')
-rw-r--r--core/libraries/ORM_MPTT.php142
1 files changed, 142 insertions, 0 deletions
diff --git a/core/libraries/ORM_MPTT.php b/core/libraries/ORM_MPTT.php
new file mode 100644
index 00000000..6d3478ad
--- /dev/null
+++ b/core/libraries/ORM_MPTT.php
@@ -0,0 +1,142 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2008 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+/**
+ * Implement Modified Preorder Tree Traversal on top of ORM.
+ *
+ * MPTT is an efficient way to store and retrieve hierarchical data in a single database table.
+ * For a good description, read http://www.sitepoint.com/article/hierarchical-data-database/3/
+ *
+ * This code was heavily influenced by code from:
+ * - http://code.google.com/p/kohana-mptt/
+ * - http://code.google.com/p/kohana-mptt/wiki/Documentation
+ * - http://code.google.com/p/s7ncms/source/browse/trunk/modules/s7ncms/libraries/ORM_MPTT.php
+ *
+ * Unfortunately that code was not ready for production and I did not want to absorb their code
+ * and licensing issues so I've reimplemented just the features that we need.
+ */
+class ORM_MPTT_Core extends ORM {
+ private $model_name = null;
+ private $parent = null;
+ private $parents = null;
+ private $children = null;
+
+ function __construct($id=null) {
+ parent::__construct($id);
+ $this->model_name = inflector::singular($this->table_name);
+ }
+
+ /**
+ * Add this node as a child of the parent provided.
+ *
+ * @param integer $parent_id the id of the parent node
+ * @return ORM
+ */
+ function add_to_parent($parent_id) {
+ $this->_lock();
+
+ try {
+ $parent = ORM::factory($this->model_name, $parent_id);
+ $parent->_grow();
+ $this->left = $parent->right - 2;
+ $this->right = $parent->right - 1;
+ $this->parent_id = $parent->id;
+ $this->level = $parent->level + 1;
+ $this->save();
+ } catch (Exception $e) {
+ $this->_unlock();
+ throw $e;
+ }
+
+ $this->_unlock();
+ return $this;
+ }
+
+ /**
+ * Return the parent of this node
+ *
+ * @return ORM
+ */
+ function parent() {
+ if (!isset($this->parent)) {
+ $this->parent =
+ ORM::factory($this->model_name)->where("id", $this->parent_id)->find();
+ }
+ return $this->parent;
+ }
+
+ /**
+ * Return all the parents of this node, in order from root to leaf.
+ *
+ * @return array ORM
+ */
+ function parents() {
+ if (!isset($this->parents)) {
+ $this->parents = $this->where("`left` <= {$this->left}")
+ ->where("`right` >= {$this->right}")
+ ->orderby("left", "ASC")
+ ->find_all();
+ }
+ return $this->parents;
+ }
+
+ /**
+ * Return all of the children of this node, unordered.
+ *
+ * @return array ORM
+ */
+ function children() {
+ if (!isset($this->children)) {
+ $this->children =
+ $this->where("parent_id", $this->id)->find_all();
+ }
+ return $this->children;
+ }
+
+ /**
+ * Grow this node's space enough to make room for 1 or more new nodes.
+ *
+ * @param integer $count the number of new nodes to add
+ */
+ private function _grow($count=1) {
+ $size = $count * 2;
+ $this->db->query(
+ "UPDATE `{$this->table_name}` SET `left` = `left` + $size WHERE `left` >= {$this->right}");
+ $this->db->query(
+ "UPDATE `{$this->table_name}` SET `right` = `right` + $size WHERE `right` >= {$this->right}");
+ $this->right += 2;
+ }
+
+ /**
+ * Lock the tree to prevent concurrent modification.
+ */
+ private function _lock() {
+ $result = $this->db->query("SELECT GET_LOCK('{$this->table_name}', 1) AS L")->current();
+ if (empty($result->L)) {
+ throw new Exception("@todo UNABLE_TO_LOCK_EXCEPTION");
+ }
+ }
+
+ /**
+ * Unlock the tree.
+ */
+ private function _unlock() {
+ $this->db->query("SELECT RELEASE_LOCK('{$this->table_name}')");
+ }
+}