Merging of model

Hello,

I need to merge 2 model parts into one. I hope my code will explain what I want to achive:




// Getting all root nodes

$roots=Category::model()->roots()->findAll();

// Getting the trees for each root

foreach($roots as $root) {

                $tree = Category::model()->findAll(

                        array(

                        'condition'=>'root='.$root->id,

                        'order'=>'lft'

                        )

                );

        }

   	// I need to merge all the trees, but don't know how


        $this->renderPartial(

                '//partials/category/_gridViewCategory',

                array(

                        'tree'=>$tree,

                )

        );

For better understanding, my category table uses nested sets for building a hierachy and I am using this extension nestedsetbehaviour. As in the forum for this extension no one answers, I am trying to get some help here.

I need to merge different trees into one CActiveDataProvider.

Any idea?

Thanks in advance!

Hi Nightmove, I hope I understand you correctly.

Does it make sence to try something like this:

The model’s search() function gives you a dataprovider. So use the search() function (or copy it and make a new function).

Create a getter function in the model, which could get the trees of a root.

In the search() function, incorporate the getter function.

Hmm, it could be I get you wrong, but I can get only one root per database-call. The result is an array ob objects, just for one root. Then I get the second root and after that I need to merge those 2 arrays of objets into one.

Tree1:

Car

  • Electro

    • automatic
    • manual
  • Gas

Tree2:

Animals

  • Monkeys

    • small
  • Crocodiles

    • small
    • middle
      • green
      • blue
    • huge

Now I need to merge these trees into one CActiveDataProvider to display the hierarchy in my view :) Is that clear? :)

Ya, I’m afraid I don’t know mush about nested sets. I would use normal relational parent-child tables to accomplish the same effect.

Then you could get all the roots with one database visit (via the model’s search() function) and you could include all the trees of each root (via the “with” statement in the search() function).

But you should still be able to get all the trees for one root with a getter statement. I would start there and try it first.

Sorry I can’t help more.

Hi nightmove,

So your object hierarchy can be multi-level, and not limited to a specific depth of 2 or 3.

Am I right?

If that’s the case, I’m afraid CActiveDataProvider (or SQL in general) will not supply you with a simple solution. I guess you have to retrieve all children per each parent one by one in a recursive manner.

[UPDATE]

"nestedsetbehavior" will do this recursive search for you. What an extension !!

@Gerhard: The solution that you describe is called “adjacency list”. I already tried that but that is not suitable for me :)

@softark:

That is right. In my case there is no limit how many trees exist and it is not limited how deep the hierarchy is. :) At the moment I am trying to get all my trees into one set to push this set into a CActiveDataProvider but I still dont know how…

$tree1 = array(Objects…);

$tree2 = array(Objects…);

How can I put those to into one CActiveDataProvider? Isn’t there any chance to get these into one CActiveDataProvider? :(

Probably you misunderstand CActiveDataProvider.

It’s not an array of model instances, but it’s a provider of it. In other words, it’s a search query executor that returns an array of CActiveRecord model instances.

And the returned array should be a flat one, without nesting or hierarchy.

You should consider using CArrayDataProvider instead.

Or, instead of CGridView or CListView, CTreeView seems worth consideration to me, although it depends on how you want the hierarchy to be displayed on the screen. In that case what you need is not a data provider, but a hierarchical associative array.

I need no hierarchy in the data provider, as this is only a layout thing. Each object has a ‘level’ attribute and with that I realize the indentation of each. As a result of that I get a table (CGridView) where the levels are display with dashes. It is not a problem of displaying the items, it is just all about getting all items sorted and merged into one ‘container’. And this container I want to pass to an *DataProvider

See the attachment. That is the current view. But just for one tree and not for all :(

Ah, I’m very sorry. I didn’t read the document of NestedSetBehavior.

So it will do the recursive search automatically for you. What a great extension!

Do you want to get the whole trees? Or do you want to merge specific 2 trees out of the root trees?

If the former, I think you could do it as simple as the doc says:

http://www.yiiframework.com/extension/nestedsetbehavior/#hh10

Yes, this statment




Category::model()->findAll(array('condition'=>'root_id=?','order'=>'lft'),array($root_id));

indicates, that I have to use a specified root id. My problem is that I want to have all roots with its descendents. As I understand the documentation this is not possible. So I am looping through each root tree and collect them in, f. ex. two vars ($tree1, $tree2).

After that I want to merge these two vars into one, to make an CActiveDataProvider and pass this provider to a grid view :)

Did you try the following?




Category::model()->findAll(array('order'=>'lft'));



I didn’t test that, but sadly, it does not work. This statement gets all root categories at first and afterwards all descendents. Not the result I expected :(

But thanks for this hint!!!

So, it seems increasingly likly that I have to merge the two (or more) trees :(

OK.

I think you can use array_merge() as the last resort.

http://www.php.net/manual/en/function.array-merge.php

But what about something like this? … not tested.




Category::model()->findAll(array('condition'=>'root_id=NULL','order'=>'lft'));



I will go a different approach. I am going to render a list of checkboxex for all root categories. With an onclick for every checkbox I am going to render/refresh a gridview with the descendants of the root-category (checkbox).

The only problem I am having at the moment is that


$model = Category::model()->roots()->findAll();

returns an array with objects for each root category. I don’t know how to render the different checkboxes for each element inside the array :(

That doesnt work:




foreach($model as $root) {

           	

            	echo CHtml::activeCheckboxList(

              	$root, 'name',

              	CHtml::listData($model, 'id', 'name'),

              	array('template'=>'<li>{input} {label}</li>',)

            	);

        	}