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.
Related
Well, I have 3 tables: Products, Stores and Alerts.
Table alerts have the product_id and table products have the store_id. I have made a model relation so I can get an array with the data of the 3.
The problem is, I need to add a condition where it would only return alerts where client_id (on table Stores) is equal to the id of the client trying to access the data.
$clientidx = $clientlist->idx;
$criteria = new CDbCriteria();
$criteria->with = 'product';
$criteria->with = 'product.store';
$criteria->addCondition('product.store.client_idx = :client_idx');
$criteria->params[':client_idx'] = $clientidx;
$openalerts = Openalert::model()->findAll($criteria);
Why can't I make the condition see product.store.client_idx?
In condition you should not use relation alias (product.store), but table alias. If you want to add condition for client_idx column in store table, you need:
$criteria->addCondition('store.client_idx = :client_idx');
Also this:
$criteria->with = 'product';
$criteria->with = 'product.store';
$criteria->with = 'product' has no effect, since you're overwriting this property immediately. If you want to set multiple relations, you need to pass array to $with. ['together' => true] will also ensure that correct JOIN is created for query, so your condition will work correctly:
$criteria->with = [
'product',
'product.store' => ['together' => true],
];
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'
),
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 );
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;
I am trying to do a Join query using CDBCriteria in Yii framework. The issue is the join query works successfully but it does not display the columns from other tables.
I am doing in the following way
$criteria = new CDbCriteria;
$criteria->order = 't.id desc';
$criteria->select = '*';
$criteria->join = ' INNER JOIN table2 INNER JOIN table3 INNER JOIN table4';
When i run this, I can see only the mail table1 columns displayed. Other columns are not shown.
In my model class, I have the relation has
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(
'user' => array(self::BELONGS_TO, 'DmPhoneUser', 'user_id'),
'command' => array(self::BELONGS_TO, 'DmOtaCommand', 'command_id'),
'partner' => array(self::BELONGS_TO, 'DmPartner', 'partner_id'),
);
}
********************************************************
public function actionGetHistory($start, $per_page)
{
$partner_id = '10';
$criteria = new CDbCriteria;
$criteria->order = 't.history_id desc';
$criteria->select = 't.*, table2.*';
$criteria->join = ' INNER JOIN table2 ON t.command_id = table2.command_id';
$result = Table_1_class::model()->with('command')->findAll();
$history_data = CJSON::encode($result);
echo $history_data;
}
here command_id is common in table1 and table2.
This is how I am using the criteria code.
As I said, Yii's Active Record implementation will only use columns which are defined in the table itself or the tables you are linking to through with, not arbitrary columns you return in the resultset.
Solution 1: Define a relation to table2, add that relation to with, and get rid of join. Then you'll probably need to convert each returned object to an array - CJSON::encode will not handle a model with relations well.
Solution 2: Use Yii::app()->db->createCommand("SQL query")->queryAll(); instead of Active Record. This will produce an array of arrays, which CJSON::encode will have no problem with.