I am having a strange behavior which I do not understand with my cakephp 2.
In my Model 'Study' I have a has many relation:
class Study extends AppModel {
public $hasOne = array(
'SubjectFilter' => array(
'className' => 'Subject_filter',
'dependent' => true
)
);
public $hasMany = array(
'ExecutedStudyTable' => array(
'className' => 'ExecutedStudyTable',
'foreignKey' =>'study_id',
'dependent' => true,
),
);
The ExecutedtStudyTable Model looks like this:
class ExecutedStudyTable extends AppModel {
public $belongsTo= array(
'Study' => array(
'className' => 'Study',
'foreignKey' => 'study_id'
),
'User' => array(
'className' => 'User',
'foreignKey' => false,
'conditions' => array('ExecutedStudyTable.user_id = User.id')
)
);
}
When retrieving data everything looks good until I try to do this:
$studies = $this -> Study -> find('all',array(
'conditions' => array(
'Study.user_id' => $cId,
'OR'=>array(
array('Study.state'=>'active'),
array('Study.state'=>'rehearsal')
),
'SubjectFilter.studyCode'=>null,
'ExecutedStudyTable.user_id'=>$user
)
));
I get this error: Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'ExecutedStudyTable.user_id' in 'where clause'
Cakephp builds this query like this:
SELECT `Study`.`id`, `Study`.`user_id`, `Study`.`created`, `Study`.`modified`, `Study`.`started`, `Study`.`completed`, `Study`.`title`, `Study`.`description`, `Study`.`numberParticipants`, `Study`.`state`, `User`.`id`, `User`.`username`, `User`.`password`, `User`.`role`, `User`.`firstName`, `User`.`familyName`, `User`.`email`, `User`.`addressStreet`, `User`.`addressCity`, `User`.`addressZip`, `User`.`phone1`, `User`.`phone2`, `User`.`created`, `User`.`modified`, `User`.`passwordResetCode`, `User`.`passwordResetDate`, `User`.`sendUsernameDate`, `SubjectFilter`.`id`, `SubjectFilter`.`study_id`, `SubjectFilter`.`m`, `SubjectFilter`.`f`, `SubjectFilter`.`age18_24`, `SubjectFilter`.`age25_34`, `SubjectFilter`.`age35_44`, `SubjectFilter`.`age45_54`, `SubjectFilter`.`age55plus`, `SubjectFilter`.`studyCode` FROM `eyevido`.`studies` AS `Study` LEFT JOIN `eyevido`.`users` AS `User` ON (`Study`.`user_id` = `User`.`id`) LEFT JOIN `eyevido`.`subject_filters` AS `SubjectFilter` ON (`SubjectFilter`.`study_id` = `Study`.`id`) WHERE `Study`.`user_id` = 1402402538 AND ((`Study`.`state` = 'active') OR (`Study`.`state` = 'rehearsal')) AND `SubjectFilter`.`studyCode` IS NULL AND `ExecutedStudyTable`.`user_id` = 1130451831
The ExecutedStudyTable is not joined like the SubjectFilter and no alias is defined for the table. Why is this working correctly on the hasOne relation and not on the hasMany? Where do I make the mistake?
I appreciate your replies
If you want to filter a model by associated model's field, one way is by Joining tables.
Note: Mention proper table name for model ExecutedStudyTable. Also mention the join condition between two tables.
$studies = $this->Study->find('all',array(
'fields' => array('Study.*'),
'joins' => array(
array('table' => 'executed_study_table', // Table name
'alias' => 'ExecutedStudyTable',
'type' => 'INNER',
'conditions' => array(
'ExecutedStudyTable.<field name> = Study.<field>', // Mention join condition here
)
)
),
'conditions' => array(
'Study.user_id' => $cId,
'OR'=>array(
array('Study.state'=>'active'),
array('Study.state'=>'rehearsal')
),
'SubjectFilter.studyCode' => null,
'ExecutedStudyTable.user_id' => $user
),
'recursive' => -1
));
Related
I have employees who have one employer. In my employees table I have a field called employer_id. A report using this information is working on my local development setup, but not on my web hosting. I can't figure out what might be causing it not to.
When I try to pull up a list of all the employees I get the following error:
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column
'Employer.last_name' in 'where clause'
SQL Query: SELECT Employee.id, Employee.employer_id,
Employee.first_name, Employee.last_name, Employee.email,
Employee.webinar_id, Employee.questions,
Employee.correct_answers, Employee.required_to_pass,
Employee.status, Employee.role, Employee.sport,
Employee.program, Employee.created, Employee.modified FROM
sportrisk_wp.employees AS Employee WHERE
((Employee.last_name NOT LIKE '') AND (Employer.last_name NOT
LIKE '')) AND contain = '1' ORDER BY Employee.created DESC LIMIT
50
On my local copy when I change debug to 2 I see the following in the SQL log (it's joining the Employer):
SELECT Employee.id, Employee.employer_id,
Employee.first_name, Employee.last_name, Employee.email,
Employee.webinar_id, Employee.questions,
Employee.correct_answers, Employee.required_to_pass,
Employee.status, Employee.role, Employee.sport,
Employee.program, Employee.created, Employee.modified,
Employer.id, Employer.employer_id, Employer.company,
Employer.first_name, Employer.last_name, Employer.phone,
Employer.email, Employer.username, Employer.password,
Employer.usercode, Employer.subscriber, Employer.active,
Employer.admin, Employer.created, Employer.modified,
Employer.status FROM local.employees AS Employee LEFT JOIN
local.users AS Employer ON (Employee.employer_id =
Employer.id) WHERE ((Employee.last_name NOT LIKE '') AND
(Employer.last_name NOT LIKE '')) ORDER BY Employee.created
DESC LIMIT 50
I believe this is all the relevant code.
UsersController
// inside a larger function
// Load Employee Model
$this->loadModel('Employee');
// Base conditions - to hide some old records which do not contain name information.
$conditions = array(
"Employee.last_name NOT LIKE" => '',
"Employer.last_name NOT LIKE" => ''
);
// Find records & paginate
$this->paginate = array(
'limit' => 50,
'conditions' => array(
"AND" => $conditions
),
'order' => array('Employee.created DESC')
);
$employees = $this->paginate('Employee');
?>
Employee Model
<?php
class Employee extends AppModel
{
var $name = 'Employee';
var $recursive = 1;
var $belongsTo = array (
'Employer' => array (
'className' => 'Employer',
'foreignKey' => 'employer_id',
'order' => 'Employee.last_name ASC'
)
);
public $actsAs = array('Containable');
var $validate = array(
'email' => array(
'rule' => 'notBlank'
),
'first_name' => array(
'rule' => 'notBlank'
),
'last_name' => array(
'rule' => 'notBlank'
),
'usercode' => array(
'rule' => 'notBlank'
)
);
}
?>
Employer Model
<?php
class Employer extends AppModel
{
var $name = 'Employer';
var $recursive = -1;
var $hasOne = 'Supervisor';
var $hasMany = 'Employee';
public $useTable = 'users';
public $actsAs = array('Containable');
var $validate = array(
'email' => array(
'rule' => 'notBlank'
),
'password' => array(
'rule' => 'notBlank'
)
);
}
?>
I can't figure out why it isn't linking the Employer model in one environment but is on the other. I've done a straight copy of all the files.
SQL Queries
This is the query I get on the hosted site
SELECT `Employee`.`id`, `Employee`.`employer_id`, `Employee`.`first_name`, `Employee`.`last_name`, `Employee`.`email`, `Employee`.`webinar_id`, `Employee`.`questions`, `Employee`.`correct_answers`, `Employee`.`required_to_pass`, `Employee`.`status`, `Employee`.`role`, `Employee`.`sport`, `Employee`.`program`, `Employee`.`created`, `Employee`.`modified` FROM `sportrisk_wp`.`employees` AS `Employee` WHERE ((`Employee`.`last_name` NOT LIKE '') AND (`Employer`.`last_name` NOT LIKE '')) ORDER BY `Employee`.`created` DESC LIMIT 50
This is what I get on my local site (and what I'm looking for)
SELECT `Employee`.`id`, `Employee`.`employer_id`, `Employee`.`first_name`, `Employee`.`last_name`, `Employee`.`email`, `Employee`.`webinar_id`, `Employee`.`questions`, `Employee`.`correct_answers`, `Employee`.`required_to_pass`, `Employee`.`status`, `Employee`.`role`, `Employee`.`sport`, `Employee`.`program`, `Employee`.`created`, `Employee`.`modified`, `Employer`.`id`, `Employer`.`employer_id`, `Employer`.`company`, `Employer`.`first_name`, `Employer`.`last_name`, `Employer`.`phone`, `Employer`.`email`, `Employer`.`username`, `Employer`.`password`, `Employer`.`usercode`, `Employer`.`subscriber`, `Employer`.`active`, `Employer`.`admin`, `Employer`.`created`, `Employer`.`modified`, `Employer`.`status` FROM `local`.`employees` AS `Employee` LEFT JOIN `local`.`users` AS `Employer` ON (`Employee`.`employer_id` = `Employer`.`id`) WHERE ((`Employee`.`last_name` NOT LIKE '') AND (`Employer`.`last_name` NOT LIKE '')) ORDER BY `Employee`.`created` DESC LIMIT 50
END RESULT
In the end (after a chat with Ved) my paginate array ended up being this, and my report loads.
$this->paginate = array(
'contain' => array('Employer'),
'limit' => 50,
'conditions' => array(
"AND" => $conditions
),
'joins' => array( array( 'alias' => 'Employer', 'table' => 'users', 'type' => 'LEFT', 'conditions' => 'Employer.id = Employee.employer_id' ) ),
'fields' => array('Employee.first_name', 'Employee.last_name', 'Employee.email', 'Employee.webinar_id', 'Employee.questions', 'Employee.correct_answers', 'Employee.required_to_pass', 'Employee.status', 'Employee.role', 'Employee.sport', 'Employee.program', 'Employee.created', 'Employer.id', 'Employer.employer_id', 'Employer.company', 'Employer.first_name', 'Employer.last_name', 'Employer.email', 'Employer.active'),
'order' => array('Employee.created DESC')
);
I think you have forget to load associated data, use contain
// Find records & paginate
$this->paginate = array(
'contain' => array('Employer'),
'fields' => array('Employer.id'), // add more fields
'limit' => 50,
'conditions' => array(
"AND" => $conditions
),
'joins' => array( array( 'alias' => 'Employer', 'table' => 'users', 'type'
=> 'LEFT', 'conditions' => 'Employer.id = Employee.employer_id' ) ),
'order' => array('Employee.created DESC')
);
I am trying to make a query using cakephp model binding. Getting an unexpected result. But whenever I run the query in phpmyadmin it works fine. Getting this error
Column not found: 1054 Unknown column 'Comment.post_id' in 'field list'
Can't figure out what is wrong
PostsController
$posts = $this->Post->find('all', array(
'conditions' => array(
'Post.is_deleted' => 0,
'Post.is_active' => 1,
),
'fields' => array(
'Post.post_id,Post.post_title,Post.post_body,User.username,Post.added_time',
'Product.product_title','COUNT(Comment.post_id) AS total_comment'
),
'GROUP'=>'Post.post_id',
'order' => 'added_time DESC',
));
Post Model
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreign_key' => 'post_id'
),
'Product' => array(
'className' => 'Product',
'foreign_key' => 'post_id'
)
);
public $hasMany = array(
'Comment'=>array(
'className'=>'Comment',
'foreign_key'=>'post_id'
)
);
Comment Model
public $belongsTo = array(
'Post'=>array(
'className'=>'Post',
'foreign_key'=>'post_id'
)
);
My expected query
SELECT posts.`post_title`, users.username,products.product_title, posts.post_body, COUNT(comments.post_id) as total_comments from posts LEFT JOIN comments on comments.post_id = posts.post_id LEFT join users on users.user_id = posts.user_id LEFT JOIN products on products.product_id = posts.product_id WHERE posts.is_active = 1 and posts.is_deleted = 0 GROUP BY posts.post_id
Try by setting the recursive level 1 or 2
$this->Post->find('all', array(
'conditions' => array(
'Post.is_deleted' => 0,
'Post.is_active' => 1,
),
'fields' => array(
'Post.post_id,Post.post_title,Post.post_body,User.username,Post.added_time',
'Product.product_title','COUNT(Comment.post_id) AS total_comment'
),
'GROUP'=>'Post.post_id',
'order' => 'added_time DESC',
'recursive'=>1
));
I would like to bind models, mostly because the datasources change from one model to the other. Let's say I have the following query :
SELECT ext.col1, ext.col2, ext.col3, COUNT(*) Link Count
FROM `external.`Media` ext
INNER JOIN `internal`.`links` l ON ext.someId = links.id
INNER JOIN `internal`.tags t ON l.tag_id = t.tag_id
WHERE t.parent_tag_id = 1098
AND t.metadata = 'someValue'
GROUP BY ext.col1, ext.col2, ext.col3
So I have 3 models from which data is coming from. Currently I have the following in the Media model..
public $belongsTo = array(
'Links' => array(
'className' => 'Links',
'foreignKey' => 'someId',
),
'Tag' => array(
'className' => 'Tag'
)
);
Then in the other models :
Tag Model
public $useTable = "tags";
public $name = 'Tag';
public $hasAndBelongsToMany = array(
'TagLink' => array(
'className' => 'TagLink',
'foreign_key' => 'tag_id',
'conditions' => array(
'Tag.parent_tag_id' => 1098
)
)
);
Link Model
public $useTable = "tag_links";
public $name = 'TagLink';
public $hasAndBelongsToMany = array(
'Tag' => array(
'className' => 'Tag',
'foreign_key' => 'tag_id'
)
);
Then back in the Media model , I use the find expecting to have the model binding set up :
return $this->find("all", array(
'conditions' => array(
'Tag.metadata' => $username
)
));
This generates an error :
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Tag.metadata' in 'where clause'
I am assuming that this is happening because the table is not found
[SQL Generated]
SELECT `Media`.`media_id`, `Media`.`mediatype`, `Media`.`created`, `Media`.`active_flag`,
`Media`.`mediafile`, `Media`.`fallbackfile`, `Media`.`setupId`, `Media`.`externalId`,
`Media`.`width`, `Media`.`height`, `Media`.`brands`, `Media`.`languages`, `Media`.`products`,
`Media`.`projects`, `Media`.`affiliates`, `Media`.`description`, `Media`.`linking_code` FROM
`external`.`ext_media_gallery` AS `Media` WHERE `Tag`.`metadata` = 'testuser' AND
`MediaGallery`.`active_flag` = 'active' AND ((FIND_IN_SET('testuser', `Media`.`affiliates`)) OR
(`Media`.`affiliates` = ''))
This basically shows that the binding is incorrect/not happening
Basically I want to replace the following to avoid using joins on the fly since these would need to apply for every query.. hence the model binding..
This works for me, but I want to convert it to do it using model binding :
return $this->find("all", array(
"joins" => array(
array(
"table" => "internal.links",
"alias" => "Link",
"type" => "INNER",
"conditions" => array(
"Link.id = Media.someId"
)
),
array(
"table" => "internal.tags",
"alias" => "Tag",
"type" => "INNER",
"conditions" => array(
"Tag.tag_id = Link.tag_id",
"Tag.parent_tag_id = 3214",
"Tag.metadata" => $username
)
),
)
));
So there are to classes that have a relationship one to many.
Event Type:
var $hasMany = array(
'Event' => array(
'className' => 'Event',
'foreignKey' => 'event_type_initials',
'dependent' => false,
)
);
Event:
var $belongsTo = array(
'EventType' => array(
'className' => 'EventType',
'foreignKey' => 'event_type_initials'
)
);
Previously the relationship was by the event_type_id, but now it was changed to initals. The problem occurs in the query when trying to access the data. At the end of the following query you can see that the left join is Event.event_type_initials = EventType.id, which doesnt make sense.
SELECT `Event`.`id`, `Event`.`event_type_initials`, `Event`.`user_id`, `Event`.`details`, `Event`.`start`, `Event`.`hours`, `Event`.`minutes`, `Event`.`all_day`, `Event`.`active`, `Event`.`created`, `Event`.`modified`, `EventType`.`id`, `EventType`.`initials`, `EventType`.`name`, `EventType`.`address`, `EventType`.`email`, `EventType`.`phone`, `EventType`.`person`, `EventType`.`color` FROM `sunshine3`.`events` AS `Event` LEFT JOIN `sunshine3`.`event_types` AS `EventType` ON (**`Event`.`event_type_initials` = `EventType`.`id`**) WHERE `Event`.`id` = 30
Any help is more than welcome.
If you specify a foreignKey it will always match to the id of the related table.
You need to use conditions here, though:
// this is important for the correct left joins
var $belongsTo = array(
'EventType' => array(
'className' => 'EventType',
'foreignKey' => false,
'conditions' => array('Event.event_type_initials = EventType.initials')
)
);
// for has many this is usually not necessary/possible (1:n), you can try though
var $hasMany = array(
'Event' => array(
'className' => 'Event',
'foreignKey' => false,
'conditions' => 'Event.event_type_initials = EventType.initials'
)
);
On cakephp 2.1, I have two tables: qca belongs to employee via field emp_number on both tables.
qca model belongsTo : (pleae note foreignKey)
public $actsAs = array('Containable');
var $belongsTo = array('Dir',
'Employee' => array(
'className' => 'Employee',
'foreignKey' => 'emp_number')
);
employee model:
public $actsAs = array('Containable');
On my controller's find, i use 'contain' to retrieve employee info based on emp_number from qca table.
$hoursvalues = $this->Qca->find('all', array('conditions' => $conditions,
'fields' => array('Qca.emp_number', 'Sum(CASE WHEN Qca.qca_tipcode = 1 THEN 1 END) AS Qca__comps', 'Sum(qca_end - qca_start) as Qca__production', 'Sum(Qca.qca_durend) as Qca__idle'),
'contain' => array(
'Employee' => array(
'fields' => array('emp_number', 'emp_ape_pat', 'emp_ape_mat', 'emp_ape_mat'))),
'group' => array('Qca.emp_number'),
));
However, the executed sql sentence shows:
LEFT JOIN `devopm`.`employees` AS `Employee` ON (`Qca`.`emp_number` = `Employee`.`id`)
Whereas
Employee.id should be Employee.emp_number
This is the full sql sentence:
SELECT `Qca`.`emp_number`, Sum(CASE WHEN Qca.qca_tipcode = 1 THEN 1 END) AS Qca__comps, Sum(qca_end - qca_start) as Qca__production, Sum(`Qca`.`qca_durend`) as Qca__idle, `Employee`.`emp_number`, `Employee`.`emp_ape_pat`, `Employee`.`emp_ape_mat`, `Employee`.`id` FROM `devopm`.`qcas` AS `Qca` LEFT JOIN `devopm`.`employees` AS `Employee` ON (`Qca`.`emp_number` = `Employee`.`id`) WHERE `Qca`.`dir_id` = 63 AND FROM_UNIXTIME(`Qca`.`qca_start`, '%Y-%m-%d') >= '2012-07-18' AND FROM_UNIXTIME(`Qca`.`qca_start`, '%Y-%m-%d') <= '2012-07-18' GROUP BY `Qca`.`emp_number`
This results on null values returned for Employee:
array(
(int) 0 => array(
'Qca' => array(
'emp_number' => 'id3108',
'comps' => '2',
'production' => '7784',
'idle' => '529'
),
'Employee' => array(
'emp_ape_pat' => null,
'emp_ape_mat' => null,
'id' => null
)
),
Note: I have other instance of 'contain' working (one with default id = tableName.id). I'm wondering if the foreignKey on belongsTo ('foreignKey' => 'emp_number') is just not good for 'contain' to work?
Can you help?
Thank you so much.
(I found a workaround but slows down the query a great deal (it duplicates the left join and takes forever)
$joins = array(
array('table' => 'publication_numerations',
'alias' => 'PublicationNumeration',
'type' => 'LEFT',
'conditions' => array(
'Publication.id = PublicationNumeration.publication_id',
)
)
);
$this->Publication->find('all', array('joins' => $joins));