CakePHP associations relations table - php

I need to build the associations like Group hasMany users and User belongToMany groups.
But I can't get the right result, it always use the wrong table instead groups_relations
My models:
class GroupsTable extends Table
{
public function initialize(array $config)
{
$this->setTable('groups');
$this->setDisplayField('title');
$this->setPrimaryKey('id');
$this->hasMany('Users', [
'joinTable' => 'groups_relations',
'foreignKey' => 'user_id',
]);
}
}
class UsersTable extends Table
{
public function initialize(array $config)
{
$this->table('user_users');
$this->belongsToMany('Groups', [
'joinTable' => 'groups_relations',
'foreignKey' => 'group_id',
]);
}
}
class GroupsRelationsTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('groups_relations');
$this->setDisplayField('group_id');
$this->setPrimaryKey('id');
$this->belongsTo('Groups', [
'foreignKey' => 'group_id',
'joinType' => 'INNER'
]);
$this->belongsToMany('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
}
}
And my table groups_relations:
id | group_id | user_id
I run query as:
$groupsWithUsers = $this->Groups->find('all', array(
'contain' => array('Users')
));
I can't understand how to tell to cake use my intermediary table and append reuslts to array.

joinTable is not a valid configuration key for a hasMany association. I think that you want to have Groups belongsToMany Users. Another clue about this is that hasMany is the "opposite" of belongsTo, while belongsToMany is it's own opposite. (That is, if A hasMany B, then B belongsTo A, but if A belongsToMany B, then B belongsToMany A.) Note that you will also want to change your GroupsRelations association with Users to belongsTo.
Is this code that was baked for you? Because it should know better. When I run into sticky association problems, I sometimes have Cake bake the model code for me, and then look at how the result differs from what I've written.

Rather than trying to use the relation the way you are doing why not just select from the relations table in the first place. This seems like the more Cake way of doing things. You can exclude the conditions clause if you want all data back.
$groupsWithUsers = $this->GroupsRelations->find('all', array(
'contain' => ['Users', 'Groups'],
'conditions' => ['Group.id' => $id]
)
);
After further looking into this I found something I have not used but seems to fit exactly what you need its a belongsToMany using an intermediary table. In your table file for the users you would add the following. A similar entry would be added to the group page.
$this->belongsToMany('Groups', [
'through' => 'GroupRelations',
]);

Related

Saving an Association with CakeDC Users

I am using CakeDC Users and as part of the registration I would like the registrant to choose some checkbox values that correspond to another Model in the application. I am able to get the form to load the choices just fine, but I am unable to get it to save to the join table even though I have added all of the associations and accessibilities where it would seem relevant. The form is displaying in a similar way to other areas where I am saving the same type of association with a different model.
TypesTable.php
{
public function initialize(array $config): void
{
//$this->addBehavior('Timestamp');
$this->setDisplayField('type');
$this->setPrimaryKey('id');
$this->belongsToMany('Words');
$this->belongsToMany('Users');
$this->belongsTo('Languages', [
'foreignKey' => 'language_id',
'joinType' => 'INNER',
]);
}```
UsersTable.php (in the plugin folders)
```class UsersTable extends Table
{
...
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('users');
$this->setDisplayField('username');
$this->setPrimaryKey('id');
$this->addBehavior('Timestamp');
$this->addBehavior('CakeDC/Users.Register');
$this->addBehavior('CakeDC/Users.Password');
$this->addBehavior('CakeDC/Users.Social');
$this->addBehavior('CakeDC/Users.LinkSocial');
$this->addBehavior('CakeDC/Users.AuthFinder');
$this->hasMany('SocialAccounts', [
'foreignKey' => 'user_id',
'className' => 'CakeDC/Users.SocialAccounts',
]);
$this->hasMany('Types', [
'foreignKey' => 'user_id', 'targetForeignKey' => 'type_id',
'joinTable' => 'types_users']);
}```
In order for a "belongsToMany" association to work in this instance, both *Table.php files must have the relations listed as belongsToMany. Don't forget to make the field accessible in the Entities as well.
One also needs to place the associated array in the patchEntity function in the RegisterBehavior.php file.
UserTable.php (in CakeDC users)
public function initialize(array $config): void
{
...
$this->belongsToMany('Types', [
'foreignKey' => 'user_id', 'targetForeignKey' => 'type_id',
'joinTable' => 'types_users']);
}
TypesTable.php (in app)
class TypesTable extends Table
{
public function initialize(array $config): void
{
....
$this->belongsToMany('Users');
$this->belongsTo('Languages', [
'foreignKey' => 'language_id',
'joinType' => 'INNER',
]);
}
RegisterBehavior.php
$user = $this->_table->patchEntity(
$user,
$data,
['validate' => $validator ?: $this->getRegisterValidators($options), 'associated' => ['Types']]
);

How to fetch associated belongsToMany entities with CakePHP3

I have Users and Courses table with belongsToMany relation. UserTable has
$this->belongsToMany('Courses', [
'foreignKey' => 'user_id',
'targetForeignKey' => 'course_id',
'joinTable' => 'courses_users'
]);
and CoursesTable has
$this->belongsToMany('Users', [
'foreignKey' => 'course_id',
'targetForeignKey' => 'user_id',
'joinTable' => 'courses_users'
]);
Now, I want to fetch courses with user_id. In my CoursesController, I tried
public function myCourses()
{
$id = $this->Auth->user('id');
$courses = $this->Courses->find('all',
['contain' => ['Users'],
'condition' => ['Courses.user_id' => $id]
]);
$this->set('courses', $courses);
}
when I debug($courses) with this code, I got '(help)' => 'This is a Query object, to get the results execute or iterate it.' message. I'm searching information and trying to do it for many hours but I can't make it. How can I fetch Courses datas with user_id? Thanks in advance.
If it's a has-and-belongs-to-many (HABTM) association with a join table of courses_users, you shouldn't even have a user_id field in your Courses table.
So now that we've determined you can't do what you were trying (Courses.user_id), we can look at what you thought you were trying:
$courses = $this->Courses->find('all',
['contain' => ['Users'],
//'condition' => ['Courses.user_id' => $id]
]);
This says "find all courses and any users that are associated with those courses".
But what you really WANT (I believe) is: "find all courses that belong to this specific user".
To do that, you'll want to use an matching() instead.
According to the CakePHP book:
A fairly common query case with associations is finding records
‘matching’ specific associated data. For example if you have ‘Articles
belongsToMany Tags’ you will probably want to find Articles that have
the CakePHP tag. This is extremely simple to do with the ORM in
CakePHP:
$query = $articles->find();
$query->matching('Tags', function ($q) {
return $q->where(['Tags.name' => 'CakePHP']);
});
So in your case, it would be something like this:
$query = $courses->find();
$query->matching('Users', function ($q) use ($id) {
return $q->where(['Users.id' => $id]);
});

Why am I getting a “Not Associated” error even though the HABTM association is in place?

I'm making some tournament scoring software for my bike polo league. The goal is to match up teams in such a way that everyone gets a chance to play every other team before repeat matches start.
In my Match Entity class, I have a function called createMatches that should find all teams and associated matches. There is a HABTM relationship between matches and teams, with a join table. This relationship works fine - I've selected teams (contain matches) and matches (contain teams) in several controller methods, saved associations through forms, and so on. But even so, in this Entity function, I get the error "Teams is not associated with Matches. Could this be caused by using Auto-Tables? ...." and so on. Can anyone tell me what's wrong?
Here's the method in question.
public function createMatches($tournamentId) {
$team = TableRegistry::get('Teams', ['contain' => ['Matches']]);
$teams = $team->find('all', ['conditions' =>
['tournament_id' => $tournamentId],
'contain' =>
['Matches' =>
['fields' =>
['Matches.id']
]]]);
return $teams;
}
Here's the init function from Match.php:
public function initialize(array $config)
{
parent::initialize($config);
$this->table('matches');
$this->displayField('id');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsTo('Rounds', [
'foreignKey' => 'round_id',
'joinType' => 'INNER',
'className' => 'SwissRounds.Rounds'
]);
$this->belongsToMany('Teams', [
'foreignKey' => 'match_id',
'targetForeignKey' => 'team_id',
'joinTable' => 'matches_teams',
'className' => 'SwissRounds.Teams'
]);
}
Here's the init function from Team.php:
public function initialize(array $config)
{
parent::initialize($config);
$this->table('teams');
$this->displayField('name');
$this->primaryKey('id');
$this->hasMany('Players', [
'foreignKey' => 'team_id',
'className' => 'SwissRounds.Players'
]);
$this->belongsToMany('Matches', [
'foreignKey' => 'team_id',
'targetForeignKey' => 'match_id',
'joinTable' => 'matches_teams',
'className' => 'SwissRounds.Matches'
]);
}
I don't believe I've touched either of those functions - they were both generated by cake bake.
Error message is quite exact, but not descriptive. It says that Teams is not associated with Matches.
Well, I tested this with minimal setup, with only difference, that I did not have my tables inside plugins. And no errors detected. So, I am pretty sure that CakePHP cant find your table classes inside SwissRounds -plugin for some reason.
If your tables are in a SwissRounds plugin, you should use plugin notation for the associations: $this->belongsToMany('SwissRounds.Teams', [... and $this->belongsToMany('SwissRounds.Matches', [...

Updating related row in another table cakephp 3

I'm trying to update a row in an associated table after a saving data in the current table. Say I have two tables, Listings and Queues. Queues has a foreign key of listing_id. So I've succesfully saved a new queue, which of course also includes the id of a listing, how can also update the status field of the Listing on the Listings table, say from from "paused" to "queued"?
See how my code looks like
$queue = $this->Queues->newEntity();
$queue->listing_id = $this->request->data['id'];
$queue->user_id = $this->Auth->user('id');
if ($this->Queues->save($queue)) {
$this->response->body("success");
$this->redirect(array('controller'=>'ListingsController','action' => 'changeStatus', $this->request->data['id'], "queued"));
}
Associations
Listings Table
$this->table('listings');
$this->displayField('name');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
Queues Table
$this->table('queues');
$this->displayField('id');
$this->primaryKey('id');
$this->belongsTo('Listings', [
'foreignKey' => 'listing_id',
'joinType' => 'INNER'
]);
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
This did the trick: $this->loadModel('Listings');
And then from there I could do a ->newEntity(), ->patchEntity() and/or ->save() on the Listings object using a WHERE clause on the query, like I would in the Listings controller.
E.g:
$this->loadModel('Listings');
$listing = $this->Listings->get($id);
$listing->status = "completed";
$this->Listings->save($listing);
Quite simple.

Saving HasMany Associations Data in CakePHP 3.x

I am having two tables. My primary table is Students. And my secondary table is Exams. I am trying to save both the tables using hasMany and belongsToMany Association. But It is saving data in Student table only, not in Exams. Can any one help me to resolve this problem.
Students Model :
class StudentsTable extends Table {
public function initialize(array $config) {
$this->addBehavior('Timestamp');
parent::initialize($config);
$this->table('students');
$this->primaryKey(['id']);
$this->hasMany('Exams', [
'className' => 'Exams',
'foreignKey' => 'student_id',
'dependent'=>'true',
'cascadeCallbacks'=>'true']);
}
}
Exams Model :
class ExamsTable extends Table {
public function initialize(array $config) {
parent::initialize($config);
$this->table('exams');
$this->primaryKey(['id']);
$this->belongsToMany('Students',[
'className'=>'Students',
'foreignKey' => 'subject_id',
'dependent'=>'true',
'cascadeCallbacks'=>'true']);
}
}
My school.ctp :
echo $this->Form->create();
echo $this->Form->input('name');
echo $this->Form->input('exams.subject', array(
'required'=>false,
'multiple' => 'checkbox',
'options' => array(
0 => 'Tamil',
1 => 'English',
2 => 'Maths')));
echo $this->Form->button(__('Save'));
echo $this->Form->end();
In my controller:
public function school() {
$this->loadModel('Students');
$this->loadModel('Exams');
$student = $this->Students->newEntity();
if ($this->request->is('post')) {
$this->request->data['exams']['subject'] =
implode(',',$this->request->data['exams']['subject']);
$student = $this->Students->patchEntity(
$student, $this->request->data, ['associated' => ['Exams']]
);
if ($this->Students->save($student)) {
$this->Flash->success(__('The user has been saved.'));
} else {
$this->Flash->error(__('Unable to add the user.'));
}
}
}
Patching BelongsToMany Associations
You need to make sure you are able to set exams. Set accessibleFields to allow you to patch associated data
$student = $this->Students->patchEntity(
$student, $this->request->data, [
'associated' => ['Exams'],
'accessibleFields' => ['exams' => true]
]
);
You can also do this with the $_accessible property in the entity.
I've never done hasMany to belongsToMany because i don't think it works that way (I mean no harm in my words.) But I'll try to explain. Your relationships should be both belongsToMany because exams will have many students and students will have many exams. So basically they're the same either way. What you need is another table to connect them which will be called students_exams or exams_students (i think its exams_students because E comes before S) because in cake if you name everything properly most of it happens automatically.
Assuming you know how patchEntity works, creating your $this->request->data properly will patch it automatically and save it in the correct table when you save it. If you have any more questions feel free to ask more. :)

Categories