Retrieve data value in CGridView by relation HAS_ONE - yii

The Relation:
public function relations() {
return array(
'submissionStatus' => array(self::HAS_ONE, 'SubmissionStatus', 'submission_status_id'),
);
}
Column value in GridView:
'value' => '$data->submissionStatus->submission_status_name',
SubmissionStatus Model Attributes:
public function attributeLabels() {
return array(
'submission_status_id' => 'Status ID',
'submission_status_name' => 'Status Name',
);
}
SubmissionStatus Relations:
public function relations() {
return array(
'submissionStatus' => array( self::BELONGS_TO, 'Submission', 'submission_status_id' ),
);
}
Yet I get an error:
Trying to get property of non-object
Why can I not retrieve submission_status_name by this relation?
EDIT:
This on the other hand works perfectly:
'submissionStatus' => array(self::BELONGS_TO, 'SubmissionStatus', 'submission_status')
DATABASE:
| tbl_submission |
-----------------------------------
| submission_id (int) PK |
| submission_name (varchar) |
| submission_status_id (int) FK |
| tbl_submission_status |
------------------------------------
| submission_status_id (int) PK |
| submission_status_name (varchar) |

There to 2 questions here.
if your relations really works
hot to get the data in gridview
I think you have an error in the realtion. The difference between BELONGS_TO and HAS_ONE is, that it tries to create the realtion in a different way. Could you post your Database tables, what fields they have, we need to see what is the key and foreign jey fields.
i guess it is id in one table and submission_status_id in the other one.
try to change you relation in one of these two (try if one of them works):
1.
'submissionStatus' => array(self::HAS_ONE, 'SubmissionStatus', array('submission_status_id'=>'id'),
'submissionStatus' => array(self::HAS_ONE, 'SubmissionStatus', array('id'=>''),
2.
in your grid the code you use is correct but as stated in the comment from #Manquer, if the relation returns null it will not work.
But you can try the more Yii style in the GridViiew like this:
do not give any value only tne name
'name'=>'>submissionStatus.submission_status_name'

If the value of relation field is null then you relation will not return a object of the Foreign model; therefore calling the attribute in it will give the error
try changing
'value' => '$data->submissionStatus->submission_status_name',
to
'value' => '(isset($data->submissionStatus))?
$data->submissionStatus->submission_status_name:""',

Related

Yii order by another table with has_many relation

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
);
}

Yii model relation one-to-newest

Assume we have 3 tables like this:
--------- --------------- -------------------
| USER | | USER_STATUS | | STATUS_DICT |
|-------| |-------------- |------------------
| id | | id | | id (int) |
| name | | user_id | | status (string) |
| email | | status_id | -------------------
--------- | timestamp |
---------------
Each user has many statuses (user_status), related with user.id <-> user_status.user_id. Each user_status has one related record in status_dict which is string label of int status (for example 0 = active).
What I want is make User model to retrieve last user's status using relation. Set relations:
/* User model */
public function relations() {
return array(
'status' => array(self::HAS_ONE, 'UserStatus', 'user_id', 'order'=>'status.id DESC'),
);
}
and
/* UserStatus model */
public function relations() {
return array(
'status' => array(self::BELONGS_TO, 'StatusDictionary', 'status_id'),
'user' => array(self::BELONGS_TO, 'User', 'user_id'),
);
}
Then when I call User::model()->findByPk(1)->status I get latest status for user #1.
BUT.
When I want to find all users with specified status it does not work as I want. Instead of getting only users with specified status (latest record in user_status) I get all users who at least once had this status.
What I have to do to make relation one-to-newest (I call it like this for this case's purpose)? I saw this article and it does the job, but I'm wondering if there is way to achieve it with Yii's conventions.
I wanted to declare named scope like this:
/* in User model */
public function withStatus($status) {
if(!is_numeric($status) && !is_null($status)) {
$status = StatusDictionary::model()->find('lower(name)=lower(:name)', array(':name' => $status))->getAttribute('id');
}
$this->getDbCriteria()->mergeWith(array(
'condition' => (is_null($status) ? 'status.status_id IS NULL' : 'status.status_id=:statusId'),
'params' => array(':statusId' => $status),
'with' => array('status')
));
return $this;
}
But it doesn't work either, User::model()->withStatus(0)->findAll() returns all users with related record in user_status with user_status.status_id = 0 even if some of users have newer, different status.
Thanks in advance for any help.
I found somewhere else similar solution:
Scope function:
public function withStatus($status) {
if(!is_numeric($status) && !is_null($status)) {
$status = StatusDictionary::model()->find('lower(name)=lower(:name)', array(':name' => $status))->getAttribute('id');
}
$this->with('status');
$this->getDbCriteria()->compare('i.statusId', $status);
return $this;
}
Relations record (this join is protecting relation from filter criteria):
'status' => array(self::HAS_ONE, 'Status', 'clientId',
'join' => 'INNER JOIN (select max(id) id from status group by clientId) j ON i.id=j.id',
'alias' => 'i'
),
It works for me.
Ok, I found solution with TheRifler's help from his post. It turned out that I had a solution right under my nose (article linked in a question).
Relation should be defined as below:
/* User model */
public function relations() {
return array(
'status' => array(self::HAS_ONE, 'UserStatus', 'user_id',
'order' => 's.id DESC',
'alias' => 's',
// http://murrayhopkins.wordpress.com/2008/10/28/mysql-left-join-on-last-or-first-record-in-the-right-table/
'join' => 'LEFT JOIN ( SELECT s1.*
FROM `user_status` as s1
LEFT JOIN `user_status` AS s2
ON s1.user_id = s2.user_id AND s1.id < s2.id
WHERE s2.user_id IS NULL ) as `status`
ON (s.user_id = `status`.user_id)'
),
);
}
and the named scope:
public function withStatus($status) {
if(!is_numeric($status) && !is_null($status)) {
$status = StatusDictionary::model()->find('lower(name)=lower(:name)', array(':name' => $status))->getAttribute('id');
}
$this->with('status');
if(!is_null($status)) {
$this->getDbCriteria()->compare('status.status_id', $status);
}
else {
$this->getDbCriteria()->addCondition('s.status_id IS NULL');
}
return $this;
}
It contains some dirty work, because withStatus() uses status join subquery's data if $status is specified or base s alias if $status is null (it's because we're looking for user without record in status table). Maybe this is not most elegant solution, but works as it should.
Now I can use:
User::model()->findByPk(1)->status to access specified user's status
User::model()->withStatus('active')->findAll() list users with specified status (status may be passed as integer [user_status.id] or string [status_dict.name])
User::model()->withStatus(null)->count() get number of users without any status

solution for Insert Data In Relation Table by yii FW

I want do a project with YiiFramework.
in this project I have 1 table that have many relation
I want create a form that insert data in main table and all relation
for example :
I want a form that add student information
I have 2 table
first : id name family
and
second: id student_id field
I want add data in table 1 and then add data in table 2
and all of the this jobs do in a form
do u have any solution for that?
my really reations :
'homehouse' => array(self::HAS_ONE, 'Homehouse', 'HouseId'),
'houseType' => array(self::BELONGS_TO, 'Parametervalues', 'HouseTypeId'),
'owner' => array(self::BELONGS_TO, 'Person', 'OwnerId'),
'region' => array(self::BELONGS_TO, 'Region', 'RegionId'),
'housemultimedias' => array(self::HAS_MANY, 'Housemultimedia', 'HouseId'),
'housestages' => array(self::HAS_MANY, 'Housestage', 'HouseId'),
'tradehouse' => array(self::HAS_ONE, 'Tradehouse', 'HouseId'),
You have to do it with each model, so, let's say you have 2 models (student and job) and you're sending the data from the form with POST method.
Them, in your controller, you save first the main data and second the relation, for example: (ps. this is just a hypothetical example)
Controller
public function actionSave(){
if(isset($_POST['Student'])) {
$Student = new Student();
$Student->Job = new Job();
$Student->attributes = $_POST['Student'];
$Student->Job->attributes = $_POST['Job'];
if($Student->save()){
$Student->Job->student_id = $Student->id;
$Student->Job->save();
}
}
}
With this idea, you can save the data in many relations that you model has.
Reference: How to save related objects?

CGridview and Yii Active Record Relation

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'
);
//....
),

Kohana ORM: Get results based on value of a foreign table

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