Display an AJAX tree from your DB using CTreeView

This example uses a MySQL DB with a table named tree with the fields id, name, and parent_id.

CREATE TABLE tree (
  id INTEGER UNSIGNED,
  name VARCHAR(50) NOT NULL,
  parent_id INTEGER UNSIGNED,
  PRIMARY(id)
)

In your view, add:

<?php
$this->widget(
    'CTreeView',
    array('url' => array('ajaxFillTree'))
);
?>

This will create a tree widget on your page, and its data will be fetch using the ajaxFillTree of the current controller.

So we have to add this action to the controller:

/**
     * Fills the JS tree on an AJAX request.
     * Should receive parent node ID in $_GET['root'],
     *  with 'source' when there is no parent.
     */
    public function actionAjaxFillTree()
    {
        if (!Yii::app()->request->isAjaxRequest) {
            exit();
        }
        $parentId = 0;
        if (isset($_GET['root'])) {
            $parentId = (int) $_GET['root'];
        }
        $req = Yii::app()->db->createCommand(
            "SELECT m1.id, m1.name AS text, m2.id IS NOT NULL AS hasChildren "
            . "FROM tree AS m1 LEFT JOIN tree AS m2 ON m1.id=m2.parent_id "
            . "WHERE m1.parent_id <=> $parentId "
            . "GROUP BY m1.id ORDER BY m1.name ASC"
        );
        $children = $req->queryAll();
        echo str_replace(
            '"hasChildren":"0"',
            '"hasChildren":false',
            CTreeView::saveDataAsJson($children)
        );
        exit();
    }

When the page loads, an AJAX request will be sent to fill the first level of the tree. It'll have the GET parameter root to source. We suppose here that the corresponding nodes have a parent_id of 0. You can replace this by NULL if necessary, and the SQL request will still work because it uses <=> instead of =).

Then we read the data in the database. We need a LEFT JOIN to find if a node has children. If each row already has a hasChildren field, you can remove this join and your SQL will be faster.

When sending the JSON-encoded result, there's a little trick: the javascript wants the hasChildren attribute to be a boolean. But the SQL result just contains 0 or 1, so we have to convert it, at least for the "false" case. Instead of operating on the string result, one could modify the PHP array with a foreach.

The exit() might not be necessary, but if you enabled the logs to Firebug, then Yii will write some JS at the end of your response, and it will break your AJAX.

Links

Chinese version

Total 2 comments:

#1364
Missing database column in create table
by scoob.junior at 4:46am on April 7, 2010.

the line . "GROUP BY m1.id ORDER BY m1.mot ASC"

causes an error if you use the default CREATE TABLE suggested because the m1.mot column doesn't exist

code CREATE TABLE tree ( id INTEGER UNSIGNED, name VARCHAR(50) NOT NULL, parent_id INTEGER UNSIGNED, ???? mot ???? PRIMARY(id) ) /code

but removing or creating the column, the tip works beautifully!!!

thanks for sharing!

#1371
Fixed the column name in the SQL
by Francois.Gannaz at 4:31am on April 8, 2010.

This code was adapted from a real application. When I wrote in the cookbook, I forgot to change a column name. It's now fixed.

Your Comment:

You may enter comment using Markdown syntax.

Please login with your forum account.
Note: you must have at least ONE forum post with your account.