CGridView with mulitple model

Hello,

can i use CGridView with mulitple model, i have two tables "product" and "tva", in product i have a forgein key "TVAID" which refer to TVA.TVAID, i want to diplay product with CGridView and put the TVA value (existing in table TVA)

Thank you

OK, i found a solution, if some one need it :

‘columns’=>array(

			...',


array( 


        'name' => 'tva',


        'header' => 'taux tva',


        'value' => 'TVA::gettauxTVA($data->TVAID)',


    ),

in model tva (tva.php)

public static function gettauxTVA($id) {

$tva = self::model()->findByPK($id);

return $tva->tauxtva;

}

I think you just need to write :




'columns'=>array(

	...',

        tva.tauxtva,



that will work, if you has set the relation() in your "product" model like this

‘tva’ => array(self::BELONGS_TO, ‘tva’,‘TVAID’);

I think you just need to write :




'columns'=>array(

	...',

        tva.tauxtva,



that will work, if you has set the relation() in your "product" model like this

‘tva’ => array(self::BELONGS_TO, ‘tva’,‘TVAID’);

Sure does, but how to make that searchable also?

Take a look to configuration of CActiveDataProvider.

It has a property sort, that is an object of type CSort. In this object you can edit the property attributes and add the new parameter.

In the function search() of the model (or wherever you are generating the dataprovider) you should do something like:




new CActiveDataProvier('modelName', array(

  'criteria'=>$CDbCriteria'

  'sort'=>array(

     'attributes'=>array(

        'tva'=>array(

            'asc'=>'...',

            'desc'=>'... DESC',

            'label'=>'TVA'

)


      )

  )

))



Thanks for the reply.

I’m doing it in a standard Yii generated CGridView. Don’t see anywhere like that in the standard CRUD to add that…? Except perhaps this:




$criteria->compare('type_id',$this->type_id,true);



Not sure how to change that to reference the type[‘name’] from my other table

There are 2 points in wich the standard code generate the dataprovider:

In the actionIndex there is a line




$dataProvider=new CActiveDataProvider('Article');



Here you can add the configuration for the CSort.

The actionAdmin uses a function of the model, so you have to edit the function search in your model class.

This function I have in my model, generated by Gii:




	public function search()

	{

		// Warning: Please modify the following code to remove attributes that

		// should not be searched.


		$criteria=new CDbCriteria;


		$criteria->compare('id',$this->id,true);

		$criteria->compare('type_id',$this->type_id,true);

		$criteria->compare('name',$this->name,true);

		$criteria->compare('username',$this->username,true);

		$criteria->compare('email',$this->email,true);


		return new CActiveDataProvider(get_class($this), array(

			'criteria'=>$criteria,

		));

	}



And these are the relations:




		return array(

			'adminAccess' => array(self::HAS_MANY, 'AdminAccess', 'admin_id'),

			'type' => array(self::BELONGS_TO, 'AdminTypes', 'type_id'),

		);



From “type” I want type[‘name’] to be searchable. I have tried modifying things a few different ways, including also the search array in rules, but haven’t found what works.

Tried this, no joy:


$criteria->compare('type.type',$this->type_id,true);

Ok, figured this out, so will post full details in the hope that it will help somebody else.

I have two database tables: administrators and admin_types. The admin_types table stores the admin type, eg. "Superadmin". These two tables are related using a foreign key "type_id" in the administrators table referencing the "id" column in the admin_types table.

The standard CRUD generated by Gii gives the type_id column as searchable, which is pretty much useless. Far better to search and display the actual type from the "type" column in the admin_types table.

In my Administrators.php class file I have the following:

Rules:




	public function rules()

	{

		// NOTE: you should only define rules for those attributes that

		// will receive user inputs.

		return array(

			// code removed for clarity

			// The following rule is used by search().

			// Please remove those attributes that should not be searched.

			array('id, type, name, username, email', 'safe', 'on'=>'search'),  // this previously had 'type_id'

		);

	}



Relations:




	public function relations()

	{

		// NOTE: you may need to adjust the relation name and the related

		// class name for the relations automatically generated below.

		return array(

			'type' => array(self::BELONGS_TO, 'AdminTypes', 'type_id'),

		);

	}



Search:




	public function search()

	{

		// Warning: Please modify the following code to remove attributes that

		// should not be searched.


		$criteria=new CDbCriteria;


		$criteria->compare('id',$this->id,true);

		$criteria->compare('type.type',$this->type,true);

		$criteria->compare('name',$this->name,true);

		$criteria->compare('username',$this->username,true);

		$criteria->compare('email',$this->email,true);

		$criteria->with = 'type'; // this is refering to the related database table alias generated by Gii in relations() above, actual table name is 'admin_types'


		return new CActiveDataProvider(get_class($this), array(

			'criteria'=>$criteria,

		));

	}



My /views/administrators/admin.php file is modified as follows:




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

	'id'=>'administrators-grid',

	'dataProvider'=>$model->search(),

	'filter'=>$model,

	'columns'=>array(

		'id',

		array('name' => 'type',

                      'value' => '$data->type->type',

                      'type' => 'text'),

		'name',

		'username',

		'email',

		array(

			'class'=>'CButtonColumn',

		),

	),

)); ?>



That’s it! Works like a charm :)

No doubt I will modify this further to use a drop menu rather than text column for admin types, but thought it best to post the simple text input version.

To change this to a drop menu filter (much more suitable), I again modified my /views/administrators/admin.php file:




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

	'id'=>'administrators-grid',

	'dataProvider'=>$model->search(),

	'filter'=>$model,

	'columns'=>array(

		'id',

		array('name' => 'type',

		      'filter' => CHtml::listData(AdminTypes::model()->findAll(), 'type', 'type'),

                      'value' => '$data->type->type'),

		'name',

		'username',

		'email',

		array(

			'class'=>'CButtonColumn',

		),

	),

)); ?>



Now to figure out how to modify the advanced search :)

Ok, easy. In /views/administrators/_search.php:




	<div class="row">

		<?php echo $form->label($model,'type'); ?>

		<?php echo $form->dropDownList($model, 'type', CHtml::listData(AdminTypes::model()->findAll(), 'type', 'type'), array('prompt'=>'Please Select')); ?>

	</div>



This is awesome and implemented in my search as well, except I get extra records found when I use the text and drop down filter examples detailed above. My field is a varchar(3) field. db is innodb/mysql if it makes any diff.

ie I get BAL, BAL BAN (3 results) when selecting ‘BAL’.

Similarly I get BAL, BAL, ALS, ALS, ALS, ALS, BAN (7 results) when I dropdown and select ‘ALS’.

Weird, any clues why and others getting similar unexplained results?

If you turn on query logging you will see that the query uses "LIKE". Not sure how to change this behaviour.

Ugghh! Found a bug!

If an option is not selected, the following PHP error occurs:

Uninitialized string offset: 0

Ok, found problem and edited above code to:




$criteria->compare('type.type',$this->type,true);



Backslider, is this for searching using the form fields at the top of each column or the advance search form or both?

Disregard this question. I realize now it was a dumb question. :P

I just wanted to say that I got this working and it’s exactly what I’ve been looking for. Thanks for posting it! :)

Backslider, I’ve noticed that I am unable to sort the data by this column by clicking on its column header like I can on the other columns. Is this a limitation of your solution or have I done something wrong?

Thanks!

I am looking to modify the CGridView in instances where the two tables are not directly related.

Given the db:




City

  CityId (PK)

  RegionId (FK)

Region

  RegionId (PK)

  CountryId (FK)

Country

  CountryId (PK)

  Name 



I want to display the Name of Country in the CGridView I have for City. I have been trying many different variations, including the different components of this thread, to get this working but have been unsuccessful.

Is what I am trying to do possible? If so can someone help me out with this please?

I have been through this. Let’s analize a simple project.

We have 3 tables in DB:

profile (

id INT PK,


name	CHAR(30)

)

user (

id INT PK,


users_profile INT FK REFERENCES profile(id)

)

message (

id INT PK,


sender INT FK REFERENCES user(id),


receiver INT FK REFERENCES user(id),

)

We generate Yii project and model classes based on that DB tables.

We want to show all messages in tabular format including columns:

ID

Senders ID

Receivers ID

Senders Name

Receivers Name

All of these columns have to be sortable and provide search ability.

That is what we have to do:

Model classes User.php and Profile.php can stay as they were generated.

We have to mess up in Message.php.

  1. Add two new fields to our Message class:

	public $sendersName;   

	public $receiversName; 

  1. Modify rules to enable searching. We must add the two fields from previous step to safe/on search rule.

	array('id, sender, receiver, sendersName, receiversName', 'safe', 'on'=>'search'),

  1. Check the names of Yii generated relations corresponding to sender and receiver. They should look like this:

	'sender0' => array(self::BELONGS_TO, 'User', 'sender'),

	'receiver0' => array(self::BELONGS_TO, 'User', 'receiver'),

  1. We can modify attribute labels:

	public function attributeLabels()

	{

		return array(

			'id' => 'ID',

			'sender' => 'Sender',

			'receiver' => 'Receiver',

		);

	}

Change to:


	public function attributeLabels()

	{

		return array(

			'id' => 'ID',

			'sender' => 'Senders ID',

			'receiver' => 'Receivers ID',

			'sendersName' => 'Senders Name',

			'receiverName' => 'Receivers Name',

		);

	}

  1. In Message::search() method:

We have to modify the $criteria inside search() method. Precisely $criteria->with property.

We must add every relation that will be used in our search. We will use these relations:

sender0

receiver0

These will give us two user objects. But to get names of sender and receiver we have to use User class relations. In my case

User class has these relations:


	'messages' => array(self::HAS_MANY, 'Message', 'sender'),

	'messages1' => array(self::HAS_MANY, 'Message', 'receiver'),

	'usersProfile' => array(self::BELONGS_TO, 'Profile', 'users_profile'),

We are interested only in usersProfile relation, cause it will give us an object of Profile class which holds users name that we want to display.

So, we have to insert it to our $criteria->with property as well.

Ther is a catch. We can do it like this:


$criteria->with=array('sender0.usersProfile', 'receiver0.usersProfile');

But that would result in bad "Not unique table/alias" exception. So, I propose this solution:




$criteria->with=array('sender0.usersProfile'=>array('alias'=>'sendersProfile'), 'receiver0.usersProfile'=>array('alias'=>'receiversProfile'));   



  1. Now that relations are set we can add two comparison expressions to enable search:



		$criteria->compare('sendersProfile.name',$this->sendersName, true);  		

		$criteria->compare('receiversProfile.name',$this->receiversName, true); 

  1. Then we can add sort to our CActiveDataProviders constructor call to enable sorting:

		return new CActiveDataProvider(get_class($this), array(

			'criteria'=>$criteria,

			'sort'=>array(

				'attributes'=>array(

					'sendersName'=>array( 'asc'=>'sendersProfile.name ASC', 'desc'=>'sendersProfile.name DESC' ),

					'receiversName'=>array( 'asc'=>'receiversProfile.name ASC', 'desc'=>'receiversProfile.name DESC' ),

				),

			),

		));

But, if we leave it like that we will see that now ‘ID’, ‘Senders ID’ and ‘Receivers ID’ are not sortable. We have to add them to our sort as well:


		return new CActiveDataProvider(get_class($this), array(

			'criteria'=>$criteria,

			'sort'=>array(

				'attributes'=>array(

					'id',

					'sender',

					'receiver',

					'sendersName'=>array( 

						'asc'=>'sendersProfile.name ASC', 

						'desc'=>'sendersProfile.name DESC' 

					),

					'receiversName'=>array( 

						'asc'=>'receiversProfile.name ASC', 

						'desc'=>'receiversProfile.name DESC' 

					),

				),

			),

		));

  1. In view just add CGridView:

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

	'id'=>'message-grid',

	'dataProvider'=>$model->search(),

	'filter'=>$model,

	'columns'=>array(

		'id',

		'sender',

		'receiver',

		array(

			'name'=>'sendersName', 

			'value'=>'$data->sender0->usersProfile->name',

		),	

		array(

			'name'=>'receiversName', 

			'value'=>'$data->receiver0->usersProfile->name',

		),			

		array(

			'class'=>'CButtonColumn',

		),

	),

)); ?>

And its all. I hope It helped someone. Also, Hi, everyone, it is my first post:)

I am trying to search my class name field where there are multiple classes in a session. I get the data to show on the screen but my search/filter isn’t working.

I have the following:

SessionClass model


public function rules()

	{

		// NOTE: you should only define rules for those attributes that

		// will receive user inputs.

		return array(

			array('session_id, class_id', 'required'),

			array('session_id, class_id', 'numerical', 'integerOnly'=>true),

			// The following rule is used by search().

			// Please remove those attributes that should not be searched.

			array('session_id, class_id, class_name', 'safe', 'on'=>'search'), 

		);

	}


public function relations()

	{

		// NOTE: you may need to adjust the relation name and the related

		// class name for the relations automatically generated below.

		return array(

			'session' => array(self::BELONGS_TO, 'Session', 'session_id'),

			'class' => array(self::BELONGS_TO, 'Class', 'class_id'),

			

		);

	}


public function search()

	{

		// Warning: Please modify the following code to remove attributes that

		// should not be searched.


		$criteria=new CDbCriteria;


		$criteria->compare('session_id',$this->session_id);

		$criteria->compare('class_id',$this->class_id);

		$criteria->compare('class.class_name',$this->class_name,true);  

		$criteria->with = 'class';

		

		return new CActiveDataProvider($this, array(

			'criteria'=>$criteria,

			'sort'=>array(

							'attributes'=>array(

                                        			'class_name'=>array( 

                                        								'asc'=>'class.class_name ASC', 'desc'=>'class.class_name DESC'

																		)

                                        		)

                        

						)

		));

	}

In my view I have the following




$this->widget('zii.widgets.grid.CGridView', array(

												'id'=>'SessionClassGrid',

												'dataProvider'=>$model->search(),

												'filter'=>$model,

    		'columns'=>array(

    				array(	'name' => 'class',

                      			'value' => '$data->class->class_name',

                      		        'type' => 'text'),

    											

																),	

    											)

);

I have also defined public $class_name in my model. It shows the class but it is not searchable. What did I miss?