Yii Framework - CGridView sort related columns - php

Thanks in advance to anyone who can help. I've been searching for an answer, but haven't found one yet. I've run into "solutions" that haven't worked that run from 1 line, to re-writing an entire class.
I've got the "grid" to show the relation, and am able to use the search feature. What I can't figure out is the sort feature. The column header becomes non-clickable once the below changes have been made.
This is what I have:
The relation name/label is "company," setup in Employee model.
Table: Employee -- Column: idCompany
&
Table: Company -- Column: companyNick
admin.php - VIEW
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'employee-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
array(
'name'=>'company',
'value'=>'$data->company->companyNick',
),
'lastName',
'firstName',
ETC...
Employee.php - MODEL
public function search()
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria=new CDbCriteria;
//Company Relation Search
$criteria->compare('company.companyNick',$this->company,true);
$criteria->with='company';
//stock
$criteria->compare('idEmployee',$this->idEmployee,true);
$criteria->compare('idAccount',$this->idAccount,true);
ETC...

I've been having the same problems and in the end solved it this way:
Model Search method:
$sort = new CSort();
$sort->attributes = array(
'assignedTo'=>array(
'asc'=>'(SELECT surname from people
WHERE people.person_id = t.assigned_to) ASC',
'desc'=>'(SELECT surname from people
WHERE people.person_id = t.assigned_to) DESC',
),
'*', // add all of the other columns as sortable
);
View file:
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'tasks-grid',
'dataProvider'=>$model->search(),
//'filter'=>$model,
'columns'=>array(
'task',
array(
'header'=>'Assigned To',
'value'=> '$data->assignedTo->surname.", ".$data->assignedTo->forename',
'name'=> 'assignedTo',
'sortable'=>TRUE,
),
'due_date',
'status',
),
));
This way I'm selecting a single field from the related table into the order by clause and then ordering by that, you create the table join in the expression, in this case it is -people.person_id = t.assigned_to (where t is a table alias provided by yii). This is perhaps not the most efficient way to create the order by clause but it works!

This seems to be a daily question on [yii]. Strip that stuff out of your search function, and add a filter attribute to your CGridView column like so:
array(
'name'=>'company',
'value'=>'$data->company->companyNick',
'filter' => CHtml::listData(Company::model()->findAll(),'id','nick'),
),

There is also good explanation how to do this on Yii web site Wiki page:
http://www.yiiframework.com/wiki/281/searching-and-sorting-by-related-model-in-cgridview/

I like Mike H's answer. I also want to point out that you could, instead of entering the raw SQL, use with() to perform a relational query and then set select to false to prevent actually loading the related models. You can also use the related attribute's attributeLabel in the view by passing the dot-notated string, e.g.,:
// Controller
$gridDataProvider = new CActiveDataProvider('EmrFormPatientTie', array(
'criteria'=>array(
'condition'=>'flag_locked=0',
'with'=>array('patient'=>array(
'select'=>false, // Perform relational query without loading related models
)),
),
'pagination'=>(array('pageSize'=>15)),
'sort'=>array(
'attributes'=>array(
'patient.display_id'=>array(
'asc'=>'patient.display_id',
'desc'=>'patient.display_id DESC',
),
'*', // Make all other columns sortable, too
),
),
));
// View
array(
'name'=>'patient.display_id',
'value'=>'$data->patient->display_id',
),

Related

Yii CArrayDataProvider Keyfield As Button Id In CGridView

My issue is two-fold. First, I'm unable to correctly set the keyField for CArrayDataProvider, all I'm getting back is a string instead of a value. Second, I'm trying to use the keyField inside of CArrayDataProvider to set an id on the button in each row inside of CGridView. The reason I want to do this is so that I can pass the id value onward to an ajax function (if there's a better way to do this in Yii, I'm all ears). Any help would be much appreciated, thanks in advance!
I also posted this question once on Yii's forums. I normally wouldn't repost, but I have had a hard time getting answers there, as opposed to stack overflow, you guys are the best! Here's the link to my original post if anyone is interested.
Here is how I'm building the array that I'm using as my RAW data:
foreach ($items as $item) {
$tableRow = array("id"=>$item["Id"], "Organization"=>$item["Organization"], "Roles"=>$item["Roles"]);
$return_items[] = $tableRow;
}
Here is the CArrayDataProvider setup I'm using. I noticed that 'keyField' is not being given the id value, just the string 'id':
$dataProvider=new CArrayDataProvider($return_items, array(
'keyField'=>'id',
'sort'=>array(
'attributes'=>array(
'Organization',
'Roles',
),
),
'pagination'=>array(
'pageSize'=>10,
),
));
Lastly, here is the CGridView I'm trying to setup in the view. All that appears on the button is the id tag, but no value:
$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider'=>$authItems,
'columns'=>array(
'Organization',
'Roles',
array('name'=>'',
'type'=>'raw',
'htmlOptions'=>array('id'=>'id'),
'value'=>'CHtml::button("Edit Roles", array("data-toggle"=>"modal", "data-target"=>"#roles-modal"))'),
),
));
Try passing it via CHtml::button which you have already applied. E.g.
'value'=>'CHtml::button("Edit Roles", array(
"id"=>$data["id"],
"data-toggle"=>"modal",
"data-target"=>"#roles-modal"
))'),

Yii CGridView, displaying multiple columns from related model with filtering capability

I'm pretty new to yii and I bumped into the following problem. I have 2 related tables, ClientTicket and Product with the following structure:
ClientTicket
id
ticket_name
client_id
product_id
Product
id
type
model
brand
The two tables are related through a foreign key which binds ClientTicket.product_id to Product.id.
The Problem
In the admin view of the ClientTicket I've managed to include two of the Product columns (brand, model) and have the search box display for each of them, but the filtering isn't working as expected. Ex: When I search in either of the two search boxes(brand, model), the other one populates automatically with the same value I typed (so no search results).
The ClientTicket model:
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(
'product' => array(self::BELONGS_TO, 'Product', 'product_id'),
........
);
}
public function search()
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria=new CDbCriteria;
...
$criteria->compare('product.model',$this->product_id, true);
$criteria->compare('product.brand',$this->product_id, true);
...
$criteria->with=array(..., 'product',);
$criteria->together= true;
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
'pagination' => array('pageSize' => 10),
));
}
The ClientTicket Admin view file:
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'client-ticket-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'ticket_number',
'ticket_date',
array('name'=>'agent_id',
'header'=> 'Agent',
'value'=> '$data->ticket_agent->name',
'filter'=>CHtml::listData(Agent::model()->findAll(), 'name', 'name'),
),
...
array('name'=>'product_id',
'header'=> 'Product',
'value'=> '$data->product->model',
),
array('name'=>'product_id',
'header'=> 'Brand',
'value'=>'$data->product->brand'
),
Your productand brand columns both have the same name but different values. The filter obtains the field name from name unless you explicitly state it i.e create your own active field. In addition you are using the same attribute product_id to search both fields in your search function.
How to filter using related models has been answered at Yii - how can I search by a column from foreign/related key on admin page?
For other users reference, here's how i got it to work.
Again, Thank you topher for the prompt response. :)
Inside ClientTicket model:
declared a new public variable named $brand
added 'brand' to 'safe' 'on'=>'search' array, inside of rules() function
added a new criteria inside the search() function like so:
$criteria->compare('product.brand',$this->brand, true);
In the ClientTicket admin view file:
modified the product brand column from this:
array('name'=>'product_id',
'header'=> 'Brand',
'value'=>'$data->product->brand'
),
to this:
array('name'=>'product.brand',
'header'=> 'Brand',
'filter'=>CHtml::activeTextField($model,'brand'),
),

Need help to create CRUD screen with dropdown (relation) using yii-framework

I need to create CRUD screen using yii-framework. Simple CRUD screen using one table is working perfectly fine. I'm facing problem/issue while using dropdown (linking table).
I have installed giix extension which is suppose to create CRUD with dropdown if FK is specified but I dnt have MySql Engine InnoDB on my hosting provider, so I'm not able to use that extension. I need to do it manually.
I have two tables
main:-
id
store_id
prefix
store:-
id
name
Now store_id of main is FK to id of store table. And I want to create CRUD for main table.
So that Add Screen should show:-
Store Name:- Dropdown
prefix:- Textbox
View screen should use name column of store table instead of showing store_id
Thanks in anticipation.
Generate CRUD using Gii, then read my blog. http://jmmurphy.blogspot.com/2013/05/using-yii-model-relations.html
Basically you will have something like this for your store_id field after Gii Generation
<?php echo $form->labelEx($model,'store_id'); ?>
<?php echo $form->textField($model, 'store_id', array('size'=>60,'maxlength'=>255));?>
<?php echo $form->error($model,'store_id'); ?>
The textField line is replaced by:
<?php $list = CHtml::listData(Store::model()->findAll(), 'id', 'name'); ?>
<?php echo $form->dropDownList($model, 'store_id', $list, array('empty'=>'(Select a Store)')); ?>
You also need to define relations in your Main model so that you can access related tables (even if your database does not support foreign keys) like this:
public function relations()
{
return array(
'store'=>array(self::BELONGS_TO, 'Store', 'store_id'),
);
}
And to complete this relation, you should also add the following relation to your Store model:
public function relations()
{
return array(
'main'=>array(self::HAS_MANY, 'Main', 'store_id'),
);
}
These relations define a One to Many relation between Store and Main where store is the parent, and Main is the Child. To make it a One to One relationship change HAS_MANY to HAS_ONE. The HAS_* goes in the parent model and points to the foreign key attribute in the child table. The BELONGS_TO goes in the child and tells the attribute in the child table that points to the primary key in the parent.
Now to see the store name in the view action, you need to change 'store_id' in your view.php to an array that looks like:
array(
'name' => 'store_id',
'value' => $model->store->name,
)
The admin view is slightly different. I am not sure exactly why, but to view the store name instead of the id in the admin view you will need to use an array that looks like:
array(
'name' => 'store_id',
'value' => '$data->store->name',
)
Note that Gii generates this so that $data is the model instead of $model, and also it does a funky double indirection thing so that you need to put the variable declaration in single quotes.
Thanks jmarkmurphy for your help. You helped me lot and I have marked your answer as correct only as you gave me guidance. Just posting exact code in detail.
I changed view.php with below code:-
<?php $this->widget('zii.widgets.CDetailView', array(
'data'=>$model,
'attributes'=>array(
'id',
'store.name',
'prefix',
),
));
Also changed admin.php with below code:-
<?php echo CHtml::encode($data->store->name); ?>
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'main-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'id',
'store.name',
'prefix',
array(
'class'=>'CButtonColumn',
),
),
)); ?>
Thanks once again jmarkmurphy. Thanks a lot . My application is now working and exactly the way I wanted.

Using Many to Many relations in yii

I am currently trying to display/retrieve data from my database using Yii framework relations which were auto generated by Gii. (MANY_MANY)
User Model contains:
return array(
'memberOfTeams' => array(self::MANY_MANY, 'UsersTeam', '{{teamMembers}}(userId, teamId)'),
);
UserTeam Model contains:
return array(
'teamMembers' => array(self::MANY_MANY, 'User', '{{teamMembers}}(teamId, userId)'),
);
Currently I am working on the User view called profile.php . All I am trying to accomplish is to display the current user with all the teams assigned to him.
teamMembers contains teamId and userId.
How would I write this query?
I have this currently
<?php echo CHtml::dropDownList("teamName", 'id', Chtml::listData(UsersTeam::model()->with('teamMembers')->findAll(teamMembers.userId, array($model->id)), 'id', 'teamName'),array('empty'=>'Select Team')); ?>
I am able to get all the information if I use findAll(), but I want only teams that the user is assigned to.
A project of mine does something similar:
MANY_MANY relation between shop and card, this dataprovider is used to display a list of shops linked to a specific card:
$shopDataProvider=new CActiveDataProvider( 'Shop',
array(
'criteria'=>array(
'with'=>array('cardShop'),
'condition'=>'cardShop.card_id=:cardId',
'params'=>array(':cardId'=>$id),
'order'=>'t.id DESC',
'together'=>true,
),
)
);

Sorting and Pagination does not work after I build a custom keyword search that is build using relational tables

I recently started to build a custom keyword search using Yii 1.1.x
The search works 100%. But as soon as I sort the columns and use pagination in the admin view the search gets lost and all results are shown. So with otherwords it's not filtering so that only the search results show. Somehow it resets it.
In my controller my code looks as follows
$builder=Messages::model()->getCommandBuilder();
//Table1 Columns
$columns1=array('0'=>'id','1'=>'to','2'=>'from','3'=>'message','4'=>'error_code','5'=>'date_send');
//Table 2 Columns
$columns2=array('0'=>'username');
//building the Keywords
$keywords = explode(' ',$_REQUEST['search']);
$count=0;
foreach($keywords as $key){
$kw[$count]=$key;
++$count;
}
$keywords=$kw;
$condition1=$builder->createSearchCondition(Messages::model()->tableName(),$columns1,$keywords,$prefix='t.');
$condition2=$builder->createSearchCondition(Users::model()->tableName(),$columns2,$keywords);
$condition = substr($condition1,0,-1) . " OR ".substr($condition2,1);
$condition = str_replace('AND','OR',$condition);
$dataProvider=new CActiveDataProvider('Messages', array(
'pagination'=>array(
'pageSize'=>self::PAGE_SIZE,
),
'criteria'=>array(
'with'=>'users',
'together'=>true,
'joinType'=>'LEFT JOIN',
'condition'=>$condition,
),
'sort'=>$sort,
));
$this->render('admin',array(
'dataProvider'=>$dataProvider,'keywords'=>implode(' ',$keywords),'sort'=>$sort
));
and my view looks like this
$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider'=>$dataProvider,
'columns'=>array(
'id',
array(
'name'=>'user_id',
'value'=>'CHtml::encode(Users::model()->getReseller($data->user_id))',
'visible'=>Yii::app()->user->checkAccess('poweradministrator')
),
'to',
'from',
'message',
/*
'date_send',
*/
array(
'name'=>'error_code',
'value'=>'CHtml::encode($data->status($data->error_code))',
),
array(
'class'=>'CButtonColumn',
'template'=>'{view} {delete}',
),
),
));
I really do not know what do do anymore since I'm terribly lost, any help will be hihsly appreciated
You could set a user state for your search criteria and test for the state each time the controller loads your view.
Something along the lines of
if(isset($_REQUEST['search'])){
$keywords = explode(' ',$_REQUEST['search']);
Yii::app()->user->setState('keywords',$keywords);
}
else if(Yii::app()->user->hasState('keywords')){
$keywords=Yii::app()->user->getState('keywords');
}
The drawback here is the keywords state will be maintained for the length of the session.
that's why I dont' like these frameworks. In raw PHP I'd make it with just $link=http_build_query($_GET); and then use this link for both pagination and sorting. But you have to find a way to do the same using your framework ideology. I bet they have some example for such a commonplace task.

Categories