SUM query in a CGridView column - yii

I'm playing around with the CGridView widget and I'm trying to find out if it would be appropriate to use it in the following context:
Lets say I have two database tables, Car and Accident. Each car may be associated to zero or more accidents. I would like to have a column in the grid view containing the number of accidents for each car, assuming that a row in the grid view represents a car.
Is this feasible using a GridView widget, or should I try some other approach?

You can define relation for your Car model as follows:
public function relations()
{
return array(
'accidents' => array(self::HAS_MANY, 'Accidents', 'Id'),
'accidentsCount' => array(self::STAT, 'Accidents', 'AccidentId'),
);
}
If you use count($model->accidents) then you will load all related models and then use PHP count function to get number of models, if you care about performance and memory you will use stat relation accidentCount to get number of accidents.

If you have the relation "cars have one to many accidents" a simple count on the relation can be used. $model is car here.
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'car-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'name',
array(
'header'=>'Accidents',
'value'=>'count($data->accidents)',
),
),
));

The easiest way is to give the car model a "getNumberOfAccidents()" getter. You can then use that in the grid as a regular column (numberOfAccidents). Easy as pie :)

Related

Get all data using CActiveDataProvider

I have a model InboxMessageHelper with relations like
'message', 'sender' and 'receiver' and I am using the following criteria to query data:
$model = new CActiveDataProvider('IndividualMessageHelper', array(
'criteria'=>array(
'condition'=>'receiver_id = '.Yii::app()->user->id,
'order'=>'message.created_at DESC',
'with'=>array('message', 'sender', 'receiver'),
'together'=>true,
),
));
I want to get all data (i.e. including the relations data) inside the controller and form a JSON, but the problem is that i cannot access related fields data. I can see that the data is available when I use
CVarDumper::dump()
when I try to encode $model->data then only data from current table gets encoded. How should I go about it?
I don't think CActiveDataProvider can be used in this way. You need to be working with the model. So you'll need something like this in your controller.
$models = IndividualMessageHelper::model()->findAll('receiver_id = '.Yii::app()->user->id);
foreach($models as $model){
$json[] = $model->getAttributes; //This won't get any model properties you've declared
yourself, only database columns
}
//Now get the related records and add them to the array.
array_push($json, $model->getRelated('message')->getAttributes());
array_push($json, $model->getRelated('sender')->getAttributes());
array_push($json, $model->getRelated('receiver')->getAttributes());
echo json_encode($json);

Filter By Looked Up Value In Cgridview

My early days of Yii. Have searched and can't find any clues on this hence post. I have modified the admin.php layout to lookup values from a couple of related tables as follows
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'silo-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'id',
array(
'name'=>'silo_id',
'value'=>'$data->silo->name',
'header'=>'Parent',
),
'name',
array(
'name'=>'silo_type',
'value'=>'$data->siloType?$data->siloType->type:"no silo type"',
),
array(
'class'=>'CButtonColumn',
),
),
)); ?>
This is great and all works ... apart from the fact that the filter fields are still looking up the numerical values rather than the text values (e.g. I want to filter on a text 'description' rather than the UID that represents that description).
How do I go about changing this?
Thanks
A quick method would be to use drop down lists instead of text fields for the filter fields. You could populate this as follows
'name'=>'silo_id',
....
'filter'=>CHtml::listDataEx(Silo::model()->findAll(),'id','name')
A longer trickier method is to alter your query to include a search for the foreign field. You would need to store the search term. For a single such field you would need to
Add a public field to your model say $silo_name
Set this field to safe in your model's rules() method
Set the required columns filter to
'filter'=>CHtml::activeTextField($model,'silo_name')
Finally edit your model's search() method to reflect the above
$criteria->together=true;//perform a single query
$criteria->with=array('silo'=>array('id','name'));//specify which relations to add to the query
$criteria->compare('silo.name',$this->silo_name,true);//$silo_name is a public field

Yii's CArrayDataProvider and new rows

Is there a way to show in a Grid that takes it data from CActiveDataProvider some new rows that are not actually yet into the database?
here's my scenario..
I have to fill by X times (the quantity of a product in a bill) and provide to each row the possibility to be edited and saved
Product QTY
product1 3
|
____ Edit item 1 (not yet in the db)
____ Edit item 2 (not yet in the db)
____ Edit item 3 (not yet in the db)
So it's kind of like a master detail grid (That I've sorted it out how to do it..)
but I can't display things on a grid that arren't yet in the database..
I know I have to create an array of temporary models like $model[]=new MODEL(); and push the somehow to the CActiveDataProvider but don't know the syntaxis...
You can't do this with CActiveDataProvider because as the documentation says
CActiveDataProvider provides data in terms of ActiveRecord objects which are of class modelClass. It uses the AR CActiveRecord::findAll method to retrieve the data from database.
However, there is CArrayDataProvider. You can put arbitrary data in it, ie. merge an array of real objects with an array of empty ones and use that.
$rawData=array(
array('id'=>1, 'username'=>'from', 'email'=>'array'),
array('id'=>2, 'username'=>'test 2', 'email'=>'hello#example.com'),
);
$arrayDataProvider=new CArrayDataProvider($rawData, array(
'id'=>'id',
'sort'=>array(
'attributes'=>array(
'username', 'email',
),
),
'pagination'=>array(
'pageSize'=>10,
),
));
As you can see, the data here is a simple array, but you can even sort the data.

Yii CGridView - Display and paginate from two sources

I have two data sources
1) Database
2) Memcached or Totally different database
From 1st database I am getting the group members list IDs ( has more than 10 thousand rows) and after getting the list I query second database or hit Memcached to get the actual details
$connection = Yii::app()->db;
$sql = 'select user_id,joined_date from group_data where group_id=:group_id ';
$dataProvider = new CSqlDataProvider($sql, array(
'keyField' => 'user_id',
'sort' => array(
'attributes' => array(
'user_id',
'joined_date'
),
'defaultOrder'=>array(
'user_id ASC',
)
),
'pagination' => array(
'pageSize' => 30,
),
'totalItemCount' => $count,
));
$connection_master = Yii::app()->masterdb;
In the above data provider , I am not sure how to include my second database and get the actual user name since it is in other database .No direct connection from one database to other.
Here the problem is pagination is based on the first database table ( 30 records per page) and the actual data is from second table .
Any idea on how to implement this ?
Thank You
You could build your own custom data provider class. You can extend it from CDataProvider and implement the missing abstract methods:
abstract protected function fetchData();
abstract protected function fetchKeys();
abstract protected function calculateTotalItemCount();
Notice, that your storage model already comes very close to these methods: You store the IDs in one database and the actual data in another. So you can focus on one DB at a time in each of these methods.
You'd start with fetchKeys(). There you need to query the keys (=IDs) from your second database. Have a look at CSqlDataProvider::fetchData() to see how to use the CPagination object from $this->getPagination() to find out the current limit and offset. If you need sorting you'd also inspect the CSort object from $this->getSort() for current sort settings. With all that data available you should be able to build the query for the IDs on the currently requested result page.
Then in fetchData() you can obtain those keys through $this->getKeys(). You should not call fetchKeys() directly, because with getKeys() the result from fetchKeys() is "cached", so if it's called again later in the same request, there won't be another query. With that keys you now can easily query your main database. The result will represent the rows on the current page.
Finally you have to implement calculateTotalItemCount(). This is very easy again: Just query your second DB for the total number of items.
That's all you need to do - the code in the base CDataProvider class will take care of the rest and call your methods on demand.

How to get n records from a Yii data provider?

I have a model which has the following relationship declared:
'messages' => array(self::HAS_MANY, 'WallMessages', 'liga_id',
'condition'=>'specific_post.parent_message_id IS NULL',
'order'=>'specific_post.date DESC',
'alias'=>'specific_post'),
Im trying to use this collection within a CListView with a custom pagination. However, if for example my pages are of size 5, i need to get from elements 5 through 10 to populate the view.. How can i get n elements from a defined relationship?
you can use model's serach() or custom CDbCriteria yourself.
CDbCriteria