Yii 1 - Ordering by relation field not working - php

I know its a common question but I get a specific error about a not joined table.
My main Model (Group) has this relation (1:1):
return array(
'groupType'=>array(self::BELONGS_TO, 'GroupType', 'groupTypeId','order'=>'name ASC');
As you can see, I've tried to put an order attributes thats is not working.
As you can immagine, my other module, GroupType, has an attribute called 'name'.
In the grid view, the relation model is printed well simply using the name of relation:
array(
'name'=>'groupType',
'value'=>'$data->groupType->name'
),
Autoload is working fine in GridView.
Unfortunally the column is not sortable. So I put a CSort parameter to DataProvider provided in the Search Method as below:
$sort = new CSort();
$sort->defaultOrder = 'id asc';
$sort->attributes = array(
'groupType'=>array(
'asc'=>'groupType.name asc',
'desc'=>'groupType.name desc'
),
'description'=>array(
'asc'=>'t.description asc',
'desc'=>'t.description desc'
),
'name'=>array(
'asc'=>'t.name asc',
'desc'=>'t.name desc'
),
'*');
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
'sort'=>$sort
));
Ordering by description and name (attribute of Group) works fine. When I tried to order by groupType, i get this simply error:
SQL: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'groupType.id' in 'order clause'. The SQL statement executed was: SELECT * FROM `cprol_groups` `t` ORDER BY groupType.idname asc LIMIT 20
As you can see, framework is not joining my related table. And in this case, what Alias I have to use due to same-property name?
Thanks you for any advice!

I've resolved forcing the join.
In the criteria, I have to force join:
$criteria->join = 'join '.GroupType::model()->tableName().' gt on gt.id = t.groupTypeId';
or using the ->with statement:
$criteria->with = 'groupType';
removing the default order in relation and adding an alias:
return array('groupType'=>array(self::BELONGS_TO, 'GroupType', 'groupTypeId','alias'=>'gt'));
so you can order by relation field:
$sort = new CSort();
$sort->defaultOrder = 'gt.name asc';
$sort->attributes = array(
'groupTypeId'=>array(
'asc'=>'gt.name asc',
'desc'=>'gt.name desc'
),

Related

Yii add one more select to CDbCriteria

I'm relatively new to Yii.
I fell confident with raw SQL but still get a bit lost when it comes to
ORM. So this may be a dummy question.
I've retrieved all necessary records building such CDbCriteria:
$criteria = new CDbCriteria(array(
'select' => 'sum(items) as items',
// 'condition' => 't.items > 0 and order.storage = "'Product::STORAGE_LOCAL . '"',
'condition' => 't.items > 0 and order.storage = "' . Product::STORAGE_LOCAL . '"',
'order' => 'sum(items) DESC',
'with' => array(
'product' => array(
'select' => 'code, title, producer, local_rest',
**// 'select' => 'code, title, producer, sum(local_rest) as local_rest',**
'group' => 'product.code',
)
),
'join' => 'inner join `order` `order` on `t`.`order_id` = `order`.`id`',
// 'group' => '`product`.`code`, `product`.`title`',
'group' => '`product`.`code`',
'together' => true
));
I try to get sum of local_rest field doing group by. Unfortunately it does not return what it should be.
This is how I tried to build in it into CDbCriteria:
// 'select' => 'code, title, producer, sum(local_rest) as local_rest',.
- no luck.
I can get the it using separated query as:
$sum_local_rest = Yii::app()->db->createCommand("
SELECT id,code, title, sum(local_rest) as sum_rest FROM product GROUP BY code
ORDER BY `sum_rest` DESC
");
One more caution - there are duplicate records in product table.
I.e. we have the same product more than one time. But if I use GROUP BY it helps to delimiter this shortcoming. This is due to bad DB design and should be fixed in the future.
The problem is that I need somehow to bind it with CDbCriteria, because it's used by CDataProvider and CDataProvider is used by GridView.
Any tips how to connect these two question in one CDbCriteria?
Thanks in advance
EDIT
Looking at the current answers I feel I need summarize. The main problem is that I need to tell CDbCriteria to retrieve records (bound by HAS_Many connections) and to calculate SUM of all these records and to make CDbCriteria to do GROUP BY of these records.
No other way. I can't do it explicitly. Because I pass CDbCriteria to CDataProvider and it should run queries. This is how things work in Yii (as far as I understand).
//You can merge your criteria like here:
$criteria = new CDbCriteria(); //First criteria
$criteria_2 = new CDbCriteria(); //Second criteria
$criteria->mergeWith($criteria_2); //Merge criteria and criteria_2
SomeModel::model()->findAll($criteria); //Find by criteria
I don't see why something like this shouldn't work:
$criteria = new CDbCriteria;
$criteria->select = array(
"SUM(t.items) as items",
"SUM(product.local_rest) as product_local_rest"
);
$criteria->condition = "t.items > 0 and order.storage = "' . Product::STORAGE_LOCAL . '"';
$criteria->join = 'inner join `order` `order` on `t`.`order_id` = `order`.`id`';
$criteria->with = "product";
$criteria->group = "product.code";
$criteria->together = true;
Since you're setting together to true, the columns of your relation should be available to your query, aliased by the relation's name (product). (Note: to access the result of SUM(product.local_rest) on the models returned by CActiveDataProvider, you'll need to set $product_local_rest as a public property on the class of the returned models.)
Alternatively, if you're more comfortable writing raw SQL, you could use CDbCommand to generate an array of results, and then use CArrayDataProvider instead of CActiveDataProvider. http://www.yiiframework.com/doc/api/1.1/CArrayDataProvider
You also don't have to pass all elements to criteria. Try to split criteria into more code like this:
$criteria = new CDbCriteria();
$criteria->select = 'sum(items) as items, ' . Product::STORAGE_LOCAL;
$criteria->condition = 't.items > 0 and order.storage = ' . Product::STORAGE_LOCAL;
//etc.

Searching and sorting by related model in CGridView Yii

I want to make my CGridView columns searchable after reading this the problem is when i use one cdbCriteria->with condition it works fine. But It gives an error when i add more than one cdbCriteria->with condition. More specifically I am pasting my code here.
$criteria=new CDbCriteria;
$criteria->with = array( 'teacherGradeSection','course' );
$criteria->with = array( 'teacherGradeSection','teacher' );
$criteria->together = true;
$criteria->compare('course.name', $this->course_search, true );
$criteria->compare('teacher.firstname', $this->teacher_search, true );
When i use only one with condition it works fine but gives an error when more than one with condition is used.
Error
SQLSTATE[42S22]: Column not found: 1054 Unknown column
Edit 1:
These are the available relations
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(
'submissions' => array(self::HAS_MANY, 'Submission', 'task_id'),
'teacherGradeSection' => array(self::BELONGS_TO, 'TeacherGradeSection', 'teacher_grade_section_id'),
'course' => array(self::HAS_MANY, 'Course', array('course_id'=>'id'),'through'=>'teacherGradeSection'),
'teacher'=> array(self::HAS_MANY,'Teacher',array('teacher_teacher_id'=>'teacher_id'),'through'=>'teacherGradeSection'),
); }
As suugested by #Nisic and #Noam148 when i use $criteria->with=array('teacherGradeSection','course','teacher ')
I got the following error
CDbCommand failed to execute the SQL statement: SQLSTATE[42000]: Syntax error or access violation: 1066 Not unique table/alias: 'teacherGradeSection'. The SQL statement executed was: SELECT COUNT(DISTINCT `t`.`id`) FROM `task` `t` LEFT OUTER JOIN `teacher_grade_section` `teacherGradeSection` ON (`t`.`teacher_grade_section_id`=`teacherGradeSection`.`id`) LEFT OUTER JOIN `course` `course` ON (`course`.`id`=`teacherGradeSection`.`course_id`) LEFT OUTER JOIN `teacher_grade_section` `teacherGradeSection` ON (`t`.`teacher_grade_section_id`=`teacherGradeSection`.`id`) LEFT OUTER JOIN `teacher` `teacher` ON (`teacher`.`teacher_id`=`teacherGradeSection`.`teacher_teacher_id`)
try defining two relations that will resolve the conflict
'teacherGradeSection' => array(self::BELONGS_TO, 'TeacherGradeSection', 'teacher_grade_section_id'),
'teacherGradeSectiontwo' => array(self::BELONGS_TO, 'TeacherGradeSection', 'teacher_grade_section_id'),
and in your criteria
$criteria->with['course'] = array( 'teacherGradeSection','course' );
$criteria->with['teacher']= array( 'teacherGradeSectiontwo','teacher' );
Your second with will actually replace your first with so you will not eager load the course, so when you later reference it in compare you get the error.
You need only one with that prepares both, course and teacher. I can't be sure how to write it without the database schema, but you can blind try this:
$criteria->with = array( 'teacherGradeSection','course', 'teacher' );
or just explain us the relations
You can use the $criteria->with property one time. When you used $criteria->with for the second time you override the first one.
See here how to use the with property. On this way you add 3 different relations to your 'CDbCriteria':
$criteria=new CDbCriteria;
$criteria->with = array( 'teacherGradeSection','course','teacher');
$criteria->together = true;
$criteria->compare('course.name', $this->course_search, true );
$criteria->compare('teacher.firstname', $this->teacher_search, true );

Sorting in yii Cdbcriteria with sum(amount)

I need to show highest amount on the top in yii Cgridview.
I have two models: members and billing,
member_id is foregion key in billing model.
My Model Function:
public function getImportantMembers(){
$criteria = new CDbCriteria;
$criteria->select ='t.*,b.billing_id,b.amount,b.billing_date,b.member_id,b.billing_status , sum(b.amount) AS totalamount';
$criteria->join = 'JOIN billing AS b ON b.member_id = t.id ';
$criteria->addCondition("b.billing_date > DATE_SUB(NOW(),INTERVAL 2 MONTH) AND b.billing_status='c' AND b.amount > 150 ");
$criteria->group = 't.id';
$criteria->order = " totalamount DESC";
return new CActiveDataProvider(get_class($this),array(
'pagination'=>array(
'pageSize'=> Yii::app()->user->getState('pageSize',Yii::app()->params['defaultPageSize']),),
'criteria'=>$criteria,
));
}
but this function not showing correctly highest amount at the top or amount DESC
How I can fix this?
Define relations in your models and use them in your criteria. Then pass a CSort object to the DataProvider like below. You can define custom sort parameters as long as you define them in your model.
return new CActiveDataProvider( 'Model', array(
'criteria'=>$criteria,
'sort'=>array(
'attributes'=>array(
...........
...........
'*',
),
),
));
For details this link will help.
http://www.yiiframework.com/wiki/281/searching-and-sorting-by-related-model-in-cgridview/
If that link doesn't help post your model codes for both the tables and I'll try to provide some example code.

Using an existing relation HAS-MANY on a criteria

i'm having some problems using a custom made function to search specific data from my database.
I have TableA(id,....) and TableB(id, tablea_id, userid,...)
Model TableA has the following relation code:
'relation_name' => array(self::HAS_MANY, 'TableB', 'tablea_id')
I need to create a custom made function to query TableA and give me results that exist on TableA that contain a certain userid on TableB data, also TableB is related to TableA from tablea_id field.
I found the solution for my problem by using this code:
public function findABCD($user_id)
{
$criteria=new CDbCriteria;
$criteria->join = 'left join TableB on t.id=TableB.tablea_id';
$criteria->condition = "TableB.userid = ".$user_id;
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
This works, but I'm not taking advantage of the relation that I have created earlier.
Any ideas of what I need to use?
I tested a lot of things but I'm receiving an SQL error.
This is what I have tested earlier (instead of the criteria Join and the Condition):
$criteria->with=array('relation_name');
$criteria->condition = "relation_name.userid= ".$user_id;
//this does not work as well
//$criteria->condition = "TableB.userid= ".$user_id;
The error is this:
SQL: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'relation_name.userid' in 'where clause'.
Any ideas?
I do this kind of joins with ActiveRecord queries, not sure if it will work for CActiveDataProvider:
$criteria->with = array(
'relation_name' => array(
'joinType' => 'INNER JOIN',
'on' => 'user_id = :user_id',
'params' => array( 'user_id' => $userId )
)
);
$criteria->together = true;

How do I specify the tablename in a filter in Yii?

There's two tables, attendee, and group. both have a gender field. Each attendee is assigned to 1 group. I've got my CGridView successfully showing the number of attendees, but when I added code to filter on gender, I get the following error. To calculate the number of attendees in a group, I'm manually performing a join; but the join makes the field 'gender' ambiguous. Is there any way to specify in the filter that it should use the 't' or base table?
When I load the view it operates correctly; when I click to sort on gender, it gives me this message:
Error Message
> Error 500: <h1>CDbException</h1>
> <p>CDbCommand failed to execute the SQL statement: SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'gender'
> in where clause is ambiguous. The SQL statement executed was: SELECT
> COUNT(*) FROM (SELECT t.*, IFNULL( count(tbl_attendee.id), 0) as
> attendeeCount FROM `tbl_group` `t` LEFT JOIN tbl_attendee ON
> tbl_attendee.group_id = t.id WHERE gender LIKE :ycp0 GROUP BY t.id) sq
> (/Applications/MAMP/htdocs/yiiroot/framework/db/CDbCommand.php:516)</p><pre>#0
> /Applications/MAMP/htdocs/yiiroot/framework/db/CDbCommand.php(411):
> CDbCommand->queryInternal('fetchColumn', 0, Array)
Model Code
$criteria=new CDbCriteria;
$criteria->compare('name',$this->name,true);
$criteria->compare('gender',$this->gender,true);
$criteria->compare('agegroup',$this->agegroup,true);
$criteria->select = 't.*, IFNULL( count(tbl_attendee.id), 0) as attendeeCount';
$criteria->join = 'LEFT JOIN tbl_attendee ON tbl_attendee.group_id = t.id';
$criteria->group = 't.id';
$sort = new CSort();
$sort->attributes = array(
'attendeeCount'=>array(
'asc'=>'attendeeCount ASC',
'desc'=>'attendeeCount DESC',
),
'*', // add all of the other columns as sortable
);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
'sort'=>$sort,
));
View Code
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'group-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'name',
array(
'name'=>'gender',
'value'=>'Group::$genderLabels[$data->gender]',
'filter'=>Group::$genderLabels, // <-- This is the filter that breaks.
// $genderLabels = array('Female','Male);
),
Ah! I got it.
Changed this line in the model code:
$criteria->compare('gender',$this->gender,true);
to
$criteria->compare('t.gender',$this->gender,true);
Found the answer here.

Categories