How to add where in Yii model search method? - yii

I have four tables in my DB as shown(user, user_test, test, status). There is a M:M to relation between user and test and therefore user_test is a Gerund between them.
The status table has statuses for both user_test and test tables. The tablename field in status table shows which table the status belongs to as shown in the lower image.
I want to show table user_test in a CGridView with the related data in all the three tables user, test and status. All is well as the relations are correct.
Problem: When I want to show status.name in my CGrid like:
array(
'header'=>'Status',
'value'=>'$data->status->name',
),
It gives me name 'completed' but it is the status of table 'test' and the correct one should be 'confirmed' as status_id in user_test is 2.
Any help?
Database design
Status table
public function search()
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria=new CDbCriteria;
$criteria->with=array('user','test','status');
$criteria->compare('id',$this->id);
$criteria->compare('user_id',$this->user_id);
$criteria->compare('test_id',$this->test_id);
$criteria->compare('status.id',$this->status_id);
$criteria->compare('bonus',$this->bonus);
$criteria->compare('user.signum',$this->signum, FALSE);
$criteria->compare('user.email',$this->email, FALSE);
$criteria->compare('test.seats',$this->seats, FALSE);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
'sort'=>array(
'attributes'=>array(
'signum'=>array(
'asc'=>'user.signum',
'desc'=>'user.signum DESC',
),
'email'=>array(
'asc'=>'user.email',
'desc'=>'user.email DESC',
),
'seats'=>array(
'asc'=>'test.seats',
'desc'=>'test.seats DESC'
),
'*',
),
),
));
}
UPDATE:
Relations are here:
//UserTest
....
return array(
'status' => array(self::BELONGS_TO, 'Status', 'status_id'),
'test' => array(self::BELONGS_TO, 'Test', 'test_id'),
'user' => array(self::BELONGS_TO, 'User', 'user_id'),
'testtimeslots'=>array(self::BELONGS_TO, 'TestTimeslots','timeslots_id'),
);
// Test
return array(
'testType' => array(self::BELONGS_TO, 'TestType', 'test_type_id'),
'status' => array(self::BELONGS_TO, 'Status', 'status_id'),
'testCriterias' => array(self::HAS_MANY, 'TestCriteria', 'test_id'),
'testTimeslots' => array(self::HAS_MANY, 'TestTimeslots', 'test_id'),
'userTests' => array(self::HAS_MANY, 'UserTest', 'test_id'),
);
// User
return array(
'userLanguages' => array(self::HAS_MANY, 'UserLanguage', 'user_id'),
'userTests' => array(self::HAS_MANY, 'UserTest', 'user_id'),
);
// Status
return array(
'tests' => array(self::HAS_MANY, 'Test', 'status_id'),
'userTests' => array(self::HAS_MANY, 'UserTest', 'status_id'),
);
UPDATED: The sql I want to generate is:
SELECT * FROM user_test AS UserTest
INNER JOIN user ON user.id=UserTest.user_id
INNER JOIN test on test.id=UserTest.test_id
INNER JOIN (SELECT status.value, name from status where status.tablename='user_test') AS Status ON (Status.value = UserTest.status_id)

Add before the compare part
$criteria->addCondition('Where a = b');
http://www.yiiframework.com/doc/api/1.1/CDbCriteria#addCondition-detail
It takes an overload of and or or like so
$criteria->addCondition('Where a = b', AND);

$criteria=new CDbCriteria;
$criteria->condition="user_name='john' and user_status='active'";

Related

how to save id of one table to another table in codeigniter?

i want to save id of one table to another table how should i write the model and controller.
For example : I have a database like this
tbl1: (id,content,time,date,category)
tbl2: (id,tbl1_id,category_detail)
$tbl1Data = array(
'id' => '',
'content' => 'Hi, content will be here',
'time' => date("H:i:s"),
'date' => date("Y-m-d"),
'category' => 'Electronics'
);
$this->db->insert('tbl1',$postData);
$recordId $this->db->insert_id();
$tbl2Data = array(
'id' => '',
'tbl1_id' => $recordId,
'category_detail' => "alskjdflkajsdlk falsjdflka lsdkfj as",
);
$this->db->insert('tbl1',$postData);

Displaying value from join table in Yii CgridView

I have this very simple table in my view and I'm trying to display value from joined table:
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'skills-grid',
'dataProvider'=>$model->searchWithStudentSuccessRate($id),
'template' => '{items}{pager}',
'cssFile'=>Yii::app()->request->baseUrl. '/themes/'. Yii::app()->theme->name.'/css/table.css',
'htmlOptions'=>array('class'=>'datagrid', 'style'=>'width:550px;'),
'columns'=>array(
array(
'name'=>'name',
),
array(
'name' => 'value',
'header' => Yii::t('MainTrans', 'Value'),
'value' => '$data->student_skills->value',
),
array(
'name' => 'successRate',
'header' => Yii::t('MainTrans', 'Success Rate'),
'value' => '$data->successRate."%"',
),
),
));
And this is the search function:
public function searchWithStudentSuccessRate($id)
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria=new CDbCriteria;
$criteria->compare('id',$this->id);
$criteria->compare('name',$this->name,true);
$criteria->compare('t.threshold',$this->threshold);
$criteria->with = array('student_skills');
$criteria->together = true;
$criteria->select = array('IFNULL(CASE WHEN ROUND((student_skills.value/t.threshold)*100,0) > 100 THEN 100 ELSE ROUND((student_skills.value/t.threshold)*100,0) END,0) as successRate','*');
$criteria->group = "t.name";
$criteria->condition = 'student_skills.student_id = '.$id;
$criteria->compare('successRate',$this->successRate);
$criteria->compare('student_skills.value',$this->value);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
'pagination'=>array(
'pageSize'=>25,
),
'sort'=>array(
'defaultOrder'=>array(
'successRate'=>CSort::SORT_DESC,
),
'attributes'=>array(
'successRate'=>array(
'asc'=>'successRate',
'desc'=>'successRate DESC',
),
'*',
),
),
));
}
But I get error: "Trying to get property of non-object" when I added value column to my CGridView.
Everything works fine without column value (columns successRate and name are fine). Relation should be fine as well. The error I get means that the value doesn't exist but it should since I did something similar in my other views and there it works.
Can anyone tell what's wrong? I'm sure it's something minor but I'm struggling with this embarrasing problem for a while.
Thanks
It means that in some conditions $data->student_skills is NULL. Try change this:
'value' => '$data->student_skills->value',
to this
'value' => 'empty($data->student_skills) ? null : $data->student_skills->value',

Yii: A HAS_MANY B, B HAS_MANY C, find count of C belonging to A

Basically, I have 3 tables.
house_table
===========
house_id
floor_table
===========
floor_id
house_id
room_table
===========
room_id
floor_id
is_occupied
A house has many floor, a floor has many rooms. A room belongs to one floor, a floor belongs to one house. The corresponding models created automatically by gii are HouseTable, FloorTable, RoomTable.
What I need is to findAll() houses that have rooms that are not occupied.
How do I do that? Something like this?
class HouseRecord extends CActiveRecord {
public function relations() {
return array(
'FREE_ROOM_COUNT' => array(self::STAT ...???...),
);
}
}
Sure, I could do it with SQL, but it needs to be done this way, because the result of findAll() is used as a data provider in a grid.
UPDATE
Following tinybyte's advice, here's what finally worked.
public function relations() {
return array(
'FREE_ROOM_COUNT' => array(
self::STAT ,
'FloorTable',
'house_id',
'select' => 'COUNT(rt.floor_id)',
'join' => 'INNER JOIN room_table rt ON t.floor_id = rt.floor_id',
'condition' => 'rt.is_occupied = 0',
),
);
}
And to be used as so:
$criteria = new CDbCriteria;
$criteria->with = array('FREE_ROOM_COUNT');
$criteria->together = true;
$provider = new CActiveDataProvider(HouseTable::model(), array(
'criteria'=>$criteria,
'pagination' => array(
'pageSize' => 1,
),
));
$this->widget('zii.widgets.CListView', array(
'dataProvider'=>$provider,
'itemView'=>'house',
));
Unfortunately it turns out one can not use these STAT relations as conditions! (Confirmed here: Using STAT relation in CActiveDataProvider criteria).
I think this should do the trick
'FREE_ROOM_COUNT' => array(
self::STAT ,
'Floor' ,
'house_id'
'select' => 'count(rt.floor_id)' , // or count(rt.room_id)
'join' => 'Inner join room_table rt ON Floor.floor_id = rt.floor_id' ,
),

yii showing data on cgridview from others table

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

How to sort the fields from relation table in Yii?

I am trying to implement sorting with Yii list view. I have joined 2 tables named provider_favourite and service_request in list view. And the fields and contents from both tables are listing in the list view. But sorting is working only in provider_favourite table, not from service_request table.How can I sort the fields from service_request table? Iam using csort for sorting. I also tried CGrid view. But the same problem is happening in grid view also ..
Iam using the following code to join
$criteria = new CDbCriteria;
$criteria->select = 'favourite_notes,favourite, favourite_added_date,max_budget,preferred_location,service_name';
$criteria->join = 'LEFT JOIN service_request AS s ON service_request_id = favourite';
$criteria->condition = 'favourite_type = 1';
$sort=new CSort('ProviderFavourite');
// $sort->defaultOrder='s.max_budget ';
$sort->applyOrder($criteria);
$sort->attributes = array(
'max_budget' => 'service_request.max_budget',
'service_name' => 'service_request.service_name',
'favourite_added_date'
);
$type = 2;
$data = new CActiveDataProvider('ProviderFavourite', array('criteria' => $criteria, 'pagination' => array('pageSize' => 4),'sort'=>$sort
));
$this->renderPartial('favourites', array(
'ModelInstance' => ProviderFavourite::model()->findAll($criteria),
'dataProvider' => $data, 'type' => $type, 'sort'=>$sort,
));
and also Iam providing sortable attributes in list view
$this->widget('zii.widgets.CListView', array('dataProvider'=>$dataProvider,'itemView'=>'index_1',
'id'=>'request',
'template' => ' {items}{pager}',
'sortableAttributes'=>array('favourite_notes','max_budget','service_name')
));
If more details needed, I will provide. Thanks in advance
You have to specify attributes property of your $sort instance. By default only fields of $modelClass (ProviderFavourite in your case) are sortable.
I think it could look like this (not tested):
$sort->attributes = array(
'service_name' => array(
'asc' => 's.service_name ASC',
'desc' => 's.service_name DESC'
),
// ...another sortable virtual attributes from service_request table
"*"
);
You should not create a CSort object in $sort. The CActiveDataProvider will already provide you with the right sort object. The way you do it, you apply the sort criteria to $criteria before you configure the sort attributes. That can not work.
You should try a simple setup like this instead:
$data = new CActiveDataProvider('ProviderFavourite', array(
'criteria' => $criteria,
'pagination' => array('pageSize' => 4),
'sort'=> array(
'attributes' => array(
'service_name' => array(
'asc' => 's.service_name ASC',
'desc' => 's.service_name DESC'
),
// ...
),
));
If you need to access the related CSort object (which you usually don't if you use a CGridView or CListView, because they deal with that for you), then you get it via $data->sort.