You are viewing revision #3 of this wiki article.
This is the latest version of this article.
You may want to see the changes made in this revision.
Lets say we have two models and relation between them:
class Author extends CActiveRecord {
...
}
class Post extends CActiveRecord {
...
function relations() {
return array(
'author'=>array( self::BELONGS_TO, 'Author', 'id_author' ),
);
}
...
}
when you list Posts in grid you would like to print author name in column, allow sort on this column and probably filtering by substring of author name. Best solution (in my opinion) to provide all this functionalities is:
First you have to add new attribute to Post model, where search string will be stored. You could use foreign key column to achieve same effect, but I preffer not to overload meaning of this column as in search scenario you will store there string, not foreign id. You have to add this new attribute to 'safe' attributes in search scenario.
class Post extends CActiveRecord {
public $author_search;
...
public function rules() {
return array(
...
array( 'xxx,yyy,author_search', 'safe', 'on'=>'search' ),
);
}
}
Now we have to use this attribute in search criteria (in standard implementation - there is a search() function in every model). Also we have to specify that we want to fetch Post models together with related Author by setting 'with' attribute of criteria (this way there will be only one database query with join instead of many queries fetching related authors in lazy load mode):
$criteria = new CDbCriteria;
$criteria->with = array( 'author' );
...
$criteria->compare( 'author.username', $this->author_search, true );
...
And since we are editing search function - lets add another tricky feature to returned CActiveDataProvider:
return new CActiveDataProvider( 'Post', array(
'criteria'=>$criteria,
'sort'=>array(
'attributes'=>array(
'author_search'=>array(
'asc'=>'author.username',
'desc'=>'author.username DESC',
),
'*',
),
),
));
'attributes' section of sort configurations lets us overload default searching. This configuration says, that when user wants to sort by 'author_search' field it should be done like specified. last entry '*' says that every other field of this model should be treated normally. This way you can override also default attributes sorting (for example specify that when user sorts by last_name column there should be applied sorting by last_name and first_name column together)
Now we have prepared everything for our grid:
$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'title',
'post_time',
array( 'name'=>'author_search', 'value'=>'$data->author->username' ),
//post strictly required user but in any case that model has not required user(or another relation) you should replace with it
array( 'name'=>'author_search', 'value'=>'$data->author ? $data->author->username: "-"' ),
array(
'class'=>'CButtonColumn',
),
),
));
Thats it. We have sorting by user name instead of id_user foreign key column and we have search by user name substring.
Thanks
it was helpfull :)
For PostgreSQL users...
PostgreSQL tables/columns are case insensitive, so if you're planning to build a query for eg.maintenance_event table, the query will generate an error on search.
class MaintenanceEvent extends CActiveRecord { ... } class Repair extends CActiveRecord { ... function relations() { return array( 'maintenance_event'=>array( self::BELONGS_TO, 'MaintenanceEvent', 'maintenance_event_id' ), ); } ... }
Re: For PostgreSQL users...
Which part exactly will generate error messages? could you provide more specific info?
Re: For PostgreSQL users...
SQL with error...
[sql] SELECT COUNT(DISTINCT "t"."id") FROM "repair" "t" LEFT OUTER JOIN "maintenance_event" "maintenanceEvent" ON ("t"."maintenance_event_id"="maintenanceEvent"."id") -- generated code doesn't include dual quote on search WHERE (maintenanceEvent.plant_id=1)
Modifed class Repair to prevent SQL error...
class Repair extends CActiveRecord { ... function relations() { return array( // change this to something case insensitive (eg.from 'maintenanceEvent' to 'maintenance_event') 'maintenance_event'=>array( self::BELONGS_TO, 'MaintenanceEvent', 'maintenance_event_id' ), ); } ... }
Re: For PostgreSQL users...
Ahhh yes.. In this case - yes, but in my examples there wouldn't be such problem.
in fact you could write:
$criteria->compare( Yii::app()->db->quoteTableName( 'author' ) . '.' . Yii::app()->db->quoteColumnName( 'username' ), $this->author_search, true ); ... 'attributes'=>array( 'author_search'=>array( 'asc'=>Yii::app()->db->quoteTableName( 'author' ) . '.' . Yii::app()->db->quoteColumnName( 'username' ), 'desc'=>Yii::app()->db->quoteTableName( 'author' ) . '.' . Yii::app()->db->quoteColumnName( 'username' ) . ' DESC', ), '*', ),
and so on, and this should also do the trick. However even official tutorials and default code generators do not use proper quoting for table/column names... So when your DB engine is not case insensitive on table/column names you have to take special care when giving relation names and reference them in queries. Oracle is also similiar when dealing with table/column names case - when you provide alias for table in quotes - then it is case-sensitive, but when you provide alias without quotes - it is case-insensitive....
Thanks a lot
This was really great explanation how to use it and good base for lot of variation on this topic :)...
In case of HAS_MANY realations
If the relation that you would like to search in is a HAS_MANY relation, then in the criteria you also need to set the together to true:
$criteria = new CDbCriteria; $criteria->together = true; $criteria->with = array('authorS'); ... $criteria->compare( 'authorS.username', $this->author_search, true ); ...
Thank you redguy, for this great tutorial.
@gesti
Thanks for pointing that out. it can be useful in some cases
Problem when You have same columns name in related tables...
It can maybe useful for someone. If you use related models in search it can cause problem if models have some attributes (that is searched) which have same name. To solve this you need to add table prefix in SQL (when You create manually SQL), so in creating CDbCriteria You need to do somthing like this for main model field (for related model field you already need to name relation which to use and field):
$criteria->compare(Yii::app()->db->quoteTableName('t') . '.' . Yii::app()->db->quoteColumnName('field_name'),$this->naziv,true);
So when you have CDbCriteria with related models which contain same name of some attributes that you search, you need also to use prefix for main model table, not just for related...
In documentation of CdbCriteria you can see that if you not use alias for main model table it will always be 't', so if you dont care about how final SQL look, just left it like 't'...
Thumbs up!
Great!
Essential Article
This is a kind of article that should be included in the "Definitive Guide", I believe.
Property "Dealclub.owner_search" is not defined.
What did I miss? I followed what you said..and still I get this error:
Property "Dealclub.owner_search" is not defined.
Thank you,
re: denisa84
what are your models and relations between them?
do you have public attribute owner_search or get/setOwner_search in main model used in search?
Re:Property "Dealclub.owner_search" is not defined.
I resolved it initializing a variable like (public $owner_search = '';) this in the model and everything looks fine. I only hope is the right solution, too.
Regarding the same topic...it is possible to search by related model and again related model?
I mean
MODEL Dealclub: public function relations() { return array( 'lists' => array(self::HAS_MANY, 'Lista', 'dealclub_id'), ); } MODEL Lista: public function relations() { return array( 'list_categories' => array(self::HAS_MANY, 'ListCategory', 'list_id'), 'dealclub' => array(self::BELONGS_TO, 'Dealclub', 'dealclub_id'), ); } MODEL ListCategory: public function relations() { return array( 'list' => array(self::BELONGS_TO, 'Lista', 'list_id'), 'category' => array(self::BELONGS_TO, 'Category', 'category_id'), ); } How it is possible to access from Dealclub model it's categories given the relations? I need this for search/sort. The categories in the grid are listed fine but I want to be searchable also usign this field.
Thanks,
thanks...
lots and lots of thanks! Great article. Should be included in the Guide.
How would this work over three tables? Let's say:
I have models user, subscription and group.
User has a
HAS_ONE
relation'subscription'
, and subscription has aBELONGS_TO
relation'group'
.I have a gridview of users, whic has a grid-column that shows the value of
user.subscription.group.name
.How could I filter this column? Could I filter this column?
@c@cba
this should be also possible. you have to add 'with' with all related models in DataProvider criteria: 'with'=>array( 'subscription', 'subscription.group' ),
and then you should be able to access related tables with their aliases ('subscription' and 'group' respectively - notice that group is also referenced by single alias like in every SQL query, not in Yii relations way). The rest of sorting and filtering code should be very same.
@redguy
It works, thanks again! :)
I overlooked the 'with'. And I would have never thoght of using
group.name
instead ofsubscription.group.name
. Thanks for pointing that out.So now I have the following added to the CDbcriteria in user-model
$criteria->with=array("subscription", "subscription.group"); $criteria->compare('group.name', $this->group_search, true);
Another point worth mentioning:
As soon as we add
subscription
andsubscription.group
to the criteria (with'with'
), we might need to disambiguate the existing criteria by adding thet.
before the attribute names, if these models have common attribute names. E.g.:$criteria->compare('t.name',$this->name,true); $criteria->compare('t.last_update_at',$this->last_update_at,true); $criteria->compare('group.name', $this->group_search, true);
Rules
Many thanks for this helpful article.
I've used it to implemnt a search for user on related profile (esp. lastname and firtsname) ,
Therefore i had to add my 'profile_search' to rules not only under 'safe':
public function rules() { ... array('profile_search', 'length', 'min' => 1), ... }
@ s.mager@avantel.de
You have to put your new attribute to rules or it will be ommited when massive assignement is done ($model->attributes = $_POST['ModelName']). It does not have to be simple 'safe' validation, you may put any rule you want, just make sure it will fire on 'search' scenario. Just be carefult with too tight validation as search conditions can have comparison operator added (CDbCriteria::compare will look for them and use).
Marvellous
Hi RedGuy,
You are a great guy! You should write a tutorial on Yii. You explained it so nicely that I implemented it and it worked first shot! I am not joking: you should write a tutorial! Many many thanks.
@Bianca
Thanks. Glad to read such nice comments :)
two databases
Your article is really great. You mentioned
(this way there will be only one database query with join instead of many queries fetching related authors in lazy load mode).
What if we want to have two databases?
In my case I am listing invoices and I also want to show companies, too.
Of course I have customer_id in both databases. Customer_id in invoice table is a FK and in customer table is a PK.
Any hint?
Thanks
@SammyGh
I don't know any easy solution to your case. Only thing that comes to my mind is that you could build query accessing both database (if they are mysql databases, oracle db-linked or other kind of db-schemas you can access through single connection with dot-notation: dbschema.tablename). CActiveDataProvider relays on database negine to filter and sort so this is the only possibility.
another (not so pretty) solution is to handle filtering on your own and fetch data to CArrayDataProvider...
@sammygh
I think you mean two tables(not two databases), don't you? Your 2 tables seem to belong to one database...
@Bianca
no.. I mean two databases. In mysql they are more schemas than separate databases. single user can have access to multiple databases and access tables from any subset of them providing database names in table reference. Example:
SELECT * FROM db1.table1 t1 INNER JOIN db2.table2 t2 ON (t1.id = t2.id);
sweet!
Great tut! If anyone is wondering about nested relations I wrote a forum post a while back about it here
$this->author->username
Hi. Why won't this work:
$criteria->compare( 'author.username', $this->author->username, true );
Doesn't the model containing the search criteria, also contain the username field (via the author relation), which should allow you to use it as above, without having to separately create $author_search?
@gerhard@ecolar.co.za: Re: $this->author->username
relation is always lazy-loaded from dbms by default, so when you access $this->author SQL query will be invoked searching for author with PK = $this->id_author. As there is no such record (mostly because id_author is null) - NULL value will be returned as related object ($this->author == null).
So basically - you cannot relations to define search criteria in current AR implementation. There was some posts on forum to change this behavior so that accessiong relation return new empty object when no record has been found, but this breaks backward compatibility... Also - CGridView will not render filter fields by default for related fields. I do not know any easy workaround to make things work the way you want it and I have also looked for similiar solution.
Problem
Does any one got any trouble when you change the page in the CGridview, i cant make the content change, it always shows the same content
Re: $this->author->username
Thanx for the good answer Redguy.
How to combine with a custom formatter
I created a custom CFormatter class:
in config/main.php:
'components'=>array( 'format'=>array( 'class'=>'application.extensions._urlFormatter' ),
The class:
<?php class _urlFormatter extends CFormatter { public function formatProductUrl($value) { return CHtml::link($value[1],Yii::app()->createUrl('products',array('id'=>$value[0],'title'=>$value[1])),array('target'=>'_blank')); } }
and of course i defined 'value' as an array:
array( 'name'=>'related_product', 'type'=>'ProductUrl', 'value'=>array('$data->product->id','$data->product->title'), ),
But i got CExceptions, evaluateExpression() method fails in CDataColumn about my 'value'.
How i can pass to my custom CFormatter an array of relational values ?
@saegeek: Re: How to combine with a custom formatter
You need to properly quote value element, because it's being passed to CComponent::evaluateExpression (though to PHP's eval function):
array( 'name'=>'related_product', 'type'=>'ProductUrl', 'value'=>'array($data->product->id, $data->product->title)', ),
7400
return new CActiveDataProvider( 'Post', array( 'criteria'=>$criteria, 'sort'=>array( 'attributes'=>array( 'author_search'=>array( 'asc'=>'author.username', 'desc'=>'author.username DESC', ), 'group_search'=>array( 'asc'=>'group.name', 'desc'=>'group.name DESC', ) '*', ), ), ));
this way, sorting will work! :D
I have trouble with ID column
everything works great except when I try search on ID column, I got this error:
CDbCommand failed to execute the SQL statement: SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'id' in where clause is ambiguous. The SQL statement executed was: SELECT COUNT(DISTINCT `t`.`id`) FROM `who_company` `t` LEFT OUTER JOIN `users` `user` ON (`t`.`user_id`=`user`.`id`) LEFT OUTER JOIN `profiles` `profile` ON (`profile`.`user_id`=`user`.`id`) WHERE (id=:ycp0)
@mohamadaliakbari
When dealing with joined tables always use table alias to compare attributes:
$criteria->compare( 't.id', $this->id ); or $criteria->compare( 'author.id', $this->id );
this way you point exactly whicz table you are referring. In your case - you have "id" column in both tables and DB does not know which one you are referring in search clause.
@redguy
is "t." always the table alias?
Relational ActiveRecord Table Alias
t is only for the primary table, use the relation name to prefix the related records:
http://www.yiiframework.com/doc/guide/1.1/en/database.arr#disambiguating-column-names
@mohamadaliakbari
"t" is default alias for primary table in AR query. you can change it with criteria.
for related tables their alias is by default relation name.
you can run profiler with sql profiling enabled and see real queries that are executed when dealing with AR in Yii - it is very handy to understand they way Yii does it :)
Problem with 2 related tables
@Redguy,
I have done the necessary modifications for 2 related tables but I am having the following error:
**PHP Error
Description
Trying to get property of non-object
Source File
C:\yii1.1.4\framework\base\CComponent.php(616) : eval()'d code(1)
No source code available.**
Necessary modifications done as follows:-
$criteria->with = array('cli','agence'); return new CActiveDataProvider(get_class($this), array( 'criteria'=>$criteria, 'sort'=>array( 'attributes'=>array( 'client_name'=>array( 'asc'=>'cli.name', 'desc'=>'cli.name DESC', ), 'agent'=>array( 'asc'=>'agence.agency_name', 'desc'=>'agence.agency_name DESC', ), '*', ), ), ));
Relations are named cli and agence. client_name and agent are the 2 public variables created in the model. Can you please help? Thanks
Problem with 2 related tables: null values in the related field cause the error
@Redguy,
I figured out the cause of the problem: it is because some of the fields have null values. Do you have a solution to this problem? Thanks
@Bianca
Get whole callstack and check if this error is not related to grid visualisation (value getters, etc). You can also post me on priv this callstack, controller, model and view code.
Problem with 2 related tables: null values in the related field cause the error
@Redguy,
How do I post you on priv? :)
Displaying array of values for one to many relation between 2 tables
Hi Redguy,
I would like to do the same thing with 2 tables having a 1-many relations. Let's say we invert the example you gave.
In the cgrid view of author, we have a column displaying the various posts for each author. Of course, this approach is only when there are not many posts for each author. I have such a relation where there could be at most 5 related records for each primary record. Can you suggest something? Thanks
Re: Displaying array of values for one to many relation between 2 tables
1- to display multiple values you will have to prepare custom pseudo-attribute (getter):
public function getRelatedObjectNames() {
return implode( ', ', CHtml::listData( $this->relatedObjects, 'id', 'name' ) );
}
or something like this. you may access it simply by
echo $model->relatedObjectNames;
or in grid column:
array( 'value'=>'$data->relatedObjectNames' ),
2- filtering by such column requires sub-query in search criteria:
if( !empty( $this->relatedNames_filter ) ) {
$criteria->addCondition( 't.id IN (SELECT rel.foreign_id FROM related_object rel WHERE rel.name LIKE :relNameLike)' );
$criteria->params[':relNameLike'] = '%' . $this->relatedNames_filter . '%';
}
...or something like this :)
3- sorting by such field is rather impossible...
PHP Error Description Trying to get property of non-object
@Jimlam
> I figured out the cause of the problem: it is because some of the fields have null values. Do you have a solution to this problem? Thanks
array( 'name'=>'author_search', 'value'=>'(isset($data->author->username)) ? $data->author->username : ""' ),
Null values in the related fields
@ Evegeniy
Sorry I am a bit late :)
array('name'=>'name', 'value'=>'$data->yourrelation!==null?$data->yourrelation->attributeinrelatedtable:"None"'),
Behavior to make relation search easier
I just released a behavior to help with this:
Related Search Behavior
Searching and sorting by related model in CGridView
Thanks! Help me a lot!
Really good stuff.
Thank you mate
TbEditableColumn
The rules() function in this wiki sets 'xxx, yyy, author_search' to be regarded as 'safe'.
I'm just wondering if this rule might cause table fields like xxx and yyy to bypass validation when being directly submitted by TbEditableColumn?
So maybe we should always use variables (like 'author_search') in the 'safe' rule, even for native table fields/properties.
Any thoughts?
'safe' only in 'search' scenario
Hi
The fields are only regarded as 'safe' during the search scenario.
When you update or create a record, the scenario is 'update' or 'insert', not 'search'.
Therefore a bulk assignment of the attributes will not apply the assignment of fields that are only safe during 'search'.
If TbEditableColumn does not work that way, that is an issue with TbEditableColumn, but I'ld be surprised that there is a mistake there.
'safe' only in 'search' scenario
Ya, that makes sense.
Thanx le_top
Alphabetical sorting
Is there any way to have these sort alphabetically when the grid is first displayed ?
nevermind i found the answer
'defaultOrder'=>'customer.company ASC',
But is there any way I can use this in a drop down box for a filter too? I was using (in view )
'filter' => CHtml::listData(Customers::model()->findAll(), 'customer_ID', 'company'),
Re: Alphabetical sorting
you can still user dropdown as a filter but it will pass customer_id and you should implement search() method so it compares passed value with proper column.
I have to set "together" or it always show column not found
I have to add:
$criteria->together = true;
if not, I'll got an error when sort/search from the CGridView: Column not found
Little fix
This tutorial is great, but this is how it worked for me.
$criteria->compare( 'author.username', $this->author_search, true );
Instead of author.username, I just put username, without that author prefix
so:
$criteria->compare( 'username', $this->author_search, true );
RE: together and table alias
@Taker
You had to set 'together' to true, because you were working with HAS_MANY or MANY_MANY relation. The sample code in this article assumes that the relation is BELONGS_TO, which doesn't need 'together' set to true.
Relational query - lazy loading and eager loading / with and togeter
@darioo
Your code will work fine only when the main table doesn't have a column with the same name. It's recommended to use always the table aliases when you are joining tables.
Re: together
like Softark pointed out - it is rather assumed that relation is BELONGS_TO or HAS_ONE, so "together" is not required in such cases. I do not prefer straight joins and searching in HAS_MANY and MAMY_MANY scenarios as it can lead to some side effects (like loading only those dependent records which match criteria). When dealing with multiple dependent records I prefer writing SQL subqueries, so criteria looks like this:
$criteria->addCondition( 't.id IN (SELECT id_article FROM author WHERE username LIKE :uname)' ); $criteria->params[':uname'] = '%' . $this->author_username . '%';
This way I have much more control on what is going on. Also - query returns only 'article' records without any dependent records if I wish it work this way :)
As for related table alias when comparing - Softark just said everything about it :)
@darioo
your "fix" only works in our example:
$criteria->compare( 'username', $this->author_search, true );
you might use a field that have the same name in both the base table and related table and you will then get a mysql error with that, so I recommend to use related_table.field instead of just field. Always use
$criteria->compare( 'author.username', $this->author_search, true );
for safety
Filter CGridView in table relation
Hi redguy,
Thanks before for the posting. It is useful to me.
But, if I want to display username in CGridView as filter, there is error.
'filter' => CHtml::listData(Author::model()->findAll(), 'id', 'username'),
If I am using code like above, when one of dropdown is selected, no data result.
How about find the solution for my case ?
Thanks..
@J.J.
From what I see you do the wrong compare there
You compare the id to the value of that field, if you use text search it will compare the text with the value of the related table.
$criteria->compare( 'author.username', $this->author_search, true );
Your filter should be:
'filter' => CHtml::listData(Author::model()->findAll(), 'username', 'username'),
with your filter the column should be:
array( 'name'=>'id_author', 'value'=>'$data->author->username', 'filter' => CHtml::listData(Author::model()->findAll(), 'id', 'username'), ),
And you shouldn't need all the other related code in this post.
The code isn't tested.
@Emil Fedorciuc
Hi Emil, thank you for your response.
I have the self solution, and is working.
Here my solution in admin.php
'filter' => CHtml::activeDropDownList($model, 'author_id', CHtml::listData(Author::model()->findAll(), 'id', 'username'), array('prompt' => ' ')),
And all the configuration is the same like redguy did post.
Thank you.
It Works !
Thanks a lot for this tutorial it works fine for me.
I have been searching to this solution for two months. Thanks
More sample code on using relations in CGridView
I've posted sample code here that's similar to this tutorial:
http://jeffreifman.com/yii/cgridview/
It has more detail on sorting related columns.
How can I access to a related model since a related model
For example if i add this class in additionnal to Post and Author :
class Family extends CActiveRecord { ... } class Author extends CActiveRecord { ... function relations() { return array( 'family'=>array( self::BELONGS_TO, 'Family', 'id_family' ), ); } ... }
And now in a Post CgridView I want to access to a family property. How can I do that ?
re: How can I access to a related model since a related model
I suggest you read CGridView documentation:
'columns'=>array( 'title', // display the 'title' attribute 'category.name', // display the 'name' attribute of the 'category' relation 'content:html', // display the 'content' attribute as purified HTML ...
re: How can I access to a related model since a related model
I've read the documentation but it does not indicate how to configure "Criteria" as in this tutorial. I want to configure search with a model related to a model related to another model. post.author.family.family_name for example.
$criteria = new CDbCriteria; $criteria->with = array( 'author' ); ... $criteria->compare( 'author.username', $this->author_search, true ); ...
I tried
$criteria->compare( 'author.family.family_name', $this->family_search, true );
But it doesn't work !
re: How can I access to a related model since a related model
Good luck with that!
I ended up adding a custom dropdown filter:
array( 'name'=>'id_author', 'value'=>'$data->author->family->family_name', filter'=>CHtml::listData(Author::model()->with('family')->findAll(), 'id', 'family.family_name'), ),
You would actually filter the data based on the author id from the Author model and display the data from the related Family model.
This is an adaptation of my script to your case, I hope I got the fields right.
I couldn't find a way to get a partial search working with an input field.
If you find the solution to this problem please share!
re: How can I access to a related model since a related model
This is not correct:
$criteria->compare( 'author.family.family_name', $this->family_search, true );
Try this instead:
$criteria->compare( 'family.family_name', $this->family_search, true );
Please read the definitive guide : Relational Active Record - 5. Disambiguating Column Names
Especially the tip column.
re: How can I access to a related model since a related model
Thank you softark!
You're a life saver, ofc I am too lazy to read all the documentation so I missed that part.
Now comes the best part for me, I wander how that works in my next scenario:
table1 related to table2, table2 related to table3, table3 related to table4, table4 related to table5. I need search on data from table5 together with table1. The initial design wasn't intended like this, but I guess unless I redesign the tables there's no other way to do this.
Relations through many tables
Dear Emil Fedorciuc
No need to panic regarding your need (table1->table2->...->table5).
What softark explains (how to setup relations, searches, ...) is made easy with an extension that I made which I called RelatedSearchBehavior.
You can have a look at the demo which has 7 layers of linked tables and yet a fully searcheable grid with columns from almost every table. On top of that the demo is running on sqlite!
It is quite easy to set up - as the demo page says, look at InvoiceLine.php (the model) and index.php (the view). (Links point to github, but you should get the zip from the extension page for the most current demo).
I hope you'll enjoy.
re: Relations through many tables
Thank you le_top
Great extension: works right out of the box, easy setup, cleaner code.
Took me a while to figure out that I was using a HAS_MANY relation, but after I defined another relation to HAS_ONE it worked.
Error
Its Working!!
but When I use the filters is not work..
RE: #17781
Hi vijay
what excactly you mean ? search user on filters ?
Please post a related thread on the forum with your code!
Great!!!
Thank's ,.,.
Alternative for cleaner code
Instead of adding a search scenario, attributes and related functions to your main model, consider creating and using a separate model like so:
class SearchPost extends Post { public $author; public function rules() { return array( array( 'xxx,yyy,author', 'safe' ), ) ); public function getSearchCriteria() { $criteria = new CDbCriteria; $criteria->.... return $criteria; } }
Controller action:
$searchModel = new SearchPost; $searchModel->unsetAttributes(); // clear any default values if (isset($_GET['SearchPost')) { $searchModel->attributes = $_GET['SearchPost']; } $dataProvider = new CActiveDataProvider('Post', array( 'criteria' => $searchModel->getSearchCriteria(), ... // set pagination/sort here or in view file )); $this->render('admin',array( 'model' => $searchModel, 'dataProvider' => $dataProvider, ));
Alternative for view code and sorting
Instead of using the search name field and rewriting the value attribute, you could do it the other way around:
'columns' => array( array( 'name' => 'author.username', 'filter' => CHtml::activeTextField($model, 'author_search'), ),
This also makes the sorting code easier:
'sort' => array( 'attributes' => array( '*', 'author.username', ), 'defaultOrder' => array( 'author.username' => CSort::SORT_ASC, ), ),
easy way
really good solution, but a while ago I had similar problem with filtering and joined tables, I just wrote a new MySQL View (join needed tables) in my db then and a new model in Yii
Getting error!
im getting "Trying to get property of non-object"
here's my code :
model:
public $search_name; public function rules() { return array( array('id, tid, status, status_date, search_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( 'teachers2'=>array( self::BELONGS_TO, 'teachers2', 'tid' ), ); } public function search() { // @todo Please modify the following code to remove attributes that should not be searched. $criteria=new CDbCriteria; $criteria->with = array( 'teachers2' ); $criteria->compare( 'user_fname', $this->search_name, TRUE ); $criteria->compare('id',$this->id); $criteria->compare('tid',$this->tid); $criteria->compare('status',$this->status,true); $criteria->compare('status_date',$this->status_date,true); $criteria->addCondition('status = "login"'); return new CActiveDataProvider($this, array( 'criteria'=>$criteria, // 'sort'=>array( // 'defaultOrder'=>'status_date DESC', // ), 'sort'=>array( 'attributes'=>array( 'author_search'=>array( 'asc'=>'teachers2.user_fname', 'desc'=>'teachers2.user_fname DESC', ), '*', ), ), )); }
my gridview
$this->widget('zii.widgets.grid.CGridView', array( 'id'=>'teacher-status-grid', 'dataProvider'=>$model->search(), 'filter'=>$model, 'columns'=>array( array( 'name'=>'search_name', 'value'=>'$data->teachers2->user_fname', 'filter' => CHtml::activeTextField($model, 'search_name'), ), ), );
PLEASE HELP..
@iamjuliano
Hi Juliano,
You can do this in your GridView at the value:
It happens when the foreign-key can be NULL
instead of this: 'value'=>'$data->teachers2->user_fname',
put this and you are good to go.
'value' => function($data) { return isset($data->teachers2) ? $data->teachers2->user_fname : null; }
Sincerely,
Babak
@bandpay
thanks babak!
im not getting the error but the search does'nt work..
what could be the problem?
@iamjuliano
hi, this is difficult to say why it is not working. If you have data in your tables and you can run a piece of SQL which could get the data out with the criteria you have chosen, then I can't say why the data is not emerging. But I have an vague idea that " status = login" or "tid" are making a little trouble.
Try to start with as few conditions as possible to pinpoint the reason for the not working search.
@bandpay
i removed my addCondition in my model
public function search() { // @todo Please modify the following code to remove attributes that should not be searched. $criteria=new CDbCriteria; $criteria->with = array( 'teachers2' ); $criteria->compare( 'user_fname', $this->search_name, TRUE ); $criteria->compare('id',$this->id); $criteria->compare('tid',$this->tid); $criteria->compare('status',$this->status,true); $criteria->compare('status_date',$this->status_date,true); // $criteria->addCondition('status = "login"'); return new CActiveDataProvider($this, array( 'criteria'=>$criteria, // 'sort'=>array( // 'defaultOrder'=>'status_date DESC', // ), 'sort'=>array( 'attributes'=>array( 'author_search'=>array( 'asc'=>'teachers2.user_fname', 'desc'=>'teachers2.user_fname DESC', ), '*', ), ), )); }
this is in my gridview
array( 'name'=>'search_name', 'value' => 'isset($data->teachers2) ? $data->teachers2->user_fname : null', 'filter' => CHtml::activeTextField($model, 'search_name'), ),
it is still not working.
waahh! what seems to be the problem..
:(
try this
have a look here
@iamjuliano
hi, do you have some sample data from both tables which you mind posting here? I think that you don't have corresponding data in your child table and please make sure that you are seeing all the errors.
Please continue in forum
When leaving a comment, it is suggested to:
Please only use comments to help explain the above article.
If you have any questions, please ask in the forum, instead.
To avoid non-existing objects in some cases you can also write:
array( 'name'=>'search_name', 'value' => 'CHtml::value($date,"teachers2.user_fname")', 'filter' => CHtml::activeTextField($model, 'search_name'), ),
The test inside 'CHtml::value' is more complete and it is easier to write (no repetition).
works for me a life saver post Thanks a lot :-) ;
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.