Many models in one controller

I have diferent sections on my site: articles, news, events, etc. Each section has it’s own model (ergo database column) and controller. But on a home page I want to show all of this types of content sorted by the time of posting.

Is there a buld-in mechanism in Yii, which allows controller to collect data from many models and then sort them by specific variable (creation time in this case)?

nope

if you want to create kind of latest news- or latest events blocks - use widgets

No, but why not seperate search results? eg like this. If you want to display everything in one list (with same sorting etc.), you may use only one model:




class Content extends CActiveRecord

{


   const TYPE_ARTICLE = 1;

   const TYPE_EVENT = 2;

   const TYPE_NEWS = 3;

   ...


}



The problem is that every model has diferent MySQL table so diferent fields also. I can imagine that building one model for every type of content is very difficult then, istn’t it?

Well it depends.

If you have 2 tables like this:

Article

id

createdAt

rating

title

text

News

id

createdAt

rating

title

text

You could merge them into one table and one model:

Content

id

type (1 = Article, 2 = News)

createdAt

rating

title

text

If you have many different fields for each table and you even want to sort by them it makes no sense of course.

using database views

I have many diferent fields, but there is one which does not change: create_time. This is a field my content should be ordered by.

Views are very slow, specially in MySQL, AFAIK.

Please don’t laugh at me,I’m a chinese boy,my english is very bad.

In such a case,as far as your requirement,i think views is a better way.

using views,you only need to query once,if you use many AR model you need to query more than once.

I think a view is a procedure,so have’t so much performance problem.

You could also take it the way Y!! suggested and just abstract out the "content" model without building a new table for it - ie, having it live only in the application, and get loaded when you explicitly call it to be loaded.

Following:

Content

id

type (1 = Article, 2 = News)

created_time

rating

title

text

First load the array with "Articles"

Then append the array with "News"

Sort the array on "Create_Time"

Now you have an array of id, title, type, create time, without having to persist it in a db.

Maybe it’s an option to build a special DataProvider class for it? So that you can assign the model class names you want to list.

I think my own DataProvider will be the solution. Thanks!

I think that sort data in php is a very bad idea for performances, if you are planning to do something like that, better to use views, I guess that is faster than sort arrays in php AFAIK.

You can get all the data in one shot by using UNION and standardizing the field with AS.

Like that you can using LIMIT on the derived table and retrive only the items you need.

Of corse you can implement this query in your own DataProvider, like Y!! suggest

Hi.

I’m doing something somewhat similar to what Y!! suggested, although my problem (representing multiple models in a tree structure) and thus solution are more complex.

What I’ve done is to create a third table, much like the Content table, but which doesn’t contain any content. Instead, it has common fields (date, createdAt, and so on) plus two fields, Model and ModelID, which contain the Model name and primary key linked.

The generation becomes more complex, you’ve got to create/modify two items every time, but it gives you some more freedoms. If you want to display them in a standard list view, change the _view.php for your binding model to something like:




eval('$model = '.$data->Model.'::model();');

$this->renderPartial('../'.lcfirst($data->Model).'/_view',$model->findByPk($data->ModelID));



I’ve not checked that code, it’s just an idea but it should work assuming I have the naming conventions and how Yii parses view locations correctly. If you think it looks like what you want to do, gimmie a shout and I’ll test it properly. The advantage of this method is that it scales to any number of models, the disadvantage is the complexity and the fact you need to modify two records. Also, there’s logic in the view, but I think a little is OK.

So that means for the third table/model, you add relations for the 2 "real" models?

// I see you not do it. Maybe it’s an option.

Try this

Site controller




public function actionIndex()

	{

		// renders the view file 'protected/views/site/index.php'

		// using the default layout 'protected/views/layouts/main.php'


	$states_model = new States();

        $states = $states_model->findAll();


        $types_model = new Types();

        $types = $types_model->findAll();


	$limit = 3;

        $dataProvider=new CActiveDataProvider('Properties', array(

        							'criteria'=>array(

									   'condition'=>' featured=1 ',

									   'limit'=> $limit,

							           'order'=>'RAND()',

								    ),

								    'pagination'=>false,

								));




		$this->render('index',

			array('states'=> $states,

     			'types' => $types,

     			'dataProvider'=>$dataProvider,

			)

		);

	}



views site index.php




<h4>Featured Properties</h4>


<?php $this->widget('zii.widgets.CListView', array(

	'dataProvider'=>$dataProvider,

	'itemView'=>'_view',

	'ajaxUpdate'=> false,

    'enablePagination' => false,

)); ?>


		<h4><img src="<?=Yii::app()->request->baseUrl;?>/images/r.gif" /> By State</h4>

		<? foreach ($states as $state){?>

			<?=CHtml::link(CHtml::encode($state->name), array('properties/index', 'state'=>$state->id)); ?><br />

		<? } ?>

		<br /><br />

		<h4><img src="<?=Yii::app()->request->baseUrl;?>/images/r.gif" /> By Property Type</h4>

		<? foreach ($types as $type){?>

			<?=CHtml::link(CHtml::encode($type->name), array('properties/index', 'type'=>$type->id)); ?><br />

		<? } ?>



Yes, it’s definitely an option. I’m not 100% used to the Yii relations system yet, I generally do relations in SQL. This is partially as I’m not fully familiar, I have 2 months to create a big site and am learning Yii as I go, and also as sometimes the stuff I need doesn’t seem nicely covered by the relations system (IE: Getting all items that don’t have a match from a relation).

Lilt, what you are suggesting is what came to my mind last time. So you can have one general table content and few more tables for every type of content, ie article_fields, news_fields, event_fields etc.

What you can do next is to join fields in a model (as you suggest) or prepare joints in SQL views (as few other Guys suggested). You can easily sort content by one of the common fields but also get all fields at once if you are displaying only one type of content.

Unfortunately, either way there are two times more SQL queries. And the question is: how does it influence on performance? Any experience, Lilt?

and how exactly do u do that? sorry i’m a complete noob. ::)