<?php
/**
 * @author Alexey Tokar aka (azazel.tap@gmail.com), © 2008
 * @version 1.0
 * @requirements PHP5 MySQL5
 * 
CREATE TABLE  `ns` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `pid` int(10) unsigned NOT NULL default '0',
  `lid` int(10) unsigned NOT NULL default '0',
  `rid` int(10) unsigned NOT NULL default '0',
  `level` int(10) unsigned NOT NULL default '0',
  `ignored` tinyint(3) unsigned NOT NULL default '0',
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB;
 */
class DBNastedSet {
    protected 
$tbl_name "ns";
    protected 
$db;
    
    public function 
__construct() {
        
$this->db Db::getInstance();
    }
    
    public function 
AddNode($parent_id) {
        
$this->db->query("START TRANSACTION");
        
        
$row $this->GetNodeInfo($parent_id);
        
        
        
$sql sprintf(
            
"UPDATE %s SET lid = lid + 2 WHERE lid > %d"
            
$this->tbl_name
            
$row['lid']
        );
        
$this->db->query($sql);
        
        
$sql sprintf(
            
"UPDATE %s SET rid = rid + 2 WHERE rid >= %d",
            
$this->tbl_name,
            
$row['lid']
        );
        
$this->db->query($sql);
        
        
$sql sprintf(
            
"INSERT INTO %s(id, pid, lid, rid, level, ignored) VALUES(NULL, %d, %d, %d, %d, 0)"
            
$this->tbl_name,
            
$parent_id,
            
$row['lid'] + 1,
            
$row['lid'] + 2,
            
$row['level'] + 1
        
);
        
$this->db->query($sql);
        
        
$this->db->query("COMMIT");
        
        return 
$this->db->insert_id;
    }
    
    public function 
MoveNode($node_id$parent_id) {
        
$this->db->query("START TRANSACTION");
        
        
$row $this->GetNodeInfo($node_id);
        
        
//изолировать перемещаемую ветку
        
$sql sprintf(
            
"UPDATE %s SET ignored = 1 WHERE lid >= %d AND rid <= %d",
            
$this->tbl_name,
            
$row['lid'],
            
$row['rid']
        );
        
$this->db->query($sql);
        
$child_cnt $this->db->affected_rows;
        
        
//обновить правые записи
        
$sql sprintf(
            
"UPDATE %s SET lid = lid - %d WHERE lid > %d AND ignored = 0",
            
$this->tbl_name,
            
$child_cnt 2,
            
$row['lid']
        );
        
$this->db->query($sql);
        
        
$sql sprintf(
            
"UPDATE %s SET rid = rid - %d WHERE rid > %d AND ignored = 0",
            
$this->tbl_name,
            
$child_cnt 2,
            
$row['lid']
        );
        
$this->db->query($sql);        
        
        
        
        
$ins_row $this->GetNodeInfo($parent_id);
        
        
//обновить записи справа от вставляемого элемента
        
$sql sprintf(
            
"UPDATE %s SET lid = lid + %d WHERE lid > %d AND ignored = 0",
            
$this->tbl_name,
            
$child_cnt 2,
            
$ins_row['lid']
        );
        
$this->db->query($sql);
        
        
$sql sprintf(
            
"UPDATE %s SET rid = rid + %d WHERE rid > %d AND ignored = 0",
            
$this->tbl_name,
            
$child_cnt 2,
            
$ins_row['lid']
        );
        
$this->db->query($sql);
        
        
//вставить ветку
        
$sql sprintf(
            
"UPDATE %s SET pid = %d WHERE id = %d",
            
$this->tbl_name,
            
$parent_id,
            
$node_id
        
);
        
$this->db->query($sql);
        
        
//обновить записи внутри вставляемой ветки
        
$sql sprintf(
            
"UPDATE %s SET lid = lid + %d, rid = rid + %2\$d, level = level + %d WHERE ignored = 1"
            
$this->tbl_name,
            
$ins_row['lid'] - $row['lid'] + 1,
            
$ins_row['level'] - $row['level'] + 1
        
);
        
$this->db->query($sql);
        
        
//деизолировать ветку        
        
$sql sprintf(
            
"UPDATE %s SET ignored = 0 WHERE ignored = 1",
            
$this->tbl_name
        
);
        
$this->db->query($sql);
        
        
$this->db->query("COMMIT");
    }
    
    public function 
DeleteNode($node_id) {
        
$this->db->query("START TRANSACTION");
        
        
$row $this->GetNodeInfo($node_id);
        
        
//изолировать перемещаемую ветку
        
$sql sprintf(
            
"DELETE FROM %s WHERE lid >= %d AND rid <= %d",
            
$this->tbl_name,
            
$row['lid'],
            
$row['rid']
        );
        
$this->db->query($sql);
        
$child_cnt $this->db->affected_rows;
        
        
//обновить правые записи
        
$sql sprintf(
            
"UPDATE %s SET lid = lid - %d WHERE lid > %d",
            
$this->tbl_name,
            
$child_cnt 2,
            
$row['lid']
        );
        
$this->db->query($sql);
        
        
$sql sprintf(
            
"UPDATE %s SET rid = rid - %d WHERE rid > %d",
            
$this->tbl_name,
            
$child_cnt 2,
            
$row['lid']
        );        
        
$this->db->query($sql);
        
        
$this->db->query("COMMIT");
    }
    
    public function 
AddRootNode() {
        
$this->db->query("START TRANSACTION");
        
        
$sql sprintf("INSERT INTO %s (id, pid, lid, rid, level, ignored) VALUES (1, 0, 1, 2, 0, 0)",
            
$this->tbl_name
        
);
        
$this->db->query($sql);
        
        
$this->db->query("COMMIT");
        
        return 
1;
    }
    
    public function 
GetBranch($node_id$deep 0) {
        
$row $this->GetNodeInfo($node_id);
        
        
$sql sprintf(
            
"SELECT *, tree.id as id
            FROM %s AS tree
            WHERE tree.lid >= %d 
                AND tree.rid <= %d 
                AND IF(%d, tree.level < %d, 1) 
            ORDER BY tree.lid"
,
            
$this->tbl_name,
            
$row['lid'],
            
$row['rid'],
            
$deep,
            
$row['level'] + $deep
        
);
        
$result $this->db->query($sql);

        
$list = array();
        while (
$row $result->fetch_assoc()) {
            
$list[] = $row;
        }

        return 
$this;
    }
    
    public function 
GetBackPath($node_id) {
        
$row $this->GetNodeInfo($node_id);
        
        
$sql sprintf(
            
"SELECT *, tree.id as id 
            FROM %s AS tree
            WHERE tree.lid <= %d 
                AND tree.rid >= %d 
            ORDER BY tree.lid ASC"
,
            
$this->tbl_name,
            
$row['lid'],
            
$row['rid']
        );
        
        
$result $this->db->query($sql);

        
$list = array();
        while (
$row $result->fetch_assoc()) {
            
$list[] = $row;
        }
    
        return 
$this;
    }
    
    private function 
GetNodeInfo($node_id) {
        
$sql sprintf(
            
"SELECT * FROM %s WHERE id = %d"
            
$this->tbl_name
            
$node_id
        
);

        
$row $this->db->query($sql)->fetch_assoc();
        
        return 
$row;
    }
}
?>