amtgard/ORK3

View on GitHub
system/lib/Yapo2/Driver/structure.Interface.YapoNestedSetTree.php

Summary

Maintainability
D
2 days
Test Coverage
<?php

include_once(Yapo::$DIR_DRIVER . '/structure.Interface.Yapo.php');

class InterfaceYapoNestedSetTree extends InterfaceYapo {

    protected $__LEFT_FIELD;
    protected $__RIGHT_FIELD;
    protected $__NEXT_MODE;
    protected $__TREE_CACHE; 
    protected $__TREE_FIELD;

    function __construct(& $database, $table, $left_field, $right_field, $tree_field = null) {
        parent::__construct($database, $table);
        $this->__LEFT_FIELD = $left_field;
        $this->__RIGHT_FIELD = $right_field;
        $this->__NEXT_MODE = YapoTree::NEXT_DEFAULT;
        $this->__TREE_CACHE = array();
        $this->__TREE_FIELD = $tree_field;
    }
    
    /**
        Retrieve a Full Tree -- root(); tree()
        Find all leaf nodes -- leaves()
        Retrieve a single path -- path()
        Finding depth of a subtree -- depthsound()
        Find immediate subordinates -- children()
        Find the root of the current node -- root()
        Find the parent of the current node -- parent()
        delete an entire tree -- destroy()
        delete this node and children -- delete()
        remove a node and move it's children up to it's parent -- excise()
        Convert a sub-tree into it's own tree -- promote()
    **/
    
    protected function multitree() {
        return is_null($this->__TREE_FIELD)?false:$this->__TREE_FIELD;
    }
    
    protected function mtvalue($value = null) {
        $mt = $this->__TREE_FIELD;
        if (!is_null($value))
            $this->$mt = $value;
        return $this->$mt;
    }
    
    protected function leftvalue($value = null) {
        $lv = $this->__LEFT_FIELD;
        if (!is_null($value))
            $this->$lv = $value;
        return $this->$lv;
    }

    protected function rightvalue($value = null) {
        $rv = $this->__RIGHT_FIELD;
        if (!is_null($value))
            $this->$rv = $value;
        return $this->$rv;
    }

    protected function parentvalue($value = null) {
        $pv = $this->__PARENT_FIELD;
        if (!is_null($value))
            $this->$pv = $value;
        return $this->$pv;
    }

    function isRoot() {
        return $this->activerecord() && $this->leftvalue() == 1;
    }
    
    // Return the all nodes of the tree from the current node
    function tree($order = YapoTree::NEXT_TREE_DEPTH) {
        $tree_id = $this->pkvalue();
        $Data = array( ':tree_id' => $tree_id );
        if ($this->multitree()) {
            $mtselect = "and parent.`" . $this->multitree() . "` = :mtvalue and node.`" . $this->multitree() . "` = :mtvalue ";
            $Data[':mtvalue'] = $this->mtvalue();
        }
        $this->clear();
        $sql = "SELECT node.* FROM `" . $this->__TableName . "` as node, `" . $this->__TableName . "` as parent
                    where 
                        node.`" . $this->__LEFT_FIELD . "` between parent.`" . $this->__LEFT_FIELD . "` and parent.`" . $this->__RIGHT_FIELD . "`
                        and parent.`" . $this->primarykey() . "` = :tree_id
                        $mtselect
                    order by node.`" . $this->__LEFT_FIELD . "`";
        return $this->query($sql, $Data, true);
    }
    
    // Return children of the current node
    function children($gt = null) {
        $tree_id = $this->pkvalue();
        $Data = array( ':tree_id' => $tree_id );
        if ($this->multitree()) {
            $mtselect = "and node.`" . $this->multitree() . "` = :mtvalue and parent.`" . $this->multitree() . "` = :mtvalue";
            $submtselect = "and sub_parent.`" . $this->multitree() . "` = :mtvalue";
            $Data[':mtvalue'] = $this->mtvalue();
        }
        $this->clear();
        $sql = "
                    SELECT node.*, (COUNT(parent.`" . $this->primarykey() . "`) - (sub_tree.depth + 1)) AS depth
                        FROM `" . $this->__TableName . "` AS node,
                                `" . $this->__TableName . "` AS parent,
                                `" . $this->__TableName . "` AS sub_parent,
                                (
                                        SELECT node.`" . $this->primarykey() . "`, (COUNT(parent.`" . $this->primarykey() . "`) - 1) AS depth
                                            FROM `" . $this->__TableName . "` AS node,
                                                `" . $this->__TableName . "` AS parent
                                            WHERE node.`" . $this->__LEFT_FIELD . "` BETWEEN parent.`" . $this->__LEFT_FIELD . "` AND parent.`" . $this->__RIGHT_FIELD . "`
                                                AND node.`" . $this->primarykey() . "` = :tree_id
                                                $mtselect
                                            GROUP BY node.`" . $this->primarykey() . "`
                                            ORDER BY node.`" . $this->__LEFT_FIELD . "`
                                )AS sub_tree
                        WHERE node.`" . $this->__LEFT_FIELD . "` BETWEEN parent.`" . $this->__LEFT_FIELD . "` AND parent.`" . $this->__RIGHT_FIELD . "`
                                AND node.`" . $this->__LEFT_FIELD . "` BETWEEN sub_parent.`" . $this->__LEFT_FIELD . "` AND sub_parent.`" . $this->__RIGHT_FIELD . "`
                                AND sub_parent.`" . $this->primarykey() . "` = sub_tree.`" . $this->primarykey() . "`
                                $mtselect
                                $submtselect
                        GROUP BY node.`" . $this->primarykey() . "`
                        HAVING depth = 1
                        ORDER BY node.`" . $this->__LEFT_FIELD . "`
                        ";
        return $this->query($sql, $Data, true);
    }
    
    // Finds the root node of the current node
    function root() {
        if ($this->multitree()) {
            $mtvalue = $this->mtvalue();
        }
        $this->clear();
        $this->left = 1;
        if ($this->multitree()) $this->mtvalue($mtvalue);
        return $this->find();
    }
    
    // Finds the immediate parent of the current node
    function parent() {
        $tree_id = $this->pkvalue();
        $Data = array( ':left' => $this->leftvalue(), ':right' => $this->rightvalue() );
        if ($this->multitree()) {
            $mtselect = "and node.`" . $this->multitree() . "` = :mtvalue ";
            $Data[':mtvalue'] = $this->mtvalue();
        }
        $this->clear();
        $sql = "SELECT * FROM `" . $this->__TableName . "` as node
                    where 
                        node.`" . $this->__RIGHT_FIELD . "` > node.`" . $this->__LEFT_FIELD . "` + 1
                        and node.`" . $this->__RIGHT_FIELD . "` > :right and node.`" . $this->__LEFT_FIELD . "` < :left
                        $mtselect
                    order by node.`" . $this->__LEFT_FIELD . "` DESC
                    limit 1";
        return $this->query($sql, $Data, true);
    }
    
    // Returns the path from this node to the root
    function path() {
        $tree_id = $this->pkvalue();
        $Data = array( ':tree_id' => $tree_id );
        if ($this->multitree()) {
            $mtselect = "and parent.`" . $this->multitree() . "` = :mtvalue and node.`" . $this->multitree() . "` = :mtvalue";
            $Data[':mtvalue'] = $this->mtvalue();
        }
        $this->clear();
        $sql = "SELECT parent.* FROM `" . $this->__TableName . "` as node, `" . $this->__TableName . "` as parent
                    where 
                        node.`" . $this->__LEFT_FIELD . "` between parent.`" . $this->__LEFT_FIELD . "` and parent.`" . $this->__RIGHT_FIELD . "`
                        and node.`" . $this->primarykey() . "` = :tree_id
                        $mtselect
                    order by node.`" . $this->__LEFT_FIELD . "`";
                    
        return $this->query($sql, $Data, true);
    }
    
    // returns all the leaves of the current tree
    function leaves() {
        $Data = array(  );
        if ($this->multitree()) {
            $mtselect = "and node.`" . $this->multitree() . "` = :mtvalue ";
            $Data[':mtvalue'] = $this->mtvalue();
        }
        $this->clear();
        $sql = "SELECT * FROM `" . $this->__TableName . "` as node
                    where 
                        node.`" . $this->__RIGHT_FIELD . "` = node.`" . $this->__LEFT_FIELD . "` + 1
                        $mtselect
                    order by node.`" . $this->__LEFT_FIELD . "`";
                    
        return $this->query($sql, $Data, true);
    }
    
    // Returns the depth of every subordinate node from here
    function depthsound() {
        $tree_id = $this->pkvalue();
        $Data = array( ':tree_id' => $tree_id );
        if ($this->multitree()) {
            $mtselect = "and node.`" . $this->multitree() . "` = :mtvalue and parent.`" . $this->multitree() . "` = :mtvalue";
            $submtselect = "and sub_parent.`" . $this->multitree() . "` = :mtvalue";
            $Data[':mtvalue'] = $this->mtvalue();
        }
        $this->clear();
        $sql = "
                    SELECT node.*, (COUNT(parent.`" . $this->primarykey() . "`) - (sub_tree.depth + 1)) AS depth
                        FROM `" . $this->__TableName . "` AS node,
                                `" . $this->__TableName . "` AS parent,
                                `" . $this->__TableName . "` AS sub_parent,
                                (
                                        SELECT node.`" . $this->primarykey() . "`, (COUNT(parent.`" . $this->primarykey() . "`) - 1) AS depth
                                            FROM `" . $this->__TableName . "` AS node,
                                                `" . $this->__TableName . "` AS parent
                                            WHERE node.`" . $this->__LEFT_FIELD . "` BETWEEN parent.`" . $this->__LEFT_FIELD . "` AND parent.`" . $this->__RIGHT_FIELD . "`
                                                AND node.`" . $this->primarykey() . "` = :tree_id
                                                $mtselect
                                            GROUP BY node.`" . $this->primarykey() . "`
                                            ORDER BY node.`" . $this->__LEFT_FIELD . "`
                                )AS sub_tree
                        WHERE node.`" . $this->__LEFT_FIELD . "` BETWEEN parent.`" . $this->__LEFT_FIELD . "` AND parent.`" . $this->__RIGHT_FIELD . "`
                                AND node.`" . $this->__LEFT_FIELD . "` BETWEEN sub_parent.`" . $this->__LEFT_FIELD . "` AND sub_parent.`" . $this->__RIGHT_FIELD . "`
                                AND sub_parent.`" . $this->primarykey() . "` = sub_tree.`" . $this->primarykey() . "`
                                $mtselect
                                $submtselect
                        GROUP BY node.`" . $this->primarykey() . "`
                        ORDER BY node.`" . $this->__LEFT_FIELD . "`";
        return $this->query($sql, $Data, true);
    }    
    
    function _depthsound() {
        $tree_id = $this->pkvalue();
        $Data = array( );
        if ($this->multitree()) {
            $mtselect = "and parent.`" . $this->multitree() . "` = :mtvalue and node.`" . $this->multitree() . "` = :mtvalue ";
            $Data[':mtvalue'] = $this->mtvalue();
        }
        $this->clear();
        $sql = "SELECT node.*, (count(parent.`" . $this->primarykey() . "`) - 1) as depth 
                    FROM `" . $this->__TableName . "` as node, `" . $this->__TableName . "` as parent
                    where 
                        node.`" . $this->__LEFT_FIELD . "` between parent.`" . $this->__LEFT_FIELD . "` and parent.`" . $this->__RIGHT_FIELD . "`
                        $mtselect
                    group by node.`" . $this->primarykey() . "`
                    order by node.`" . $this->__LEFT_FIELD . "`";
        return $this->query($sql, $Data, true);
    }

    // Returns the path depth of the current node to the root
    function length() {
        return $this->path();
    }
    
    protected function _save_child(& $yapo, $child, $insert_left = false) {

    }
    
    function save($new_child = false, $after_id = false) {
        $this->beginTransaction();
        try {
            if (is_object($new_child) && get_class($new_child) == 'YapoGraphNode') {
                if ($this->activerecord()) {
                    $yapo = new Yapo($this->__Database, $this->__TableName);
                    $yapo->clear();
                    if ($this->multitree()) {
                        $this->_save_child($yapo, $new_child, $after_id);
                    }
                    $this->commit();
                    parent::save($new_child);
                } else {
                    throw new Exception('Multiple child insert not supported');
                }
            } else {
                // Creates a new tree
                if (!$this->activerecord()) {
                    $yapo = new Yapo($this->__Database, $this->__TableName);
                    $yapo->clear();
                    $mtfield = $this->multitree();
                    $yapo->$mtfield = $this->mtvalue();
                    if ($yapo->find() > 0)
                        return 0;
                    $this->leftvalue(1);
                    $this->rightvalue(2);
                }
                $this->commit();
                return parent::save();
            }
        }  catch (PDOException $pdo) {
            $this->rollback();
            throw $pdo;
        }
        return 0;
    }
    
    // Removes this node
    function excise() {

    }
    
    function delete() {
        parent::delete();
    }
    
    // Changes this subtree into it's own tree
    function promote($mtvalue = null) {

    }
    
    function move($node_id, $parent_node_id) {
        $this->beginTransaction();
        
        try {
            $this->clear();
            $this->pkvalue($parent_node_id);
            $this->find();
            
            $newpos = $this->leftvalue() + 1;
            
            $this->clear();
            $this->pkvalue($node_id);
            $this->find();
            
            $width = $this->rightvalue() - $this->leftvalue() + 1;
            $distance = $newpos - $this->leftvalue();
            $tmppos = $this->leftvalue();
            
            if ($distance < 0) {
                $distance -= $width;
                $tmppos += $width;
            }
            
            $width = array( ":width" => $width );
            $distance = array( ":distance" => $distance );
            $tmppos = array( ":tmppos" => $tmppos );
            $newpos = array( ":newpos" => $newpos );
            $mtvalue = array( ':mtvalue' => $this->mtvalue() );
            $oldrpos = array( ':oldrpos' => $this->rightvalue() );
            
            // make space for sub-tree
            $sql = "update `" . $this->__TableName . "` 
                        set 
                            `" . $this->__LEFT_FIELD . "` = `" . $this->__LEFT_FIELD . "` + :width
                        where
                            `" . $this->__LEFT_FIELD . "` >= :newpos and
                            `" . $this->multitree() . "` = :mtvalue";
            $this->execute($sql, array_merge($width, $newpos, $mtvalue));
            //print_r(array($sql, array_merge($width, $newpos, $mtvalue)));
            $sql = "update `" . $this->__TableName . "` 
                        set 
                            `" . $this->__RIGHT_FIELD . "` = `" . $this->__RIGHT_FIELD . "` + :width
                        where
                            `" . $this->__RIGHT_FIELD . "` >= :newpos and
                            `" . $this->multitree() . "` = :mtvalue";
            $this->execute($sql, array_merge($width, $newpos, $mtvalue));
            //print_r(array($sql, array_merge($width, $newpos, $mtvalue)));
            
            // move subtree
            $sql = "update `" . $this->__TableName . "` 
                        set 
                            `" . $this->__LEFT_FIELD . "` = `" . $this->__LEFT_FIELD . "` + :distance,
                            `" . $this->__RIGHT_FIELD . "` = `" . $this->__RIGHT_FIELD . "` + :distance
                        where
                            `" . $this->__LEFT_FIELD . "` >= :tmppos and
                            `" . $this->__RIGHT_FIELD . "` < :tmppos + :width and
                            `" . $this->multitree() . "` = :mtvalue";
            $this->execute($sql, array_merge($distance, $width, $mtvalue, $tmppos));
            //print_r(array($sql, array_merge($distance, $width, $mtvalue, $tmppos)));
            
            // remove old space
            $sql = "update `" . $this->__TableName . "` 
                        set 
                            `" . $this->__LEFT_FIELD . "` = `" . $this->__LEFT_FIELD . "` - :width
                        where
                            `" . $this->__LEFT_FIELD . "` > :oldrpos and
                            `" . $this->multitree() . "` = :mtvalue";
            $this->execute($sql, array_merge($width, $oldrpos, $mtvalue));
            //print_r(array($sql, array_merge($width, $oldrpos, $mtvalue)));
            $sql = "update `" . $this->__TableName . "` 
                        set 
                            `" . $this->__RIGHT_FIELD . "` = `" . $this->__RIGHT_FIELD . "` - :width
                        where
                            `" . $this->__RIGHT_FIELD . "` > :oldrpos and
                            `" . $this->multitree() . "` = :mtvalue";
            $this->execute($sql, array_merge($width, $oldrpos, $mtvalue));
            //print_r(array($sql, array_merge($width, $oldrpos, $mtvalue)));
            

        } catch (PDOException $pdo) {
            $this->rollback();
            throw $pdo;
        }
    }
    
    function insert($node_id, $insert_parent_id) {
        $this->beginTransaction();
        
        try {
            $this->clear();
            $this->pkvalue($node_id);

            $this->find();
            $from_mtvalue = array( ':from_mtvalue' => $this->mtvalue() );
            $width = array( ':width' => $this->rightvalue() - $this->leftvalue() + 1 );
            
            $this->clear();
            $this->pkvalue($insert_parent_id);
            $this->find();

            $to_mtvalue = array( ':to_mtvalue' => $this->mtvalue() );
            $to_left = array( ':to_left' => $this->leftvalue() );
            
            // make space for sub-tree
            $sql = "update `" . $this->__TableName . "` 
                        set 
                            `" . $this->__RIGHT_FIELD . "` = `" . $this->__RIGHT_FIELD . "` + :width
                        where
                            `" . $this->__RIGHT_FIELD . "` > :to_left and
                            `" . $this->multitree() . "` = :to_mtvalue";
            $this->execute($sql, array_merge($to_mtvalue, $width, $to_left));
            
            $sql = "update `" . $this->__TableName . "` 
                        set 
                            `" . $this->__LEFT_FIELD . "` = `" . $this->__LEFT_FIELD . "` + :width
                        where
                            `" . $this->__LEFT_FIELD . "` > :to_left and
                            `" . $this->multitree() . "` = :to_mtvalue";
            $this->execute($sql, array_merge($to_mtvalue, $width, $to_left));
            
            // insert sub-tree
            $sql = "update `" . $this->__TableName . "` 
                        set 
                            `" . $this->__LEFT_FIELD . "` = `" . $this->__LEFT_FIELD . "` + :to_left,
                            `" . $this->__RIGHT_FIELD . "` = `" . $this->__RIGHT_FIELD . "` + :to_left,
                            `" . $this->multitree() . "` = :to_mtvalue
                        where
                            `" . $this->multitree() . "` = :from_mtvalue";
            $this->execute($sql, array_merge($from_mtvalue, $to_left, $to_mtvalue));
            
            $this->commit();
        } catch (PDOException $pdo) {
            $this->rollback();
            throw $pdo;
        }
        return true;
        //print_r(array($sql, array_merge($from_mtvalue, $to_left, $to_mtvalue)));
    }
    
}