[EXTENSION] Nested Set implementation using ActiveRecord

Sorry i’ve been so quiet, last half year has been quiet busy and since I’m currently not actively developing using Yii (I’m planning to start again next month) this extension didn’t have a high priority for me.

I’ve just added a new release which fixes most bugs that were posted here and in the reviews section. Please see the changelog for more information.

I’m currently in the final 3 weeks of my study and very busy with moving and my ‘new’ job, so please be patient. I will add the new proposed features (like scopes) as soon as I have more time. Sadly, I don’t have time to dive into Yii 1.1 at the moment, so if some could help me making it compatible it would be greatly appreciated.

good

It works nice with Yii 1.1rc, you just have to add ->Owner or ->getOwner() to $this->getIsNewRecord(), $sibling->getIsNewRecord(), $node->getIsNewRecord() and $node->save().

I think it’s also good to change $node->save() to $node->Owner->save(false) or if(!$node->Owner->save()) throw new TreeException(print_r($node->Owner->getErrors(), true)).

And… thank you for your work

Sorry for my English.

Thank you, excellent extension!

It may need to add methods: getParents, getLeaves and etc. ?


       /**

     *  Returns the parents of this node

     * @param boolean $root

     * @return array

     */

    public function getParents($root = true) {


        $builder = $this->Owner->getCommandBuilder();

        $cstring = $this->_lftCol." < ? AND ".$this->_rgtCol." > ?";

        if ( ! $root)

            $cstring .= " AND ".$this->_lftCol ." !=1";


        $cstring .= " ORDER BY ".$this->_lftCol. " ASC";

        $criteria = $builder->createCriteria($cstring,array($this->getLeftValue(), $this->getRightValue()));

        $command = $builder->createFindCommand($this->Owner->getTableSchema(),$criteria);

        return $this->Owner->populateRecords($command->queryAll());

    }


    /**

     * Returns a list of leaf nodes.

     * @return array

     */

    public function getLeaves() {

        $builder = $this->Owner->getCommandBuilder();

        $cstring = $this->_lftCol . " = (".$this->_rgtCol." - 1) AND "

                .$this->_lftCol." >= ? AND "

                .$this->_rgtCol." <= ? ORDER BY ".$this->_lftCol. " ASC";

        $criteria = $builder->createCriteria($cstring,array($this->getLeftValue(),$this->getRightValue()));

        $command = $builder->createFindCommand($this->Owner->getTableSchema(),$criteria);

        return $this->Owner->populateRecords($command->queryAll());


    }


    /**

     * Is the current node a root node?

     * @return boolean 

     */

    public function isRoot() {

        return ($this->getLeftValue() === 1);


    }


    /**

     * Is the current node a leaf node

     * @return boolean

     */

    public function isLeaf() {

        return !$this->hasChildNodes();

    }


    /**

     * Is the current node a direct child of the supplied node

     * @param object node object

     * @return boolean

     */

    public function isChild($target) {

        return ($this->getParentNode()->getIdValue() === $target->getIdValue());

    }


    /**

     * Is the current node the direct parent of the supplied node

     *

     * @param object node object

     * @return boolean

     **/

    public function isParent($target) {

        return ($this->getIdValue() === $target->getParentNode()->getIdValue());

    }


    /**

     * Is the current node a sibling of the supplied node

     *

     * @param object node object

     * @return boolean

     **/

    public function isSibling($target) {

        return ($this->getParentNode()->getIdValue() === $target->getParentNode()->getIdValue());

    }


    /**

     * Get Size

     *

     * Returns the current size of the node.

     *

     * @return int

     **/

    protected function getSize() {

        return ($this->getRightValue() - $this->getLeftValue()) + 1;

    }




so, I think: keys for Root node:

lft = 1

rgt = 2

And in your example:

lft = 0

rgt = 1

I could be wrong…

Does nobody else have problems with this extension? It seems to be broken ‘big-time’…

Yes, there are some issues.

There is another nested set extension with some more functionality. It was used in a real project but is not documented yet:

http://code.google.com/p/yiiext/source/browse/trunk/app/extensions#extensions/yiiext/behaviors/model/trees

I tried to move a node with appendChild method as it supposed. In the beginning i got this error:

I opened the TreeBehavior.phpAnd function was


    public function appendChild($node, $brother = null) 

    {

        // Fetch nodes information

        $parent = $this;

        $transaction= $this->Owner->dbConnection->beginTransaction();        

        if($this->Owner->getIsNewRecord())

            throw new TreeException('You can\'t append a node to a parent that is not yet in the database. Add the parent node to the tree first using AppendChild or using an Insert* method.');

        if(!$node->getIsNewRecord())

        {

            return $node->moveBelow($this);

        }


        try

        {.....

Transaction begin before moveBelow so i move it after if ;) like:


    public function appendChild($node, $brother = null) 

    {

        // Fetch nodes information

        $parent = $this;

        if($this->Owner->getIsNewRecord())

            throw new TreeException('You can\'t append a node to a parent that is not yet in the database. Add the parent node to the tree first using AppendChild or using an Insert* method.');

        if(!$node->getIsNewRecord())

        {

            return $node->moveBelow($this);

        }

        $transaction= $this->Owner->dbConnection->beginTransaction(); 

        try

        {.....



And worked.

I am in a strange situation. In my TreeController i call the method


$newnode->insertAfter($reference_node);

The insertAfter($node) method tries to


return $node->getParentNode()->appendChild($this);

and returns [color="#0000FF"]false[/color].

But if in my TreeController do


$parent=$reference_node->getParentNode();

$parent->appendChild($newnode);

Everything will work proper and the return of appendChild will be [color="#0000FF"]true[/color].

From the TreeBehavior.php


            else 

            {

                $minleft = $parent->getRightValue();

                $minright = $parent->getRightValue();

                $cond = ">= ".$parent->getRightValue();

                fb("cnod: ".$cond);

                $lv = $parent->getRightValue();

            }

            $rv = $lv + 1;

  

            $sql = "UPDATE %3\$s SET %1\$s = %1\$s + 2 WHERE %1\$s %2\$s";

            $command = $this->Owner->dbConnection->createCommand(sprintf($sql,$this->_lftCol,$cond, $this->Owner->tableName()));

            $command->execute();

            $command = $this->Owner->dbConnection->createCommand(sprintf($sql,$this->_rgtCol,$cond, $this->Owner->tableName()));

            $command->execute();

This is where the issue i am describing above happens. Is it correct to all the variables take value from $getRightValue(); ???????? :unsure:

in application log i get this.

2010/05/16 22:43:03 [error] [application.extensions.nestedset.treebehavior] Error appending node, transaction aborted. Exception: Class TreeBehavior does not have method with name "save".

in /home/dmtrsslvdr/public_html/wwwTrans/protected/extensions/nestedset/TreeBehavior.php (435)

in /home/dmtrsslvdr/public_html/wwwTrans/protected/extensions/nestedset/TreeBehavior.php (553)

in /home/dmtrsslvdr/public_html/wwwTrans/protected/controllers/TreeController.php (359)

Hi,

with yii v1.1.4, running the given exemple provided in the archive file, I’ve got an error:


Undefined index: children

line 241 (in my category controller):




00238:         $result = "<strong>".$tree['node']->name."</strong (".$tree['node']->getLeftValue().",".$tree['node']->getRightValue().")";

00239: 

00240:             $result .= "<ul>";

00241: foreach($tree['children'] as $key => $child)

00242:             {



This look strange. This extension worked fine for me with the previous release of yii.

Luc