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. :)
Related
I have read several Stack Overflow posts and the documentation pages about saving associated data in CakePHP 3, but I can't get my code to work. When creating a new Organisation, I also want to save the data of the new account (NewAccount) that belongs to that Organisation.
Below is a reproducible part of my code. The validation of the Organisations model are executed and if passed, the data is saved. The NewAccounts data does not get saved, and is not even being validated. I have already checked the naming conventions in all of these files, but I can't find anything that might be a problem anymore.
// Model/Table/OrganisationsTable.php
class OrganisationsTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
$this->hasOne('NewAccounts');
}
}
// Model/Table/NewAccountsTable.php
class NewAccountsTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
$this->belongsTo('Organisations', [
'foreignKey' => 'organisation_id'
]);
}
}
// Template/Organisations/admin_add.ctp
echo $this->Form->create($organisation);
echo $this->Form->control('new_account.email', [
'label' => __('Email address'),
'class' => 'form-control',
]);
echo $this->Form->control('new_account.name', [
'label' => __('Name'),
'class' => 'form-control',
]);
echo $this->Form->control('name', [
'label' => __('Organisation name'),
'div' => 'form-group',
'class' => 'form-control',
]);
// Controller/OrganisationsController.php
class OrganisationsController extends AppController
{
public function adminAdd() {
$organisation = $this->Organisations->newEntity();
if($this->request->is('post')) {
$this->Organisations->patchEntity($organisation, $this->request->getData(), [
'associated' => ['NewAccounts']
]);
if ($this->Organisations->save($organisation)) {
$id = $organisation->id;
debug("Success!");
}
else {
debug("Error");
}
}
$this->set(compact('organisation'));
}
}
CakePHP documentation I have referenced:
Saving Data
Form
Associations - Linking Tables Together
Validating Data
Entities
I forgot to make $organisation->new_account accessible:
// Model/Entity/Organisation.php
class Organisation extends Entity
{
protected $_accessible = [
// ...
'new_account' => true,
];
}
By doing this, the field is marked as to be safely assigned.
Entities: Mass Assignment (book) and EntityTrait::$_accessible (API docs)
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',
]);
Cake PHP Version: 3.1.5
I have a problem with saving a hasOne association, which works fine on one table but not with a second.
Ticketsand Cashdrafts are related to Cashpositions in a belongsTo relations. Cashpositions holds two FK for their id. So when a new cashposition is auto-created it holds either a ticket_id or a cashdraft_id. The second FK will be null.
The thing is, that the Tickets-Cashpositions saving is working fine, so everytime a ticket is created a related cashposition is created. But it is not working with Cashdrafts-Cashpositions. I don't understand why, because the setup and relations are exactly the same.
Here is the setup:
class CashpositionsTable extends Table
{
public function initialize(array $config)
{
$this->belongsTo('Tickets', [
'foreignKey' => 'ticket_id'
]);
$this->belongsTo('Cashdrafts', [
'foreignKey' => 'cashdraft_id'
]);
}
}
class TicketsTable extends Table
{
public function initialize(array $config)
{
$this->hasOne('Cashpositions', [
'foreignKey' => 'ticket_id'
]);
}
}
class CashdraftsTable extends Table
{
public function initialize(array $config)
{
$this->hasOne('Cashpositions', [
'foreignKey' => 'cashdraft_id'
]);
}
}
And then in the controllers add() functions:
class TicketsController extends AppController
{
public function add($memberId = null)
{
$ticket = $this->Tickets->newEntity();
if ($this->request->is('post')) {
$ticket = $this->Tickets->patchEntity($ticket, $this->request->data, [
// working fine: creates new cashposition for this ticket
'associated' => ['Cashpositions']
]);
if ($this->Tickets->save($ticket)) {
$this->Flash->success(__('ticket saved'));
return $this->redirect(['action' => 'view', $ticket->$id]);
} else {
$this->Flash->error(__('ticket could not be saved'));
}
}
class CashdraftsController extends AppController
{
public function add()
{
$cashdraft = $this->Cashdrafts->newEntity();
if ($this->request->is('post')) {
$cashdraft = $this->Cashdrafts->patchEntity($cashdraft, $this->request->data,[
// fail: no associated record created
'associated' => ['Cashpositions']
]);
if ($this->Cashdrafts->save($cashdraft)) {
$this->Flash->success(__('cashdraft saved.'));
return $this->redirect(['action' => 'view', $cashdraft->id]);
} else {
$this->Flash->error(__('cashdraft could not be saved'));
}
}
}
I debugged the $ticket and $cashdraft. But I cannot say I understand the output because:
The array for the ticket will show every related data but no cashposition, although a new record for it was created successfully...
And the array for the new cashdraft where the related cashposition is NOT created will look like this and say "null" for it:
object(App\Model\Entity\Cashdraft) {
'id' => (int) 10,
'amount' => (float) -7,
'created' => object(Cake\I18n\Time) {
'time' => '2015-12-13T20:03:54+0000',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\Time) {
'time' => '2015-12-13T20:03:54+0000',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'cashposition' => null, // this part not even showing up for a "ticket" in the debug
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[repository]' => 'Cashdrafts'
}
In the SQL in DebugKit I can see that for the ticket an INSERT into the related cashpositions table is done. But for cashdrafts there is no INSERT done into the related table. So obviously Cake does not even try to create the associated record.
I'm really out of ideas now! In the database itself both FKs are set up exactly the same, names are correct etc.
Does anybody have an idea what the problem could be or where I could further search for the cause of the second association not working? Thanks!
Ok, so after searching for a million hours I finally realized, that the problem was not with the Model or Controller like I thought. It was (just) the view and the request data not being complete.
Somehow I thought Cake would magically add the entity for the association if non exists even if there is no input for it ;)
In the tickets table for which the saving worked I had an empty input field for a column in Cashpositions that does not even exist anymore and I just hadn't deleted it yet, but it did the trick (don't ask me why).
To fix it now I just put in a hidden input field for the association cashposition.ticket_id and cashposition.cashdraft_id in the add.ctp view for both tables that stays empty. Now the request data contains the array for the association and auto creates a new cashposition with the matching FK every time a new ticket or cashdraft is added.
<!-- Cashdrafts/add.ctp -->
<?php echo $this->Form->input(
'cashposition.cashdraft_id', [
'label' => false,
'type' => 'hidden'
]) ?>
Since I'm just a beginner with this I don't know if this is the best way to go, but it works (finally...)
I am sorry if the title is a bit confusing, I just don't know how this is properly called. I have a table structure like this for my CakePHP project.
users id, name, surname, userrecords
userrecords id, user_id, records_id
records id, description
I understand that to access the userrecords middle table in my users view I have to do something like
$user['userrecords']['id'];
How can I neatly access description in records table through a users view?
You didn't specify whether you're using CakePHP 2.x or 3.x, so I provided solutions for both.
The relationship you are referring to is called a "Has And Belongs To Many" relationship. Since both of your model are associated to a linking table (userrecords), you can freely associate as many records to as many users as you want.
First, I would consider renaming your 'userrecords' table to 'users_records' to play nicely with CakePHP.
First, define your relationship within your Users model:
// Using CakePHP 2.x:
class User extends AppModel {
public $actsAs = array('Containable'); // Instantiate Containable behavior
public $hasAndBelongsToMany = array(
'Record' =>
array(
'className' => 'Record',
'joinTable' => 'users_records',
'foreignKey' => 'user_id',
'associationForeignKey' => 'record_id'
)
);
}
// Using CakePHP 3.x:
class UsersTable extends Table
{
public function initialize (array $config)
{
$this->belongsToMany('Records', [
'joinTable' => 'users_records' // Defines our linking table
]);
}
}
Now, we must define our relationship within our Records model:
// Using CakePHP 2.x:
class Record extends AppModel {
public $actsAs = array('Containable'); // Instantiate Containable behavior
public $hasAndBelongsToMany = array(
'User' =>
array(
'className' => 'User',
'joinTable' => 'users_records',
'foreignKey' => 'record_id',
'associationForeignKey' => 'user_id'
)
);
}
// Using CakePHP 3.x:
class RecordsTable extends Table
{
public function initialize (array $config)
{
$this->belongsToMany('Users', [
'joinTable' => 'users_records' // Defines our linking table
]);
}
}
Now, we can access the associated records freely from each model using the ORM's contain method:
// Using CakePHP 2.x:
// Getting 'User' and associated 'Record' models in Controller:
$this->loadModel('User');
$this->User->find('all', array('contain' => 'Record'));
// Getting 'Record' and associated 'User' models in Controller:
$this->loadModel('Record');
$this->Record->find('all', array('contain' => 'User'));
// Using CakePHP 3.x:
// Getting 'User' and associated 'Record' models:
$users_table = TableRegistry::get('Users');
$users = $users_table->find()->contain('Records')->all();
// Getting 'Record' and associated 'User' models:
$records_table = TableRegistry::get('Records');
$records = $records_table->find()->contain('Users')->all();
Read the Cookbook, it will make your life a million times easier:
CakePHP 2.x Containable Behavior
CakePHP 2.x Has And Belongs To Many Relationship
CakePHP 3.x belongsToMany Relationship
CakePHP 3.x Retrieving Associated Data
CakePHP 3.x Eager Loading Associations
Read this,this may be helpful for you
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html
Because your table structure as below :
User -> UserRecord -> Record
So that you can only get [Record] via [UserRecord]
You should set recursive property in find command.
Please refer for more information about recursive at this link : what is the meaning of recursive in cakephp?
I hope this answer doesn't misunderstand your question.
Here is my model relationship...
class User extends Eloquent {
public function loginLog(){
return $this->hasMany('LoginLog');
}
}
class LoginLog extends Eloquent {
public function user(){
return $this->belongsTo('User');
}
}
When I insert data into the login_logs table in my database all the data is input correctly but it does not insert the id of the user into user_id (laravel expects this).
Here is how I am inserting into login_logs.
$user->loginLog()->insert(array(
'user_id' => $user->id, //I could put it here, but then what is the point in a relationship?
'email' => $user->email,
'ip_address' => Request::getClientIp(),
'country_code' => $country_code,
'status' => $status,
'created_at' => Helper::dateTimeNow()
));
You have to attach the user.
Its here in the docs
http://laravel.com/docs/eloquent#inserting-related-models
Update:
On rereading your question I think you want to find the user by their id first as you are doing $user->loginLog()->insert not $loginLog->insert
Try chaining it so:
$user::find($theIDYouWant)->loginLog()->insert