SimpleTree provides tree-view for ActiveRecords with drag'n'drop support for quick table manipulation. It uses the jsTree jQuery plugin from jstree.com.
Requirements ¶
Yii 1.1 or above. Could possibly run on Yii 1.0 as well.
Usage ¶
Unpack to your extension folder.
Make sure the Model you're using has properties/columns for the following: ID, parent ID, name and position. By default, these are respectively called id, id_parent, title and position.
/**
* Quick setup
**/
$this->widget('application.extensions.SimpleTreeWidget',array(
'model'=>'MyModel',
));
/**
* Customized setup
**/
$this->widget('application.extensions.SimpleTreeWidget',array(
'model'=>Folder::model()->findByPk(43),
'modelPropertyParentId' => 'parent_id',
'modelPropertyName' => 'name',
'ajaxUrl' => '/ajax/simpletree',
'onSelect'=>'
var id = data.inst.get_selected().attr("id").replace("node_","");
$("#contentBox").load("/ajax/getContent/id/"+id);
'
));
//ajaxController
public function actionsimpletree()
{
Yii::import('application.extensions.SimpleTreeWidget');
SimpleTreeWidget::performAjax();
}
/**
* Parameters and defaults
**/
$ajaxUrl; //it is recommended that you use your own url
$id = 'simpletree_widget';
$model; //this can be the name of a model, an empty model or a loaded model
$modelPropertyName = 'title'; //name of the name property in your model
$modelPropertyId = 'id'; //name of the ID property in your model
$modelPropertyParentId = 'id_parent'; //name of the parent ID property in your model
$modelPropertyPosition = 'position'; //name of the position property in your model
$theme = 'default'; //apple, classic, default
$onSelect; //javascript
$onCreate; //javascript
$onMove; //javascript
$onRemove; //javascript
$onRename; //javascript
Changelog ¶
January 27, 2011
- Release 0.6
- FEATURE: It is now possible to write protect a model with $Model->readonly = true;. Write protected models are not protected from indirect deletion in case one of its ancestors is deleted.
- CHANGE: Models are now loaded via Ajax to accommodate large tables
- FIX: It's no longer possible to drag folders outside the root folder(s)
- FIX: Copied models now show correct IDs instead of "unidentified"
FIX: It's now possible to copy a copied model without the need to refresh first
- Release 0.5b
- CHANGE: onFocus has been renamed to onSelect and calls select_node() instead of on_focus()
- FIX: calling data.inst._get_node() from onSelect (formerly onFocus) now returns the selected node rather than the previous selected node
January 26, 2011
- Release 0.5
- Initial release
demo
I would expect a demo for this kind of extension? Where do I find one?
re: demo
I don't have a demo for the widget, but the one at jstree should give you a good idea.
http://www.jstree.com/demo
SimpleTree is made explicitly for ActiveRecords, so there are no leafs as each record is represented by a folder. Also, Simpletree preloads all ActiveRecords recursively.
nice one
looks nice, i'll give it a try :) 10x man
Update?
Are you going to update to jsTree v.1.0rc2?
re: Update
The extension is made with jsTree v.1.0rc2.
needed functionality
I tested the widget and it works OK. But there is bug when I drag an item to the root - an error "1054 Unknown column 'simpletree_widget' in 'where clause'".
Also it would be nice to have a readonly property.
Issue with PHP 5.3 and APC
Please, be aware that there is an issue preventing your widget to run on some servers (APC activated I believe).
"PHP Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM" errors occurs because of the way you access static "model" objects.
Instead of using code like that :
you should write something like :
$mymodel = new $model;
Nice extension anyway.
Juban
Thanks for the feedback, this should be fixed in the v0.6.
Any example?
hi there, i'm still a newbie to Yii extension, do you have any model example or data example (SQL) that i can fetch into this widget?
TIA
Try this
CREATE TABLE IF NOT EXISTS
category
(id
int(11) NOT NULL AUTO_INCREMENT,id_parent
int(11) unsigned NOT NULL,position
mediumint(5) unsigned NOT NULL,title
varchar(255) NOT NULL,PRIMARY KEY (
id
)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=0 ;
it works
thanks! it works :D
PHP Error [2048]
when i run the script via ajax i got this error:
PHP Error [2048] Non-static method SimpleTreeWidget::_get_children() should not be called statically
i think you should change the '_get_children' method to be static:
public static function _get_children()
Works very nice!
INSERT INTO
category
(
id
,id_parent
,position
,title
)VALUES
(1, 0, 0, 'Hello');
Tree return parent model recursively - how get children?
I have the tree working, but when I click on a parent node, it displays the parent model again instead of children. For example, a parent named 'Strategy' continues to return the 'Strategy' model, instead of its child 'Task'.
After searching through the code, I couldn't find where the child model's name is determined. Is there a way that this extension is examining the parent model's metada to find child relations? Can anybody help with this, or explain how to specify the child model's name that should be loaded so the parent model is not continuously reloaded?
Thanks :)
Menu item ordering
Thanks JayRoe - nice extension!
One thing I struck, and not sure if it was just my configuration - but I had to add a paramater to order the model data by position to get the move node part working, e.g.
SimpleTreeWidget.php line 207
foreach ($Model->findAllByAttributes(array($_REQUEST['modelPropertyParentId']=>$_REQUEST['id'])) AS $k => $Model)
Changed to:
foreach ($Model->findAllByAttributes(array($_REQUEST['modelPropertyParentId']=>$_REQUEST['id']),array('order'=>'position ASC')) AS $k => $Model)
Without this, whenever I moved nodes the position field was updating in the database, but the menu was not reflecting the changes. Anyway, thought I'd post this in case someone else encountered the same problem.
loading problem
i still can't get it to work, even created the table to test it, but the script just keeps saying "Loading..." i can add new nodes, but it's not working with te db
Absolute url
@ dzaeny
Try using an absolute url
'ajaxUrl' => $this->createAbsoluteUrl('/site/simpletree'),
doodle
loading problem
i was trying to use the quick setup with just the name of the model, tried using the advanced configuration and i added the actionsimpletree to my site controller, tried creating the path manually (eg 'http://localhost/camp/index.php/site/simpletree'), unfortunately nothing works
i'm using the latest yii version
any suggestions?
Firebug?
If you aren't already using firebug, I suggest you install it in firefox or chrome. It might help with debugging, for ajax problems it is almost a requirement.
doodle
sorry to bother again
but can you guys pls send me or post here some example files using this extension? i'm no good with ajax and clearly don't understand why it isn't working
my mail's: dzaeny@gmail.com
thx in advance
dzaeny
I don't think there's a simpler way to do it than this.
Run this MySQL command:
CREATE TABLE IF NOT EXISTS category ( id int(11) NOT NULL AUTO_INCREMENT, id_parent int(11) unsigned NOT NULL, position mediumint(5) unsigned NOT NULL, title varchar(255) NOT NULL, PRIMARY KEY (id) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=0 ;
Use Gii to create a Category model.
Put this in a view file:
$this->widget('application.extensions.SimpleTreeWidget',array(
'model'=>'Category'
));
If that doesn't work, I suggest you try the included developer tools in Chrome or Firefox. They could probably tell you the problem in a matter of seconds.
brilliant
works like a charm.
Congrats
Working but not saving to db
Hi,
plugin is working well in frontend, except that nothing is stored to database. Table is created by example in earlier comments. Checked error logs - nothing. Firefox error console also shows nothing.
justinas
Do you have rules that prevent the records from being saved?
no rules
I have default newly created webapp.
in site/index.php view
<?php
$this->widget('application.extensions.SimpleTreeWidget',array(
'model'=>'Category', 'ajaxUrl' => $this->createAbsoluteUrl('/site/tree'),
));
?>
site controller
public function actionTree()
{
Yii::import('application.extensions.SimpleTreeWidget'); SimpleTreeWidget::performAjax();
}
Config file:
....
'db'=>array( 'connectionString' => 'mysql:host=localhost;dbname=test', 'emulatePrepare' => true, 'username' => 'root', 'password' => 'pass', 'charset' => 'utf8', ),
...
Category model is created with gii. I also have urlformat path, and any other stuff is default.
I have no idea what is wrong...
Modify JavaScript
Manage contextmenu:
~~~
[javascript]
"contextmenu" : { "items": function(node) { // The default set of all items var items = { create: { // The "delete" menu item label: "Создать", "action":function(obj) { this.create(obj); }, }, rename: { // The "rename" menu item label: "Переименовать", "action":function(obj) { this.rename(obj); }, }, remove: { // The "delete" menu item label: "Удалить", "action":function(obj) { if(confirm("Вы действительно хотите удалить\n категорию и все ее подкатегории? ")){ this.remove(obj); } }, } }; if ($(node).attr("rel")=="readonly") { // Delete the "delete" menu item //delete items.deleteItem; items={}; } return items; } }
And manage hover_node and select_node of root node ("readonly")
[javascript]
"types" : { "readonly" : { "delete_node" : false, "icon" : {"image" : "'.$this->baseUrl.'/themes/readonly.png"}, "hover_node":false, "select_node" : function () {return false;} } }
Hide delete and rename in contextmenu
Sorry!
[javascript] "contextmenu" : { "items": function(node) { // The default set of all items var items = { create: { // The "delete" menu item label: "Создать", "action":function(obj) { this.create(obj); }, }, rename: { // The "rename" menu item label: "Переименовать", "action":function(obj) { this.rename(obj); }, }, remove: { // The "delete" menu item label: "Удалить", "action":function(obj) { if(confirm("Вы действительно хотите удалить\n категорию и все ее подкатегории? ")){ this.remove(obj); } }, } }; if ($(node).attr("rel")=="readonly") { // Delete the "delete" menu item delete items.delete; delete items.rename; } return items; } }
just the one I need, but...
I am experiencing the same issue as justinas. It is not working with me as well :(
When I used the quick setup, only loading displays.
Then I used this as my customized setup:
//view $this->widget('application.extensions.jsTree.SimpleTreeWidget',array( 'model'=>'Menu', 'ajaxUrl' => $this->createAbsoluteUrl('/site/simpletree'), )); //controller public function actionsimpletree() { Yii::import('application.extensions.jsTree.SimpleTreeWidget'); SimpleTreeWidget::performAjax(); }
and what appeared is the folder "root". Yes I can create,move,edit,and delete a node, but what I am expecting to see is the tree derived from my table.
Security with cookies plugin
I just thought I would share a problem that I had when deploying this to my production server. The jquery.cookies.js file would not be served to the client because of security rules set in place by the hosting company.
This was their response when I asked for assistance. > The error you saw was being returned because access to the file was being blocked by a mod_security rule set in the server.
Before the change was made all that happened was an endless loading indicator and firebug reported that cookies.js could not be loaded.
doodle
need i18n be supportted
the context menu should be i18n supportted , if can add or remove some menuitem will be better, what's this extension aim ,just for showing?
if in realistic project there should have more attributes to consider etc : mutli roots. ,there is a type field to differ from different categories .
can anyone give me some advice how to apply this extionsion in real project but a demo one.
or does this extionsion can work togerther with the nestedSet and the EJNestedTreeActions ? any help will be appretiate
it doesn't work when create two widgets in same page.
when i create two simpleTree in same page, only one display.
it seems that you haven't consider the id attribute ,or the htmlOptions:
/* this id will be the tree container id , and the jstree will use it to initialize tree */ 'htmlOptions'=>array('id'=>'simpleTree_'.mt_rand());
although there is a id attribute i can set ,but it seems to not work .
another thing:
you should refer to jstree , even though it out of date, but the code technique is very good , or you may see this swfupload extension, these two js wrapper widgets only have little code , but it give the user more choices to config it.
you can expose the under jstree config object to us like the swfupload extension do.
if people who didn't support the config ,then its your default setting config come out ,
at last , yours is a good extension , above words is only my advice , never mind if i say some thing wrong !
best regards :)
very good~
Thank you ~~~
Some change~~~~
you can add
if($params['id']=="")$params['id']=0;
in you _create_node like this !
if parent id is empty.were throw a error!
like this~~~
static function _create_node($params) { $Model = new $params['model']; if($params['id']=="")$params['id']=0;
Sorting Problem
Hi,
first of all i realy like this extension. There is a little bug with the sorting of the tree. The tree is initialy not sorted by the position field. Here is a solution how to fix this. The Code in SimpleTreeWidget.php
public function _get_children() { $children = array(); $Model = new $_REQUEST['model']; $criteria = new CDbCriteria(); $criteria->order = $_REQUEST['modelPropertyPosition']; $criteria->condition = $_REQUEST['modelPropertyParentId']."=:parent_id"; $criteria->params = array(":parent_id"=>$_REQUEST['id']); foreach ($Model->findAll($criteria) AS $k => $Model) .... ....
way to improve
Hello,
maybe would be more elegant to extend the 'model' attribute with the possibility to give a db criteria in array format and introduce a new attribute 'theHasManyRelation'(string or array, the relation connecting the nodes) instead of 'modelPropertyId','modelPropertyPosition' and 'modelPropertyParentId'.
All of those could be easely posted in a serialized format on user events to the ajax controller.
No interactivity with the DB
I try to run this extension unfortunately without success, just the same issue as in the comments: No interactivity with the DB. Could one please be so kind and give me a hint how to run this thing?
My settings so far:
in the Site Controller:
public function actionTree() { Yii::import('application.extensions.SimpleTreeWidget'); SimpleTreeWidget::performAjax(); }
in the view/site/index:
$this->widget('appli
cation.extensions.SimpleTreeWidget',array( 'model'=>Category::model()->findByPk(43), 'modelPropertyParentId' => 'parent_id', 'modelPropertyName' => 'name', 'ajaxUrl' => '/ajax/simpletree', / also tried: 'ajaxUrl' => $this->createAbsoluteUrl('/site/simpletree'), 'onSelect'=>' var id = data.inst.get_selected().attr("id").replace("node_",""); $("#contentBox").load("/ajax/getContent/id/"+id); ' )); ?>
The Table:
CREATE TABLE IF NOT EXISTS category ( id int(11) NOT NULL AUTO_INCREMENT, id_parent int(11) unsigned NOT NULL, position mediumint(5) unsigned NOT NULL, title varchar(255) NOT NULL, PRIMARY KEY (id) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=0 ;
Is it necessary to fill the table with values?
Best regards
Norbert
RE:No interactivity with the DB
@norbert
1: Definitely the table should have something in it if you want to see any nodes on the tree. If the table is empty there is nothing to display.
2: 'model'=>Category::model()->findByPk(43) this is requesting a single record so unless there is a record with id (43) you won't get anything, plus I doubt that this example would give any useful results unless it provide a collection of products for example and you are supplying the product category. I think it's kind of a half-baked example.
Here's an example of working code.
$url = $this->createAbsoluteUrl('SBPageadmin/getContent').'/&id='; $this->widget('application.extensions.WebPageTreeWidget',array( 'model'=>'Webpage', 'modelPropertyParentId' => 'parent_id', 'modelPropertyId'=>'id', 'modelPropertyName' => 'name', 'ajaxUrl' => $this->createAbsoluteUrl('SBPageadmin/simpletree'), 'onSelect'=>' var id = data.inst.get_selected().attr("id").replace("node_",""); $("#contentBox").load("'.$url.'"+id); ', ));
Notice that my example calls WebPageTreeWidget, that's because I copied the original file and tweaked it for my purposes.
In this example when you click on a node it calls this ajax routine.
/** * getContent * Ajax update * this updates the contents of #contentBox using _view_webpage.php * gets the id of the page using $_GET['id] * if no id then id is set to zero and help page is displayed using _page_index.php * @name ajaxGetContent */ public function actiongetContent() { if($_GET['id'] > 0) { $page = Webpage::model()->findByPk($_GET['id']); $data = Webpagedata::model()->pageID($page->id)->findAll(array('order'=>'position')); $this->renderPartial('_view_webpage',array('model'=>$page,'data'=>$data),false,true); } else { $this->renderPartial('help'.DIRECTORY_SEPARATOR.'_getting_started'); } }
hope this helps to get you going.
doodle
Hi doodle
Thanks for the Re.
I don't figured out. Now I' am getting:
<data.inst.get_selected().attr("id") is undefined>
What exactly does this Code do?:
$data = Categorydata::model()->pageID($page->id)->findAll(array('order'=>'position'));
Of course Categorydata is a Model, but what kind of values are in it? It seems this does a kind of relation between Category and Categorydata.
My Settings:
Controller:
/** * getContent * Ajax update * this updates the contents of #contentBox using _ajaxContent.php * gets the id of the page using $_GET['id] * if no id then id is set to zero and help page is displayed using _page_index.php * @name ajaxGetContent */ public function actiongetContent() { if($_GET['id'] > 0) { $page = Category::model()->findByPk($_GET['id']); $data = Categorydata::model()->pageID($page->id)->findAll(array('order'=>'position')); $this->renderPartial('_ajaxContent',array('model'=>$page,'data'=>$data),false,true); } } public function actionsimpletree() { Yii::import('application.extensions.SimpleTreeWidget'); SimpleTreeWidget::performAjax(); }
Index:
$url = $this->createAbsoluteUrl('Category/getContent').'/&id='; $this->widget('application.extensions.SimpleTreeWidget',array( 'model'=>'Category', 'modelPropertyParentId' => 'parent_id', 'modelPropertyId'=>'id', 'modelPropertyName' => 'name', 'ajaxUrl' => $this->createAbsoluteUrl('Category/simpletree'), 'onSelect'=>' var id = data.inst.get_selected().attr("id").replace("node_",""); $("#contentBox").load("'.$url.'"+id); ', ));
And the _ajaxContent.php:
<?php echo $page ?> <?php echo $data ?>
If I get it right, the function actiongetContent takes the ids(>0) out of Category Model and store it temporarily into the _ajaxContent.php. The Index file takes the ids out of the _ajaxContent.php, right?
Where can I go further?
Best Regards.
Explanation
@agrippa
$data = Categorydata::model()->pageID($page->id)->findAll(array('order'=>'position'));
The data table is simply that, in my case it is data associated with a web page. So the webpage has an id and the webpagedata has pageId field. In the above snippet pageID() is a method within the model, it helps build the mysql query, findAll() is a built in method and I pass an array that specifies what order to return the results in. Again it is just building a query.
public function pageID($id) { $this->getDbCriteria()->mergeWith(array( 'condition'=>'webpageid=:ID', 'params'=>array(':ID'=>$id), )); return $this; }
You should probably read the guide, if you have already read it, read it again. I read the guide a ton of times and I still refer to it all the time.
Regarding the simple tree widget, I suggest that you start with a very simple example but make sure that there are some values in the database or nothing will be displayed.
doodle
further on
doodle
thanks again.
The Problem in my case seems to be the answer to the request. Firebug gives back the Login Page of the demo application. Therefor I Think there is something wrong with the Ajaxurl.
Could you please tell me where to point the ajaxurl?
Thanks for your endurance.
Of course the guide is my constant companion ;-). But its not easy cause I've got no programmers background.
Regards
Problem Rules
Hi,
the problem was a rule. I've to login to solve the Login page thing.
But still not working because of a 403 error.
Damn, I will get this running by hook or by crook.
Regards
permissions problem
@ agrippa
I just realized that the accessRules need to be tweaked.
array('allow', 'actions'=>array('index','simpletree','getContent'), 'roles'=>array('member'), ),
doodle
Thanks doodle
Now I'm faced with this:
1>PHP Error [2048]</h1> <p>Non-static method SimpleTreeWidget::_get_children() should not be called statically (C:\xampp\htdocs\yii\baum\protected\extensions\SimpleTreeWidget.php:245)</p>
Trying to get rind of it with no success:
ini_set('display_errors',1); error_reporting(E_ALL|E_STRICT);
The lines in the Widged:
static function performAjax() { $Model = new $_REQUEST['model']; $method = '_'.$_REQUEST['operation']; header("HTTP/1.0 200 OK"); header('Content-type: text/json; charset=utf-8'); header("Cache-Control: no-cache, must-revalidate"); header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Pragma: no-cache"); self::$method($_POST); }
Strange anyway.
Got it
Finally I got it running.
Get rid of the "static"- Problem when I changed the "_get_Children"- function into "static function" (SimpleTreeWidget.php line 203).
I don't know if this results in some new problems, but so far everything seems to be smooth.
Thanks to the community and special thanks to doodle.
See u.
FIX THIS BUG
If you have any problems with sorting after refresh - find this line in method _get_children
foreach ($Model->findAllByAttributes(array($_REQUEST['modelPropertyParentId']=>$_REQUEST['id'])) AS $k => $Model)
and change it to
foreach ($Model->findAllByAttributes(array($_REQUEST['modelPropertyParentId']=>$_REQUEST['id']),array('order' => 'position')) AS $k => $Model)
root item
Hello,
with this example
/** * Customized setup **/ $this->widget('application.extensions.SimpleTreeWidget',array( 'model'=>Folder::model()->findByPk(43), 'modelPropertyParentId' => 'parent_id', 'modelPropertyName' => 'name', 'ajaxUrl' => '/ajax/simpletree', 'onSelect'=>' var id = data.inst.get_selected().attr("id").replace("node_",""); $("#contentBox").load("/ajax/getContent/id/"+id); ' ));
how can I make, that the root item have been rendered?
I need this item (with primary key:43) beause without it I can't drag an element below the root.
When Title is empty or with white space, the folder created we can resolve this issue
if (trim($params['title'])!='') { //save function } else{ echo json_encode(array('status'=>0)); }
php 5.3 to 5.5
When I've upgrade php from 5.3 to 5.5 I've error in function:
public function _get_children()
Path SimpleTeeeWidnet.php and works ok - line 213:
public static function _get_children()
Add static -
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.