Display an AJAX tree from your DB using CTreeView

You are viewing revision #11 of this wiki article.
This version may not be up to date with the latest version.
You may want to view the differences to the latest version or see the changes made in this revision.

« previous (#10)next (#12) »

  1. The database
  2. The view
  3. The controller

The database

This example uses a MySQL DB with a table named tree with the fields id, name, and parent_id. The parent_id will be NULL for root elements. The SQL is kept simple (no autoinc, no FK, etc).

  name VARCHAR(50) NOT NULL,

MySQL has no recursive queries. In order to fetch tree nodes, you have to recursively send SQL queries for each node, asking for its children. The easiest way to do so is to send these queries with AJAX, so that the tree can be displayed even if the deep nodes haven't been fetched yet. If you're using Oracle or Postgresql, there are other solutions, though this will also work.

The view

In your view, add:

~ `php <?php $this->widget(

array('url' => array('ajaxFillTree'))

); ?> `~

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

The controller

So we have to add this action to the controller:

~ `php /**

 * 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) {
	$parentId = "NULL";
	if (isset($_GET['root']) && $_GET['root'] !== 'source') {
		$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(

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` set to `source` (this is the behavior of the [Treeview](http://bassistance.de/jquery-plugins/jquery-plugin-treeview/) JS plugin that [CTreeView](http://www.yiiframework.com/doc/api/1.1/CTreeView/) uses). We suppose here that the corresponding nodes (the root nodes) have a `parent_id` set to `NULL`. In SQL, "=" can't compare a value with `NULL`, so we have to use the `<=>` operator instead.

The other AJAX requests will have an integer value (the parent node's id) in `$_GET['root']`. We typecast this to "int" for security.

Then the code reads 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 or extensions like a debug toolbar, then Yii may write some JS at the end of your response that would break the JSON format. The drawback of `exit()` is that logs won't be written, whereas `Yii::app()->end()` ends gracefully.

### Final note

This is meant as a simple introduction. There are many ways to enhance this:

* Refactor the action by putting its 3 last lines into a model.
* Inscrease the abstraction level, so that the code can be used on various models, as long as they share the same table structure.
* If the tree content rarely changes, you may consider using a different structure, like nested sets. This will make most queries easier, e.g. fetching the path of a node.
19 0
Viewed: 63 046 times
Version: Unknown (update)
Category: Tutorials
Written by: François Gannaz
Last updated by: François Gannaz
Created on: Apr 7, 2010
Last updated: 7 years ago
Update Article


View all history

Related Articles