Sorting CListView in yii - php

Please consider this:
class User extends CActiveRecord
{
...
public function relations()
{
return array(
...
'articleCount' => array(self::STAT, 'Article', 'userid'),
...
);
}
...
}
Now, I need to create a $dataProvider = new CActiveDataProvider(...) to feed a CListView widget to which I want add articleCount to the property sortableAttributes so that I can sort User records according to the number of articles the user is the author for.
What are the most convenient method? what are the other alternatives?

Well, it took me a while, but I finally got it figured out. ;)
First, make sure you still have the same STAT relation you mention in your question.
Then, in your search() method where you are building your CDbCriteria for the CActiveDataProvider, do something like this:
public function search() {
$criteria=new CDbCriteria;
$criteria->select = 't.*, IFNULL( count(article.id), 0) as articleCount';
$criteria->join = 'LEFT JOIN article ON article.userid = t.id';
$criteria->group = 't.id';
// other $criteria->compare conditions
$sort = new CSort();
$sort->attributes = array(
'articleCount'=>array(
'asc'=>'articleCountASC',
'desc'=>'articleCountDESC',
),
'*', // add all of the other columns as sortable
);
return new CActiveDataProvider(get_class($this), array(
'criteria'=>$criteria,
'sort'=>$sort,
'pagination'=> array(
'pageSize'=>20,
),
));
}
Then, in your View where you output your CGridView, do this like so:
<?php $this->widget('zii.widgets.grid.CGridView', array(
'dataProvider'=>$model->search(),
'columns'=>array(
'articleCount',
// other columns here
)
)); ?>
This works, I tested it (although I did not use the exact same names, like 'article', but the basic idea should work :) I did add some bonus features so it works better (like the IFNULL magic) but most of the credit goes to this Yii forum post:
http://www.yiiframework.com/forum/index.php?/topic/7061-csort-and-selfstat-to-sort-by-count/
I hope this helps! They should add better support for this though, so you don't need to make these tricky SELECT statements. Seems like something that should 'just work', I would file an 'enhancement' request in the Yii bug tracker.

Related

Yii how to search for models (populate activedataprovider) with query result?

I use a search symptoms form and I pass off the symptomCode to my diseaseController
both symptoms and diseases have model classes, but the table connecting the 2 does not.
I use this query to find all the diseases that have the specific symptom
$diseaseCodes = Yii::app()->db->createCommand()
->select ('ICD10')
->from('tbl_disease')
->join('tbl_symptom_disease', 'tbl_disease.ICD10=tbl_symptom_disease.diseaseCode')
->where('symptomCode=:symptomCode',
array(':symptomCode'=>$_GET['symptomCode']))
->queryAll();
Now I want to know how I can use this to populate a dataprovider to populate a gridview
One idea I had was to create a custom model function
public function queryResultSearch($diseaseArray)
{
$criteria=new CDbCriteria;
$criteria->compare('ICD10',$diseaseArray,true);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
And use this to render yii's admin action (for disease models), but I can't get it to work because probably my entire way at going at this is wrong.
Can someone help me please? How to use a mysql query result to populate an activedataprovider object.
Thank you for your time
In your controller action you can try this:
$model=new ModelName('search');
$model->unsetAttributes();
if(isset($_GET['ModelName']))
$model->attributes=$_GET['ModelName'];
$diseaseCodes = Yii::app()->db->createCommand()
->select ('ICD10')
->from('tbl_disease')
->join('tbl_symptom_disease', 'tbl_disease.ICD10=tbl_symptom_disease.diseaseCode')
->where('symptomCode=:symptomCode',
array(':symptomCode'=>$_GET['symptomCode']))
->queryAll();
$diseaseArray=array();
foreach ($diseaseCodes as $dc) {
$diseaseArray[]=$dc['ICD10'];
}
$criteria=new CDbCriteria;
$criteria->compare('ICD10',$diseaseArray,true);
$dataProvider = new CActiveDataProvider(get_class($model),array('criteria'=>$criteria));
$dataProvider->criteria->mergeWith($model->search()->criteria);
$this->render('view',array(
'model'=>$this->loadModel($id),
'dataProvider'=> $dataProvider,
));
You are going to want to use CArrayDataProvider. This is less than ideal.. and you will have to implement all your sorting and pagination on your own
You are much better off using a model and implementing similar to
public function queryResultSearch($diseaseArray)
{
$criteria=new CDbCriteria;
$criteria->compare('ICD10',$diseaseArray,true);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
To make your first model I would personally use Gii.

fetch photos of an album in Yii (relation case)

I have two tables , one album and one photo. the photo table has a FK (album_id) which refer to id in album table.
now I want to show photos in CListview but I don't know how. :(
As I see it is uses dataProvider and I don't know how to combine relation with it.
would you help me?
Thank you masters.
It's simple
$dataProvider=new CActiveDataProvider('Photo', array(
'criteria'=>array(
'condition'=>'album_id=:album_id',
'params'=>['album_id'=>$album_id]
)
));
Then in view use this dataProvider:
$this->widget('zii.widgets.CListView', array(
'dataProvider'=>$dataProvider,
'itemView'=>'_photo'
));
First you should create the relation in you models.
In Album.php add sth like this:
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(
'photos'=>array(self::HAS_MANY, 'Photo',array('album_id'=>'id')),
);
}
Similar in Photo.php:
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(
'album'=>array(self::BELONGS_TO, 'Album',array('id'=>'album_id')),
);
}
Now you can use it in your code:
$dataProvider=new CActiveDataProvider('Photo', array(
'criteria'=>array(
'condition'=>'album_id='.$yourAlbumId,
)
));
$this->widget('zii.widgets.CListView', array(
'dataProvider'=>$dataProvider,
...
));
If you need to refer to the Album in the CListview (let's say you have a field called "title" in the Album model), you can refer to it as $data->album->title in your items template.

Change yii crud generated page

I have 2 models: taxiDrivers and taxiOrders. In taxiOrders I have a field driver_id, but instead of just an id, I want to output driver's name (which is located in taxiDrivers). Both models are generated via gii, and crud tools are also generated. The page which is needed to be changed is taxiOrders/admin (view: admin.php, models: TaxiOrders.php, TaxiDrivers.php and respective controllers)
2 DCoder: Thanks dude! but one more query I have and hope you can clearify: I have a standart generated admin.php view page with following code:
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'taxi-orders-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'id',
'uid',
'did',
'type',
'notes',
//'or_lat',
//'or_lng',
//'des_lat',
//'des_lng',
'cost',
'rating',
'date',
'time',
'status',
array(
'class'=>'CButtonColumn',
),
),
)); ?>
and below code is for controller:public function actionAdmin()
{
$model=new TaxiOrders('search');
$model->unsetAttributes(); // clear any default values
if(isset($_GET['TaxiOrders']))
$model->attributes=$_GET['TaxiOrders'];
$this->render('admin',array(
'model'=>$model,
));
}
So can u please show me where I could put your manipulations.
The Yii way would be to define a Relation between the two model classes (and enforce it with a Foreign Key). Then Yii would know how the two classes are related and you would have an easier time loading the related data.
In the TaxiOrders model class, a relation would look like this:
/**
* #property TaxiDrivers $Driver
*/
class TaxiOrders extends CActiveRecord {
// ...
public function relations() {
return array(
'Driver' => array(self::BELONGS_TO, 'TaxiDrivers', 'driver_id'),
);
}
// ...
}
In the controller, when you load the order data, you can prefetch the associated driver data like this:
public function actionOrderInfo($orderID) {
$order = TaxiOrders::model()->with('Driver')->findByPk($orderID);
// render it
}
with('Driver') will make sure that each returned order has its driver info already loaded, no matter if you need to find one record or a lot of records. It is a lot more efficient than trying to load that related data yourself.
In the view, you can output the driver info like this:
echo CHtml::encode($order->Driver->Name);
Unless you have a foreign key to ensure data integrity, it is possible that the Driver has been deleted without clearing his existing orders... In that case $order->Driver will be NULL and the line above will cause an error. Figuring out how to avoid it should be obvious.
In TaxiOrders admin use this to display driver name
TaxiDrivers::getName($data->driver_id);
Now in the TaxiDrivers Model write a function with sql query to get the driver name like this...
public function getName($getid) {
$sql = "SELECT name from `taxi_drivers` where `driver_id`='$getid'";
$command=yii::app()->db->createCommand($sql);
$rs = $command->queryScalar();
return $rs;
}
I Hope it will help you..
I always prefer Query Builder approach instead of Active record approach in joined scenarios. Like that
First Get Data through Query Builder
public function getTaxtOrder($id) {
$select = Yii::app()->db->createCommand()
->select('to.*, td.name as driver_name')
->from('TaxiOrders to')
->join('TaxiDriver td', 'td.id = to.driver_id')
->where('to.id = :id', array(':id' => $id));
return $select->queryRow();
}
Then Pass through controller
$data = TaxiOrders::model()->getTaxtOrder($id);
$this->render('view',array(
'data' => $data
));
Last use this into views
$this->widget('zii.widgets.CDetailView', array(
'data'=>$data,
'attributes'=>array(
array('label' => 'Order No', 'value' =>$model['order_no']),
array('label' => 'Driver Name', 'value' =>$model['driver_name']),
array('label' => 'Date', 'value' =>$model['order_date']),
),
));
This approach easily and flexibly work with multiple joined tables than Active Record Approach.

unable to fetch records from multiple tables using join

I am using Yii framework. i am wonder how i can get records from multiple tables i did research but couldn't find any usefull link i am using following code for this please let me know where i am missing
my model Task.php
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(
'prj_user' => array(self::BELONGS_TO, 'User', 'id'),
);
}
model User.php
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(
array('task', self::HAS_MANY, 'Task','project_id')
);
}
and this is my main controller
$criteria = new CDbCriteria;
$criteria->compare('t.id', 1);
$criteria->with = array( 'prj_user' => array('select' => 'username,title,roles', 'joinType'=>'inner join'));
$rows = Task::model()->findAll( $criteria );
but still i am getting columns only from task table but i need more three columns from users table please help me
Let Yii worry about joining your tables. Your relations looks fine so you should be able to access them directly
For example, what does this return?
foreach ($rows as $task)
{
if ( isset($task->prj_user) )
echo $task->prj_user->username . "<br>";
}
Or this?
this->widget('zii.widgets.grid.CGridView', array(
'dataProvider'=>new CActiveDataProvider('Task'),
'columns'=>array(
'id',
'prj_user.username',
'prj_user.title',
'prj_user.roles',
)
));
->with() is used for eager loading, so at this point you probably don't need it. In fact, unless I misread you completely, you can remove your criteria all together.

Yii framework: Using data from related Active Record models for searching

Yii 1.1 application development Cookbook explain a method for using data from related Active Record Models for searching the related models as well. This method is explained in page number 193 and 194. i have tried to integrate this method in to my application but it does not work. could anybody explain me whether this feature is still available in Yii framework version 1.1.8
At this location also i could find comments for searching data form related active record models. But it also does not work.
http://www.yiiframework.com/doc/api/1.1/CDbCriteria
I have order table and user table
Order table and User table has One to many Relation.
User has many orders and order has exactly one user.
So , i am editing following CDbCriterial to include user tables name and email field in to Order tables search entries.
Order table has following 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(
'comments' => array(self::HAS_MANY, 'Comment', 'order_id'),
'user' => array(self::BELONGS_TO, 'User', 'user_id'),
'orderstatus' => array(self::BELONGS_TO, 'Orderstatus', 'orderstatus_id'),
'commentCount' => array(self::STAT, 'Comment' , 'order_id')
);
}
This is the search/filter conditions with user table's name filed included
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);
$criteria->compare('order_create_date',$this->order_create_date,true);
$criteria->compare('price',$this->price,true);
$criteria->compare('bank_account_number',$this->bank_account_number,true);
$criteria->compare('hardwaredetail_Id',$this->hardwaredetail_Id);
$criteria->compare('user_id',$this->user_id);
$criteria->compare('order_update_date',$this->order_update_date,true);
$criteria->compare('is_received',$this->is_received);
$criteria->compare('order_received_date',$this->order_received_date,true);
$criteria->compare('is_notify_by_email',$this->is_notify_by_email);
$criteria->compare('warehouse_offered_price',$this->warehouse_offered_price,true);
$criteria->compare('warehouse_offered_price_date',$this->warehouse_offered_price_date,true);
$criteria->compare('orderstatus_id',$this->orderstatus_id);
$criteria->together = true;
$criteria->with = array('user');
$criteria->compare('user.name',$this->user,true);
//$criteria->compare('user.name',$this->user->name);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
and Order admin page is edited to display the name filed as follows
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'order-grid',
'dataProvider'=>$model->search(),
//'filter'=>$model,
'columns'=>array(
'id',
'order_create_date',
'price',
'bank_account_number',
array(
'name'=>'user',
'value'=>'$data->user->name'
),
),
));
Error message returned
After solving the id column ambiguity problem by applying the solution that thaddeusmt gave i have faced with the following error message.
Thanks in advance for any help
I could find the answer. This is all i did.
Following are the two tables i have. Order and User
I have Order/Admin page, Default generated code provide searching order table data only. i want to relate user tables name field in the search criteria.
This is the initial look of the search page.
I want to integrated user name filed from other table in to this search. then final look will be as follows.
so these are the steps i did.
first in the Order model i have following 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(
'user' => array(self::BELONGS_TO, 'User', 'user_id'),
);
}
This is generated by Yii framework , i did nothing :)
Then , i changed the search method as follows
public function search()
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria=new CDbCriteria;
$criteria->compare('t.order_create_date',$this->order_create_date,true);
$criteria->compare('t.price',$this->price,true);
$criteria->compare('t.bank_account_number',$this->bank_account_number,true);
$criteria->compare('t.hardwaredetail_Id',$this->hardwaredetail_Id);
//$criteria->compare('user_id',$this->user_id);
$criteria->compare('t.order_update_date',$this->order_update_date,true);
$criteria->compare('t.is_received',$this->is_received);
$criteria->compare('t.order_received_date',$this->order_received_date,true);
$criteria->compare('t.is_notify_by_email',$this->is_notify_by_email);
$criteria->compare('t.warehouse_offered_price',$this->warehouse_offered_price,true);
$criteria->compare('t.warehouse_offered_price_date',$this->warehouse_offered_price_date,true);
$criteria->compare('t.orderstatus_id',$this->orderstatus_id);
$criteria->together = true;
$criteria->compare('t.id',$this->id,true);
$criteria->with = array('user');
$criteria->compare('name',$this->user,true,"OR");
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
it is important to put t in-front of the t if your Order table primary key field if both have save name. in my case it is id and id, so i had to put t.
Other thing is the order of the elements
$criterial->togeter = true; should come before the relational elements.
then u updated to rules method in Order table. i added user filed and name filed to safe attributes.
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
//array(' orderstatus_id', 'required'),
array('hardwaredetail_Id, user_id, is_received, is_notify_by_email, orderstatus_id', 'numerical', 'integerOnly'=>true),
array('price, warehouse_offered_price', 'length', 'max'=>10),
array('bank_account_number', 'length', 'max'=>100),
array('order_create_date, order_update_date, order_received_date, warehouse_offered_price_date, user,name', 'safe'),
// The following rule is used by search().
// Please remove those attributes that should not be searched.
array('id, order_create_date, price, bank_account_number, hardwaredetail_Id, user_id, order_update_date, is_received, order_received_date, is_notify_by_email, warehouse_offered_price, warehouse_offered_price_date, orderstatus_id', 'safe', 'on'=>'search'),
);
}
Finally update your UI code.
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'order-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'id',
'order_create_date',
'price',
'bank_account_number',
array(
'name'=>'user',
'value'=>'$data->user->name'
)
)); ?>
i updated the order admin with the
array(
'name'=>'user',
'value'=>'$data->user->name'
)
That is what i did and it worked for me. ask me if you need any help. Thanks every one looking in to this issue.
It looks like both the user and order have columns named id. And your criteria uses them both in the WHERE clause, which is giving the "ambiguous" mySql error message.
When using with criteria (which does a SQL JOIN of the tables), if two of your tables have columns with the same name you need to use the mySql "dot" prefix on conditions, like so:
$criteria->compare('t.id',$this->id); // the default table prefix in Yii is "t"
When I JOIN a table using $criteria->with I prefix all of the column names (in my compare and condition criteria, etc)., like so:
$criteria->compare('t.id',$this->id); // t
$criteria->compare('t.order_create_date',$this->order_create_date,true); // t
$criteria->with = array('user');
$criteria->compare('user.name',$this->user_id,true); // user (the filter will set user_id)
Your gridview will need to look like this:
array(
'name'=>'user_id',
'header'=>'User',
'sortable'=>false,
'value'=>'$data->user->name'
),
Also, I think there is a larger problem, as you point out with your edit:
Your search function is set up like this: user.name',$this->user - but $this->user is going to return the current user object via the relation, not the search criteria. The column filter will set the user_id, property.
EDIT: Nope, you can $this->user as your column name so long as you set it as a safe attribute.
The way I am getting around this is shown in more detail here:
Search in BELONGS_TO model column with CGridView, Yii
Sorting will not work with that though - just the filtering.
Here is good post in the Yii forum that might give you more clues, too:
http://www.yiiframework.com/forum/index.php?/topic/9083-search-filter-of-a-relations-field-through-cgridview/
Sadly, sorting and filter CGridViews on relations is not really default functionality. YOu can easily display related info with a column name like user.name, but it won't sort or filter. Good luck!

Categories