I have the following tables.
User:
---------
id<br>
firstName
Project:
---------
id<br>
Name
StaffingManager
---------------
id<br>
User_id(FK)<br>
Total_Staff<br>
StaffingProjectMonth
-----------------------
id<br>
Project_id(FK)<br>
StaffingManager_id(FK)<br>
I want to define the relations in StaffingProjectMonth model
This is the default relation defined by YII using gii
public function relations()
{
return array(
'project' => array(self::BELONGS_TO, 'Project', 'Project_id'),
'staffingManager' => array(self::BELONGS_TO, 'StaffingManager', 'StaffingManager_id'),
);
}
I was able to get the ProjectName and search by ProjectName.
I want to get the UserfirstName and search by that.
I defined the relation this way.
return array(
'project' => array(self::BELONGS_TO, 'Project', 'Project_id'),
'staffingmanager' => array(self::BELONGS_TO, 'StaffingManager', 'StaffingManager_id'),
'user'=> array(self::HAS_MANY,'User',array('User_id'=>'id'),'through'=>'staffingmanager' ),
);
and in search method I did this:
$criteria->with = array('project','user');
//$criteria->compare('id',$this->id);
$criteria->compare('Name',$this->Project_id,true);
$criteria->compare('firstName',$this->StaffingManager_id,true);
and in the view:
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'staffing-project-month-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
// 'id',
array('name'=>'Project_id','header'=>'Project','value'=>'$data->project->Name',),
array('name'=>'StaffingManager_id','header'=>'User','value'=>'$data->staffingmanager->user->firstName',),
..............
only the search by project name works. Able to sess the UserfirstName but unable to search by the firstName. Some wrong in defining relations.
Any help is appreciated. Thanks.
I recommend the following excellent wiki article by redguy, that describes how to search by related model attributes:
Following the approach of this article:
You should declare two new variables in your model:
public $project_name;
public $staffingmanager_firstname;
You should declare these new variables as safe for search in the rules() method:
array( 'project_name,staffingmanager_firstname,...', 'safe', 'on'=>'search' ),
Your search() method criteria should look like this:
$criteria->compare('project.Name',$this->project_name,true);
$criteria->compare('user.firstName',$this->staffingmanager_firstname,true);
In your view file, the 'columns' should be:
array('name'=>'project_name','header'=>'Project','value'=>'$data->project->Name',),
array('name'=>'staffingmanager_firstname','header'=>'User','value'=>'$data->user->firstName',),
Hope this helps.
Best regards...
I had troubles with $criteria->with and ”through“ relation.
Thing was in relation kind. BELONGS_TO doesn't work correct. I switched it to HAS_ONE and inverted id direction — array('parent_id'=>'id'). Now it's working!
Related
I'm having problem to show data on cgridview using foreign keys.
This is my case, i have table employee(id, username), client(id, username), and transaction(id, employeeId, clientId). employeeId foreign key to employee.id, and clientId is foreign key to client.id. Now, I want to show employee's name and client's name instead of their id on transaction admin.php.
This is my code:
class Transaction extends CActiveRecord
{
public $client_search;
public $employee_search;
public function rules()
{
return array(
.
.
.
array('id, employeeId, clientId, balance, status, date, client_search, employee_search', 'safe', 'on'=>'search'),
);
}
public function relations()
{
return array(
'employee' => array(self::BELONGS_TO, 'Employee', 'employeeId'),
'client' => array(self::BELONGS_TO, 'Client', 'clientId'),
);
}
public function search()
{
$criteria=new CDbCriteria;
$criteria->with = array( 'client', 'employee' );
$criteria->together = true;
$criteria->compare('t.id',$this->id,true);
$criteria->compare('employee.username', $this->employee_search, true );
$criteria->compare('client.username', $this->client_search, true );
$criteria->compare('t.balance',$this->balance,true);
$criteria->compare('t.status',$this->status);
$criteria->compare('t.date',$this->date,true);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
//the other functions are there, i don't edit it.
}
that is my model/Transaction.php
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'transaction-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'id',
array(
'header' => 'Employee',
'name' => 'employee_search',
'value' => '$data->employee->username',
),
array(
'header' => 'Client',
'name' => 'client_search',
'value' => '$data->client->username',
),
array(
'class'=>'CButtonColumn',
),
),
));
that is my views/transaction/admin.php.
and this code gave me error Trying to get property of non-object ($data->employee->id marked).
Actually I have succed to show the employee's name instead of employee's id, but, after that I use the same method for the client, and the error appear.
anyone can help me? My method is making public employee_search, add the rules, add the relation, adding $creiteria->with, then change the admin.php. Anyone please help me.
//UPDATE
SOLVED. Its actually my fault. There is an error in the database about the relation (foreign key). My coding is fine.
It's hard to guess without being able to debug your code but here are some things that could help (I'll add more as I can think of them).
It might be because both the default joinType for relations is LEFT OUTER JOIN and maybe you have a Transaction where one of them is null? If you try to access attributes on null object (as opposed to an actual ActiveRecord object), that's precisely the error message you would be seeing.
You could change it by doing:
public function relations()
{
return array(
'employee' => array(self::BELONGS_TO, 'Employee', 'employeeId',array('joinType'=>'INNER JOIN')),
'client' => array(self::BELONGS_TO, 'Client', 'clientId',array('joinType'=>'INNER JOIN')),
);
}
P.S.: INNER JOIN is the same as just JOIN
Not sure if it will help you, but it's worth trying.
More information at: http://www.yiiframework.com/doc/api/1.1/CActiveRecord#relations-detail
I have this Object model with 'has_many' relation to Variables
public function relations()
{
return array(
'variables' => array(self::HAS_MANY, 'Variables', 'variable_id')
);
}
Now i want to use the variables to order my data like
$criteria->with = array('variables');
$criteria->order = 'variables.id DESC';
But it doesn't work.
Is there a way to do something like this? Thanks.
You can define the relation directly with an order if you want, in this case you can do.
public function relations()
{
return array(
'variables' => array(self::HAS_MANY, 'Variables', 'object_id', 'order'=>'variables.id DESC')
);
}
What you wrote it is not working because you have a 1 to many relation. The criteria will run 2 queries, 1 to get the main record, the second time to get the relations. That is why your order is not working.
If you want it to work like you said you should do a ->join instead of ->with.
There is quite a difference between the 2 so take care how you are writing the criteria.
I think the issue is with foreign key binding, if you are adding a relationship in object table (Object model) which has many variables having object_id as foreign key in variable table (Variable model) then you need to define relationship as follows:
public function relations()
{
return array(
'variables' => array(self::HAS_MANY, 'Variables', 'object_id') // check the change in foreign key column
);
}
I have two tables: user and asset and there is user_asset table that makes many_many relation
Now I want to show only the assets related with logged-in user in CGridView (user_id comes from Yii::app()->user->id)
How to write a criteria that makes it possible?
1) You need to define your relations in both of the model
user:
'assets' => array(self::MANY_MANY, 'Asset', 'user_asset(user_id, asset_id)'),
asset:
'users' => array(self::MANY_MANY, 'Asset', 'user_asset(asset_id, user_id)'),
2) You Create a dataProvider that will fetch only the recquired datas (define it in a model (best) or a controller bu not in a view):
$dataProvider=new CActiveDataProvider('Asset', array(
'criteria'=>array(
'with'=>array(
'users'=>array(
'condition' => 'id = ' . Yii::app()->user->id
),
),
),
...
));
3) You give to your CGridView the good provider:
$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider'=>$dataProvider,
...
));
The code is given as example, you should adapt it to your need!
I got it work, adding this inside the Model class's search method:
$criteria->with=array(
'users'
);
$criteria->together = true;
$criteria->condition = "users_users.user_id='".Yii::app()->user->id."'";
... and inside CActiveDataProvider declaration:
'criteria'=>array(
'with' => 'users',
'together'=>true,
'condition'=>'users_users.user_id = '.Yii::app()->user->id,
),
Maybe not the best way but does what needed. users_users is sql table alias by Yii, I got a hint from error message :)
I have two tables tbl_business and business_contacts of the following structure:
tbl_business
---
business_id (PK)
othercolumns
and
business_contacts
---
contact_id (PK)
business_id
othercolumns
The scenario is that one business row has many contacts. I am using cGridview using gii's CRUD generator and needed to display firstname and lastname from business_contacts (one of multiple possible rows in the table) for each tbl_business record.
As far as I understand, I've updated the relation function in tbl_business's model as:
'businesscontacts' => array(self::HAS_MANY,'BusinessContact','business_id','select' => 'contact_firstname, contact_lastname')
and for the same, a contact relation is defined in the business_contacts' model as:
'contactbusiness' => array(self::BELONGS_TO,'BusinessContact','business_id')
I expected that would work for pulling related records so that I can have something in the grid like, business_id, contact_firstname, contact_lastname , ... otherbusinesstablecolumns .. but I'm only getting blank values under firstname and lastname .. could someone please help me understand the error? :(
So you are trying to display a table of Businesses (tbl_business) using CGridView? And in each Business's row you want to list multiple Contacts (business_contacts)?
CGridView does not support displaying HAS_MANY relations by default. CGridView makes it easy to list which Business a Contact BELONGS_TO (i.e. you can use a column name like contactbusiness.business_id), but not all of the Contacts that are in a business.
You can do it yourself though, by customizing a CDataColumn. (Note: this will not allow you to sort and filter the column, just view. You'll have to do a lot more work in to get those working.)
First, in your Business model, add a method like this to print out all of the contacts:
public function contactsToString() {
$return = '';
foreach ($this->businesscontacts as $contact) {
$return .= $contact->contact_firstname.' '.$contact->contact_firstname.'<br />';
}
return $return;
}
(EDIT: Or do this to print out just the first contact):
public function contactsToString() {
if($firstContact = array_shift($this->businesscontacts)) {
return $firstContact->contact_firstname.' '.$firstContact->contact_firstname;
}
return '';
}
Then make a new column in your grid and fill it with this data like so:
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'business-grid',
'dataProvider'=>$model->yourDataProviderFunction(),
'columns'=>
'business_id',
array(
'header'=>'Business Contacts', // give new column a header
'type'=>'HTML', // set it to manual HTML
'value'=>'$data->contactsToString()' // here is where you call the new function
),
// other columns
)); ?>
EDIT2: Yet another way of doing this, if you just want to print out ONE of a HAS_MANY relation, would be to set up a new (additional) HAS_ONE relation for the same table:
public function relations()
{
return array(
'businesscontacts' => array(self::HAS_MANY,'BusinessContact','business_id','select' => 'contact_firstname, contact_lastname') // original
'firstBusinesscontact' => array(self::HAS_ONE, 'BusinessContact', 'business_id'), // the new relation
);
}
Then, in your CGridView you can just set up a column like so:
'columns'=>array(
'firstBusinesscontact.contact_firstname',
),
Getting only the first contact could be achieved like this also:
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'business-grid',
'dataProvider'=>$model->yourDataProviderFunction(),
'columns'=>
'business_id',
//....
array(
'name' => 'contacts.contact_firstname',
'value' => '$data->contacts[0]->contact_firstname', // <------------------------
'type' => 'raw'
);
//....
),
taxonomies
-id
-name
taxonomy_type
-taxonomy_id
-type_id
I've configured two models:
class Model_Taxonomy{
protected $_has_many = array('types'=>array());
}
class Model_Taxonomy_Type{
protected $_belongs_to = array('taxonomy' => array());
}
*Please note that taxonomy_type is not a pivot table.*
A taxonomy can have multiple types associated.
Then, what I'm trying to do is get all taxonomies that belong to a given type id.
This is would be the SQL query I would execute:
SELECT * FROM taxonomies, taxonomy_type WHERE taxonomy_type.type_id='X' AND taxonomies.id=taxonomy_type.taxonomy_id
I've tried this:
$taxonomies = ORM::factory('taxonomy')
->where('type_id','=',$type_id)
->find_all();
Obviously this doesn't work, but I can't find info about how execute this kind of queries so I have no clue.
class Model_Taxonomy{
protected $_belongs_to = array(
'types' => array(
'model' => 'Taxonomy_Type',
'foreign_key' => 'taxonomy_id'
)
);
}
class Model_Taxonomy_Type{
protected $_has_many = array(
'taxonomies' => array(
'model' => 'Taxonomy',
'foreign_key' => 'taxonomy_id'
)
);
}
And use some like that:
$type = ORM::factory('taxonomy_type')
->where('type_id', '=', $type_id)
->find();
if( ! $type->taxonomies->loaded())
{
types->taxonomies->find_all();
}
type_id column is a PK of taxonomy_type table, am I right?
So, you have one (unique) taxonomy_type record, and only one related taxonomy object (because of belongs_to relationship). Instead of your:
get all taxonomies that belong to a
given type id
it will be a
get taxonomy for a given type id