model_name = inflector::singular($this->table_name); } /** * Add this node as a child of the parent provided. * * @chainable * @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 this node's immediate parent. * * @return array ORM */ function parents() { if (!isset($this->parents)) { $this->parents = $this ->where("`left` <= {$this->left}") ->where("`right` >= {$this->right}") ->where("id <> {$this->id}") ->orderby("left", "ASC") ->find_all(); } return $this->parents; } /** * Return all of the children of this node, ordered by id. * * @chainable * @param integer SQL limit * @param integer SQL offset * @return array ORM */ function children($limit=NULL, $offset=0) { if (!isset($this->children)) { $this->children = $this->where("parent_id", $this->id) ->orderby("id", "ASC") ->find_all($limit, $offset); } return $this->children; } /** * Return all of the children of this node, ordered by id. * * @chainable * @param integer SQL limit * @param integer SQL offset * @return array ORM */ function children_count() { if (!isset($this->children_count)) { $this->children_count = $this->where("parent_id", $this->id) ->orderby("id", "ASC") ->count_all(); } return $this->children_count; } /** * @see ORM::reload */ function reload() { $this->parent = null; $this->parents = null; $this->children = null; $this->children_count = null; return parent::reload(); } /** * 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}')"); } }