Association finding wrong table - cakephp

I am trying to get contain the Children of my DomainTypes table for my DomainTypes view() function but it is complaining with "Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Children.domain_type_id' in 'where clause'" The problem is that my DomainTypes table doesn't have a domain_type_id column. The Domain table does. Here is the code for my DomainTypes view():
public function view($id) {
if (!$id) {
throw new NotFoundException(__('Invalid DomainType'));
}
$domainType = $this->DomainTypes
->find()
->where(['DomainTypes.id' => $id])
->contain(['Parent', 'Children', 'Affiliates'])
->first();
$this->set(compact('domainType'));
}
So a little about my setup. I have 2 tables, Domains and DomainTypes. Both use the Tree behavior.
Here is the initialize code for the Domain Table:
public function initialize(array $config){
parent::initialize($config);
$this->displayField('name');
$this->addBehavior('Tree');
//Associations
$this->hasMany('Children', [
'className' => 'Domains',
]);
$this->belongsTo('Parent', [
'className' => 'Domains',
]);
$this->belongsTo('Affiliates');
$this->belongsTo('DomainTypes');
}
And here is the initialize code for the DomainTypes Table:
public function initialize(array $config){
parent::initialize($config);
$this->displayField('name');
$this->addBehavior('Tree');
//Associations
$this->hasMany('Children', [
'className' => 'DomainTypes',
]);
$this->belongsTo('Parent', [
'className' => 'DomainTypes',
]);
$this->belongsTo('Affiliates');
$this->hasMany('Domains');
}
Both very similar but also clearly defining which className to use. Why is Cakephp 3 assuming there is a domain_type_id column on my DomainTypes table? Thanks in advance!

We were both close, but not quite right. ndm, your answer was helpful though as it did point to another mistake I was making. The final association code should look like this:
$this->hasMany('Children', [
'className' => 'DomainTypes',
'foreignKey' => 'parent_id',
]);
$this->belongsTo('Parent', [
'className' => 'DomainTypes',
]);
When I was trying to enter the foreign key, Instead of "foreignKey", I was using "foreign_key" which was pretty much useless. Thanks for the help ndm.

Update I wasn't paying attention properly, and CakePHP seems to be actually correct when it auto-generates a foreign key named domain_type_id, it's a hasMany association and so the FK should be based on the source table.
The solution is still the same, the FK needs to be set explictly, and as already figured by #MjGaiser the parent_id for the belongsTo association isn't necessary, and instead the FK for the hasMany association should use parent_id instead of child_id:
$this->hasMany('Children', [
'className' => 'DomainTypes',
'foreignKey' => 'parent_id'
]);
$this->belongsTo('Parent', [
'className' => 'DomainTypes'
]);

Related

Cakephp3 doesn't recognize the table with different name in deep associations

I have created a table called Delegates, and I created a UsersTable and User Entity. And I have used $this->setTable('delegates'); in UsersTable to be able to access Delegates from $this->Users; (I just want to say I have created a User Model with delegates table)
So far so good...
In my application I am trying to access deep associations. Every thing is fine with this query but when I contain the User model I get The Users association is not defined on Comments.
I can confirm the associations are set correctly.
...
// There are more associations up there.
...
'Comments', [
'className' => 'App\Model\Table\CommentsTable',
'foreignKey' => 'delegate_assessment_criteria_id',
'belongsTo' => [
'Assessors' => [
'className' => 'App\Model\Table\AssessorsTable',
'foreignKey' => 'assessor_id',
],
'Users' => [
'className' => 'App\Model\Table\UsersTable',
'foreignKey' => 'delegate_id',
]
]
]
Here is deep association.
...
// There are more associations up there.
...
'Comments' => function($q) {
return $q
->select([
'id'
])
->order(['Comments.created DESC'])
->contain([
'Assessors' => function($q) {
return $q
->select([
'id'
])
->enableAutoFields();
}
])
->contain([
'Users' => function($q) {
return $q
->select([
'id',
'delegate_id'
])
->enableAutoFields();
}
])
->enableAutoFields();
}
2 Notes:
If I contain the User model in very first in hierarchy of my query I
can access the User fields but in deep association this doesn't
work.
If I contain Delegates it works.
I believe there is a problem with Cakephp query builder.
Alright finally I figured it out. I have done it before I don't know why I forgot. Probably because I was in deep association I drained into it.
The deep association contain was creating conflict with very first contain. if I would set different propertyName in deep associations then it does the purpose.
Bear in mind you must set this associations on your Table Models.
'Comments', [
'className' => 'App\Model\Table\CommentsTable',
'foreignKey' => 'delegate_assessment_criteria_id',
'belongsTo' => [
'Assessors' => [
'className' => 'App\Model\Table\AssessorsTable',
'foreignKey' => 'assessor_id',
],
'Delegates' => [ // <= Here set the name of Assossiation you want to be shown in array
'className' => 'App\Model\Table\UsersTable',
'propertyName' => 'delegates', // <= Add propertyName and set another name here
'foreignKey' => 'delegate_id',
]
]
]
And on association
'Comments' => function($q) {
return $q
->select([
'id'
])
->order(['Comments.created DESC'])
->contain([
'Assessors' => function($q) {
return $q
->select([
'id'
])
->enableAutoFields();
}
])
->contain([
'Users' => function($q) {
return $q
->select([
'id',
// 'delegate_id' // <= Remove this - We have already done it when we set the association on above code.
])
->enableAutoFields();
}
])
->enableAutoFields();
}

Struggling to save in 2 tables with hasOne association with cakePHP 3

I’m struggling with the hasOne association. Here are my 2 tables :
Actually the ‘Etudiant’ (means student) has one User associated to it with the foreign key id present in both table which is the primary key of Users.
Here is the model EtudiantTable (student) :
class EtudiantTable extends Table
{
/**
* Initialize method
*
* #param array $config The configuration for the Table.
* #return void
*/
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('etudiant');
$this->setDisplayField('ETUDIANT_ID');
$this->setPrimaryKey('ETUDIANT_ID');
$this->addBehavior('Timestamp');
$this->belongsTo('Maitredestage', [
'foreignKey' => 'MAITREDESTAGE_ID',
'joinType' => 'INNER'
]);
$this->belongsToMany('Tuteuriut', [
'foreignKey' => 'ETUDIANT_ID',
'targetForeignKey' => 'TUTEURIUT_ID',
'joinTable' => 'Suivre'
]);
$this->belongsTo('Users', [
'foreignKey' => 'ID',
'joinType' => 'INNER'
]);
}
And here is the add function in the EtudiantController :
public function add()
{
$etudiant = $this->Etudiant->newEntity();
if ($this->request->is('post')) {
$etudiant = $etutable->patchEntity($etudiant, $this->request->data(), [
'associated' => ['Users']
]);
if ($this->Etudiant->save($etudiant)){
$this->Flash->success(__('The etudiant has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The etudiant could not be saved. Please, try again.'));
}
$this->set(compact('etudiant'));
}
And my usersTable model is like this :
$this->belongsTo('Users', [ 'foreignKey' => 'ID', 'joinType' => 'INNER' ]);
I want to do this : when i insert an Etudiant it inserts a new user and both have the same id.
But currently when i insert an Etudiant i get an error saying “no default value…id is empty” but the id is supposed to be inserted automatically.
I'm completely stuck so i need your help. Thanks in advance.

Creating Association with condition using other association in CakePHP 3

I'm building a cake php 3 app. My app model includes 3 Tables (amongst others):
Structures
MeasuringPoints
DeviceTypes
where each Strcuture can have multiple MeasuringPoints:
// StrcuturesTable.php
...
public function initialize(array $config)
{
parent::initialize($config);
...
$this->hasMany('MeasuringPoints', [
'foreignKey' => 'structure_id'
]);
}
Further, each measuring point is of a certain device type:
// MeasuringPointsTable.php
...
public function initialize(array $config)
{
parent::initialize($config);
...
$this->belongsTo('DeviceTypes', [
'foreignKey' => 'device_type_id',
'joinType' => 'INNER'
]);
}
What i'm lookong for, is how to create a 'SpecialMeasuringPoints' association in the Structure table.
Somewhat like:
// MeasuringPointsTable.php
...
$this->hasMany('SpecialMeasuringPoints',[
'className' => 'MeasuringPoints',
'foreignKey' => 'structure_id',
'conditions' => ['MeasuringPoints.DeviceTypes.id'=>1]
]);
As you may see, I want only those measuring points, whose associated device type has the id 1.
However, the previous association condition is not valid; and i have no clue how to correctly implement this.
Any help is appreciated.
Correct, that condition is invalid, for a number of reasons. First of all paths aren't supported at all, and even if they were, you already are in MeasuringPoints, respectively SpecialMeasuringPoints, so there would be no need to indicate that again.
While it would be possible to pass a condition like:
'DeviceTypes.id' => 1
That would require to alawys contain DeviceTypes when retrieving SpecialMeasuringPoints.
I would suggest to use a finder, that way you can easily include DeviceTypes and match against your required conditions. Something like:
$this->hasMany('SpecialMeasuringPoints',[
'className' => 'MeasuringPoints',
'foreignKey' => 'structure_id',
'finder' => 'specialMeasuringPoints'
]);
In your MeasuringPoints class define the appropriate finder, for example using matching(), and you should be good:
public function findSpecialMeasuringPoints(\Cake\ORM\Query $query) {
return $query
->matching('DeviceTypes', function (\Cake\ORM\Query $query) {
return $query
->where([
'DeviceTypes.id' => 1
]);
});
}
Similar could be done via the conditions option when passing a callback, which however is less DRY:
$this->hasMany('SpecialMeasuringPoints',[
'className' => 'MeasuringPoints',
'foreignKey' => 'structure_id',
'conditions' => function (
\Cake\Database\Expression\QueryExpression $exp,
\Cake\ORM\Query $query
) {
$query
->matching('DeviceTypes', function (\Cake\ORM\Query $query) {
return $query
->where([
'DeviceTypes.id' => 1
]);
return $exp;
}
]);
It should be noted that in both cases you need to be aware that such constructs are not compatible with cascading/dependent deletes, so do not try to unlink/delete via such associations!
See also
Cookbook > Database Access & ORM > Retrieving Data & Results Sets > Custom Finder Methods
Cookbook > Database Access & ORM > Retrieving Data & Results Sets > Filtering by Associated Data

CakePHP 3 - Users belongsToMany Users

I have a specific request, to build an association between users. This causes me confusion, how to reduce duplicate associations, query and results?
The starting point would look like this?
// UsersTable
$this->belongsToMany('Users', [
'through' => 'Connections',
]);
How to fetch all associations in one query, regardless of whether users key in "user_from" or "user_to" field?
How about using aliases?
Your users table:
class UsersTable extends Table
{
public function initialize(array $config)
{
$this->hasMany('ToConnections', [
'className' => 'Connections',
'foreignKey' => 'user_to'
]);
$this->hasMany('FromConnections', [
'className' => 'Connections',
'foreignKey' => 'user_from'
]);
}
}
And your connections table:
class ConnectionsTable extends Table
{
public function initialize(array $config)
{
$this->belongsTo('ToUsers', [
'className' => 'Users',
'foreignKey' => 'user_to'
]);
$this->belongsTo('FromUsers', [
'className' => 'Users',
'foreignKey' => 'user_from'
]);
}
}
You can then use contain() to load associated models as required.
$query = $conections->find()->contain([
'ToUsers',
'FromUsers'
]);
$recipients = TableRegistry::get('users');
$query = $recipients->find()->contain([
'ToConnections.FromUsers',
]);

Cakephp 3, Saving form data on association tables

Trying to set up an example on how to use join table with extra data I have the following set:
table students: id, name, [...]
table courses: id, title, [...]
join table courses_students: id, course_id, student_id, grade, hours_attended
The two base table's :
class StudentsTable extends Table {
public function initialize(array $config) {
$this->belongsToMany('Courses', [
'alias' => 'Courses',
'foreignKey' => 'student_id',
'targetForeignKey' => 'course_id',
'joinTable' => 'courses_students',
'through' => 'CoursesStudents',
]);
}
class CoursesTable extends Table {
public function initialize(array $config) {
$this->belongsToMany('Students', [
'alias' => 'Students',
'foreignKey' => 'course_id',
'targetForeignKey' => 'student_id',
'joinTable' => 'courses_students',
'through' => 'CoursesStudents',
]);
}
And the association table:
class CoursesStudentsTable extends Table {
public function initialize(array $config) {
$this->belongsTo('Courses', [
'alias' => 'Courses',
'foreignKey' => 'course_id'
]);
$this->belongsTo('Students', [
'alias' => 'Students',
'foreignKey' => 'student_id'
]);
}
Having some courses available in the table, I try to add and edit student records. Setting
[courses] => [_ids]
in the student record creates the records in both students table and the association table.
How should the post data array be formed in order to be able to store the grade and hours_attended fields in the association table when saving the student record?
You should configure your form field as the following assuming you are in the Courses form.
echo $this->Form->create($courses);
echo $this->Form->input("Courses.id");
echo $this->Form->input("Courses.title");
echo $this->Form->input("Courses.courses_students.$i.grade");
echo $this->Form->input("Courses.courses_students.$i.hours_attended");
The basic idea is that your forms should exactly follow how the data is formatted when retrieved in your controller.
This will then format your data correctly for you.
Then in your controller, you'll need to pass the associations to patch your entity.
$courses = $this->Courses->patchEntity($this->request->data(), ['associations' => ['CoursesStudents']]);
This will merge your associated request data in your entity, so you can save it.

Resources