Yii with join using CDbCriteria and CActiveDataProvider with custom Search - yii

Hi I am using Yii to create an Application
I have modified the search() in model and I have come up with a problem.
Users can log in as admins, managers, clients
When a user is logged as a manager, he can view clients only from the store they both belong. So far so good, I've managed to accomplish that.
Now the problem is when I try to prevent managers from viewing other managers from the same store (and therefore edit each other's accounts) in CGridView.
The relations
/**
* #return array relational rules.
*/
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(
'authitems' => array(self::MANY_MANY, 'Authassignment', 'authassignment(userid, itemname)'),
'additionalContacts' => array(self::HAS_MANY, 'AdditionalContact', 'Client_Id'),
'store' => array(self::BELONGS_TO, 'Store', 'Store_Id'),
'user' => array(self::BELONGS_TO, 'User', 'User_Id'),
'genericPoints' => array(self::HAS_MANY, 'GenericPoint', 'Client_Id'),
);
}
The Custom model search
public function searchCustom()
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria=new CDbCriteria;
$criteria->addCondition('t.id !='.$this->id);
$criteria->compare('t.Created',$this->Created,true);
$criteria->compare('t.Updated',$this->Updated,true);
$criteria->compare('t.Discount',$this->Discount,true);
$criteria->compare('t.Discount_Type',$this->Discount_Type,true);
$criteria->addCondition('t.Store_Id ='.$this->Store_Id);
//$criteria->addCondition('t.id !='.$this->id);
//GET FIELDS FROM USER IN SEARCH
$criteria->with=array('user');
$criteria->compare('user.id',$this->User_Id,true);
$criteria->compare('user.First_Name',$this->First_Name,true);
$criteria->compare('user.Last_Name',$this->Last_Name,true);
$criteria->compare('user.Username',$this->Username,true);
$criteria->compare('user.Email',$this->Email,true);
$criteria->addCondition('user.Status = 1');
$criteria->with=array('authitems');
$criteria->compare('authitems.userid',$this->id,false);
$criteria->compare('authitems.itemname','client',false);
$criteria->together = true;
$criteria->order = 't.Created DESC';
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
And this the error I am getting
CDbCommand failed to execute the SQL statement: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'user.Status' in 'where clause'. The SQL statement executed was: SELECT COUNT(DISTINCT `t`.`id`) FROM `client` `t` LEFT OUTER JOIN `authassignment` `authitems_authitems` ON (`t`.`id`=`authitems_authitems`.`userid`) LEFT OUTER JOIN `authassignment` `authitems` ON (`authitems`.`itemname`=`authitems_authitems`.`itemname`) WHERE (((((t.id !=2) AND (t.Store_Id =1)) AND (user.Status = 1)) AND (authitems.userid=:ycp0)) AND (authitems.itemname=:ycp1))
I know it has something to do with the relations the gii set up for me but I can't pinpoint it.
Perhaps authitems' MANY_MANY relation stops the user relation from loading?

Instead of making another assignement for $criteria->with (which will override the previous one), you should simply try :
$criteria->with=array('user', 'authitems');

Related

Yii 1.1 relationship search issue

1. Table meeting
Model name CoreMeeting
Fields id, title, start time, created_by
Relationship:
'MeetingParticipants' => array(self::HAS_MANY, 'MeetingParticipants', 'meeting_id'),
2. Table core_meeting_participant
Model name is meeting_participant
Fields are id, meeting_id, participant_id, group_id
Relationship:
'meeting' => array(self::BELONGS_TO, 'CoreMeeting', 'meeting_id'),
'group' => array(self::BELONGS_TO, 'MeetingGroup', 'group_id'),
3. Table core_meeting_group
Model name is MeetingGroup
Fields are id, group_name
My search filter in the meeting model is:
public function search()
{
$group=filter_var($_REQUEST['group'], FILTER_SANITIZE_STRING);//contain group name
$criteria=new CDbCriteria;
$user_id = Yii::app()->user->id;
$criteria->compare('id',$this->id);
$criteria->compare('title',$this->title,true);
$criteria->with=array('MeetingParticipants'=>array("select"=>"*"),'MeetingParticipants.group'=>array('select'=>'id,group_name'));
if(isset($this->start_time) && !empty($this->start_time))
$criteria->compare('start_time',date("Y-m-d", strtotime($this->start_time)), true);
$criteria->compare('created_by',$user_id);
if(isset($group)&&!empty($group))
$criteria->compare('group.group_name',$group);
$criteria->together = true;
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
I have created 4 meetings, each meeting have at least 5 participants each participant belongs to a meeting group. I want to list all meetings with the following filed meeting title, groups and meeting time.
My problem is if I enable $criteria->together = true; if the meeting has more than 10 participants that will show only 1 meeting in grid view. If I disable this that will show all but I can't search with the meeting name.
SQL fiddle link is http://sqlfiddle.com/#!9/fdaacf
full SQL dump https://pastebin.com/NtpMuCpE
Documentation for CDbCriteria->together property:
https://www.yiiframework.com/doc/api/1.1/CDbCriteria#together-detail
Whether the foreign tables should be joined with the primary table in a single SQL. This property is only used in relational AR queries for HAS_MANY and MANY_MANY relations.
As I can see your relation in CoreMeeting model:
'group' => array(self::BELONGS_TO, 'MeetingGroup', 'group_id')
Obviously you are trying to fetch one-to-one (BELONGS_TO) relation CoreMeeting -> MeetingGroup eagerly by globally setting to your CDbCriteria->together = true; which is not correct according to the documentation.
What I would suggest you, first of all get rid of this global together setting to CDbCriteria and try changing your with clause like this:
$criteria->with = [
// this is your has-many relation (one meeting has several participants), thus we set here `together` to true
'MeetingParticipants' => [
'together' => true,
'with' => [
// this is your one-to-one relation (one participant has one group)
'group'
]
]
];

Adding a condition to every query in a model

I have a system where all tables in the MySQL database are populated with external data (synchronized with another system every 5 minutes). All tables have a column DELFLAG which is used to mark disabled entries.
So I have about 15 AR models in Yii that are linked to those tables. Whenever I make a query, I need to add something like $criteria->addCondition('DELFLAG=0'). This gets ugly if there are multiple tables present in the query, as every one of them has the flag. Also, there's a potential for error if I forget one of those conditions.
Here's how I do it now:
public function search($showtype = NULL) {
$criteria=new CDbCriteria;
if (isset($showtype)) {
$criteria->with = array(
'TSSSHOW',
'TSSSHOW.TSSSHOWTYPEITEM',
'TSSSHOW.TSSSHOWTYPEITEM.TSSSHOWTYPE'
);
$criteria->compare('TSSSHOWTYPEITEM.TSSSHOWTYPEID', $showtype);
$criteria->addCondition('TSSSHOW.DELFLAG=0');
$criteria->addCondition('TSSSHOWTYPEITEM.DELFLAG=0');
}
$exp = new CDbExpression("`TSSEVENT_START_DATETIME` > NOW()");
$criteria->addCondition($exp);
$criteria->addCondition('t.DELFLAG=0');
$criteria->together = true;
$criteria->order = 't.TSSEVENT_START_DATETIME ASC';
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
'pagination' => array(
'pageSize' => 15,
),
));
}
Is there a convenient way to include this condition into every query that includes these tables? Perhaps a special class which my models shall descend from (as opposed to the default CActiveRecord)?
I'd suggest declaring named scope for these models:
public function scopes()
{
return array(
'disabledEntry'=>array(
'condition'=>'DELFLAG=1',
),
);
}
Using the named scope: Model::model()->disabledEntry()->findAll();
You can provide scope when relaring to model in the with() statement as well: ModelA::model()->with('model:disabledEntry')->findAll();
But if you have to set this condition each time, you may set defaultScope:
public function defaultScope()
{
return array(
'condition' => 'DELFLAG=0',
);
}
Thus, this model by default would have this condition.
Update: Since you have the identically named columns in several models, YII can have some column name ambiguity troubles while building sql-query. If it is the case, use alias in scope declaration or use the following statement to set current table alias explicitly while declaring scope 'condition' => $this->getTableAlias(false, false) . '.DELFLAG=0',
You can create Your own class e.g. CustomActiveRecord extends CActiveRecord
then You should override findAll, findByPk methods with your criteria..
Other solution is to run this query 'DELETE FROM table_name WHERE DELFLAG=1' after every import..

CakePHP - HABTM find() don't make the JOIN to other tables

My title will look like naive but I have to say I read/searched/tested everything possible, but my find() method don't implement the JOIN to related tables in the SQL query. I used it several times in other projects without problems but here...
Here my 2 models (nothing special but the manual definition of the related model) :
class Pflanzen extends AppModel {
public $useTable = 'pflanzen';
public $hasAndBelongsToMany = array(
'Herbar' => array(
'order'=>'Herbar.order ASC',
'joinTable' => 'herbar_pflanzen',
'foreignKey' => 'pflanzen_id',
'associationForeignKey' => 'herbar_id')
);
}
class Herbar extends AppModel {
public $useTable = 'herbar';
public $hasAndBelongsToMany = array(
'Pflanzen' => array('joinTable' => 'herbar_pflanzen',
'foreignKey' => 'herbar_id',
'associationForeignKey' => 'pflanzen_id')
)
}
Here my query in the "Herbar" controller (can't be more normal...) :
$pflanzen = $this->Herbar->Pflanzen->find('all',array(
'fields'=>array('Herbar.name','Pflanzen.linkplatter'),
'conditions' => array('Pflanzen.linkplatter' => true),
'order' => 'Herbar.name',
'limit' => 10,
'recursive'=>2)
);
$this->set('pflanzen',$pflanzen);
and the resulting error in the view :
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Herbar.name' in 'field list'
SQL Query: SELECT `Herbar`.`name`, `Pflanzen`.`linkplatter`, `Pflanzen`.`id` FROM `burgerbib`.`platter_pflanzen` AS `Pflanzen` WHERE `Pflanzen`.`linkplatter` = '1' ORDER BY `Herbar`.`name` ASC LIMIT 10
You can see that their is no JOIN in the SQL. Why ?? What do I wrong ?
I would really appreciate your help as I'm searching for hours and do no more see any solutions and didn't find nothing using google. Thanks in advance !!
HABTM doesn't make joined queries, it makes a query for all base records and more queries as needed for each relationship to fill the array. Your condition assumes a join, hence the error.
You can force a join using the 'joins' parameter. http://book.cakephp.org/1.2/en/view/872/Joining-tables
In the End, the better way of doing this is using the containable behaviour. Force Join is only useful when the containable behavior don't respond to the need :
http://book.cakephp.org/2.0/fr/core-libraries/behaviors/containable.html#using-containable

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