Cakephp 3.x linking same table with join table - php

This is my model
messages table:
message_id int,
body varchar(50)
messages_replies table:
original_id int, reply_id int
As you can see I have a table of messages, whenever a user writes a comment it's saved here. But when a user replies a previous comment I want to save the new comment in the message table and which comment is replying in the message_replies table.
My problem is that I dont know how to link the tables in MessageTable.php because it references the same table.
This is what I've tried in MessagesTable.php :
$this->belongsToMany('Replies', [
'className' => 'Messages',
'foreignKey' => 'message_id',
'targetForeignKey' => 'original_id',
'joinTable' => 'messages_replies'
]);
And it fails in MessagesController.php
public function index()
{
$messages = $this->Messages->find('all',['contain' => ['Replies']]);
$this->set([
'messages' => $messages,
'_serialize' => ['messages']
]);
}

Related

Enabling null on many FKs to same associated table

I have multiple fields setup as FK to the same table. The FK can also be NULL.
I keep getting this error:
ExistsIn rule for 'late_agreement_exception_outcome_recommendation_id' is invalid. 'ExceptionOutcomes' is not associated with 'App\Model\Table\ExceptionsTable'.
Database structure:
exceptions
id,
request_id,
late_agreement_exception_outcome_recommendation_id (FK, exception_outcomes->id)
late_agreement_exception_outcome_id (FK, exception_outcomes->id),
...
exception_outcomes
id,
name
...
Column definition (ie. late_agreement_exception_outcome_recommendation_id):
Column relationship (ie. late_agreement_exception_outcome_recommendation_id):
ExceptionTable:
FK setup to ExceptionOutcomes
$this->belongsTo('LateAgreementExceptionOutcomeRecommendations', [
'class' => 'ExceptionOutcomes',
'foreignKey' => 'late_agreement_exception_outcome_recommendation_id',
'joinType' => 'INNER',
]);
Edited rules attempting to enable entry of a null value for the field value:
$rules->add(
function ($entity, $options) {
$rule = new ExistsIn('late_agreement_exception_outcome_recommendation_id', 'ExceptionOutcomes');
return $entity->late_agreement_exception_outcome_recommendation_id === NULL || $rule($entity, $options);
},
['errorField' => 'late_agreement_exception_outcome_recommendation_id']
);
Update #1
I changed the association name like so:
$rules->add(
function ($entity, $options) {
$rule = new ExistsIn('late_agreement_exception_outcome_recommendation_id', 'LateAgreementExceptionOutcomeRecommendations');
return $entity->late_agreement_exception_outcome_recommendation_id === NULL || $rule($entity, $options);
},
['errorField' => 'late_agreement_exception_outcome_recommendation_id']
);
And got the following issue simply saving the data:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'sdi_ips2.late_agreement_exception_outcome_recommendations' doesn't exist
Previously, I could save the column when providing a value. However, trying to revert to NULL would cause an issue.
Update #2
try
{
$this->Requests->save($request);
}
catch(Cake\Database\Exception\DatabaseException $e)
{
debug("here!");
exit;
}
Update #3
Here's what I see in the SQL log:
Generated Models
The following Table objects used Cake\ORM\Table instead of a concrete class:
LateAgreementExceptionOutcomeRecommendations
Solution:
Note the className attribute in belongsTo.
$this->belongsTo('LateAgreementExceptionOutcomeRecommendations', [
'className' => 'ExceptionOutcomes',
'foreignKey' => 'late_agreement_exception_outcome_recommendation_id',
'joinType' => 'INNER',
]);

Laravel inserting by relationship to composite key table

I have these tables currently:
User table
id (primary key), name, email
User Model
protected $fillable = ['name', 'email'];
protected $visible = ['id','name','email'];
//Relationship
public function customAttributes()
{
return $this->hasMany('App\Models\UserAttribute');
}
UserAttribute Table
user_id, attribute_id, value //user_id and attribute_id is a composite key, both foreignkeys acting as primary keys establishing an unique combination
UserAttribute Model
protected $fillable = ['user_id', 'attribute_id','value'];
protected $visible = ['user_id', 'attribute_id','value'];
I'll use the following example to explain the issue:
$user = $this->user->create(['name' => 'admin', 'email' => 'admin#admin.com']);
//This works
$user->customAttributes()->save(new \App\Models\UserAttribute(['user_id' => $user->id, 'attribute_id' => 1, 'value' => 'Just a custom1']));
//This does not work
$user->customAttributes()->create([new \App\Models\UserAttribute(['user_id' => $user->id, 'attribute_id' => 1, 'value' => 'Just a custom1'])]);
I could just repeat the save for every custom that I want since it works, but I'm trying to figure out why create doesn't work.
The error I'm getting when I use create is (and yes, I've checked the record exists in the table that isn't listed here):
Cannot add or update a child row: a foreign key constraint fails (`testdatabase`.`user_attributes`,
CONSTRAINT `user_attributes_attribute_id_foreign` FOREIGN KEY (`attribute_id`) REFERENCES `attributes` (`id`))
This is the query it's trying to execute:
insert into `user_attributes` (`user_id`) values (1)
I'm just curious at why this doesn't work with create, I'm not sure if it's something related to this specific scenario (create to a composite key table by relationship). It's somewhat ignoring the value and attribute_id field in the query that is executing
try this:
$user->customAttributes()->create(['user_id' => $user->id, 'attribute_id' => 1, 'value' => 'Just a custom1']);
customAttributes() already returns you instance of UserAttribute model, you don't need to enject that dependency when you use create() method via that relation
your query should be like below;
$user->customAttributes()->insert([
[
'user_id' => $user->id,
'attribute_id' => 1,
'value' => 'Just a custom1'
],
[
'user_id' => $user->id,
'attribute_id' => 2,
'value' => 'Just a custom2'
],
]);

cake php 3.x, model join 3 table

Im trying to join 3 tables in cake php.
I'll shorten the table I have to make it simple.
table_users(
id int primary key,
username varchar(10),
password varchar(10),
)
table_details(
id int primary key,
user_id int, //fk of table_users.id
//more fields here
)
table_ot(
id int primary key,
user_id int, //fk of table_users.id
//more fields here
)
I plan to join the table_details and table_ot by using there user_id.
In the model that was generated by cake bake, the table_details is joining table_users and table_ot is table_users.
But table_details is NOT joining table_ot.
This is the content of table_details and table_ot.
$this->belongsTo('table_users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
also tried this one in the controller still does not work.
$Overtime = $this->table_ot->find()->all(array('joins' =>
array(
'table' => 'table_table_details',
'alias' => 'table_table_details',
'type' => 'full',
'foreignKey' => false,
'conditions'=> array('table_ot.user_id = table_table_details.user_id')
)
));
Any advise.. Help please
As you pointed in your question, you have already tables associations set up. So you can write your query like this:
$this->table_ot->find("all",[
"contain" => [
"table_users" => ["table_details"]
]
]);
After executing this query with, for example, toArray(), you can access your table_details record associated with table_ot like this:
$detailId = $results[0]->table_users->table_details->id;
As an alternative, I would suggest you to try joining these two tables like so:
//in initialize() method of your ot_table:
$this->hasOne("table_details")
->setForeignKey("user_id")
->setBindingKey("user_id");
All available options for each type of associations are listed here: https://book.cakephp.org/3.0/en/orm/associations.html
You have to add another field in table_ot to join with table_details according to cake convention. Because you have a foreign just to join with user table.
table_ot(
id int primary key,
user_id int, //fk of table_users.id
details_id int, //fk of table_details.id
//more fields here
)
Then add this code in table of table_ot
$this->belongsTo('table_details', [
'foreignKey' => 'details_id',
'joinType' => 'INNER'
]);

updating related tables in cake

I have this:
$this->request->data['Person']['person_id'] = $this->Session->read('insertedPersonID');
$savedPerson = $this->Person->saveAll($this->request->data, array('validate'=>'first'));
which updates the selected person row fine however its related tables like PersonColor and PersonParts are not updating, instead inserting new rows.
I thought cake automatically updates the related tables as well as long as the id of the main table which is the foreign key for the other two tables are provided since doing this:
$savedPerson = $this->Person->saveAll($this->request->data, array('validate'=>'first'));
inserts to the Person table and the other two related two tables fine.
How do I make it update the other two tables as well?
Edit:
For the model relations:
Person Model:
public $hasMany = array(
'PersonParts' => array(
'className' => 'Part',
'foreignKey' => 'part_person_id'
),
'PersonColors' => array(
'className' => 'Color',
'foreignKey' => 'color_person_id'
)
);
Part Model:
public $belongsTo = array(
'PartPerson' => array(
'className' => 'Person',
'foreignKey' => 'part_person_id'
)
);
Color Model:
public $belongsTo = array(
'ColorPerson' => array(
'className' => 'Person',
'foreignKey' => 'color_person_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
edit 2
var_dump of $this->request->data
array(3){
["Person"]=>array(4){
["person_user_id"]=>string(1)"3"
["person_name"]=>string(9)"Britney"
["person_category_id"]=>string(2)"16"
["visibility"]=>string(1)"1"
["person_id"]=>string(1)"71"
}
["PersonParts"]=>array(1){
[0]=>array(3){
["part_name"]=>string(4)"hands"
["quantity"]=>string(1)"2"
["part_part_type_id"]=>string(1)"1"
}
}
["PersonColors"]=>array(2){
[0]=>array(4){
["color_name"]=>string(3)"blue"
["test_field1"]=>string(1)"8"
["test_field2"]=>string(1)"9"
["position"]=>int(1)
}
[1]=>array(2){
["color_name"]=>string(5)"red"
["position"]=>int(2)
}
}
}
Note: This var_dump is only showing ["person_id"]=>string(1)"71" under Person array as the added field to make cake do an update, not insert... person_id is not showing under the PersonParts and PersonColors here since it's not working that way. What should I pass or How should I do an update on the other 2 related tables?
To make Cake do an update on the related tables, you do need to pass in the id of the related record, otherwise Cake won't know which record to update.
You can make this a hidden field in your form, which is how I do things.
The var dump for PersonParts should look like this (note the extra 'part_id' field):
["PersonParts"]=>array(1){
[0]=>array(3){
["part_id"]=>string(1)"7"
["part_name"]=>string(4)"hands"
["quantity"]=>string(1)"2"
["part_part_type_id"]=>string(1)"1"
}
}
If you don't pass the id, then (from memory) Cake will delete the existing related records, and add new ones.

looking up table-join value from database with cakephp

I'm trying to access a user's name in a view on cakephp. I'm given a userid and I need to use that to look up their username which is stored in a different table.
The table that I'm retrieving displaying is messages and is as follows
id | title | message | from_user | to_user | date
The users table which contains the username
id | username | email | password
Anyway how can I look up the username of from_user in the message? I'm still getting used to cake so I don't know how to make a "join" statement without actually writing a query.
Should I send the username from the action in the controller or is it OK to do this from the view?
edit- I got it to work
Here is my index function now
public function index() {
$options['joins'] = array(
array('table' => 'Users',
'type' => 'inner',
'fields' => array('user.username'),
'conditions' => array(
'Message.from_user = User.id',
'alias' => 'user_id')
)
);
$message = $this->Message->find('all', $options);
$this->set('messages', $message);
}
I added this to my Message controller and the username now displays, but ONLY for the first message. All of the others it's blank.
public $belongsTo = array('User');
You have to redefine your relationship in Message model :
public $belongsTo = array(
'Sender' => array(
'className' => 'User',
'foreignKey' => 'from_user'
),
'Recipient' => array(
'className' => 'User',
'foreignKey' => 'to_user'
)
);
You can use containable behavior to retrieve that record. What I do is add the behavior to AppModel and set recursive to -1 in AppModel.
Then your read() call would turn into
public function view($id) {
$message = $this->Message->find('first', array(
'conditions' => array('Message.id' => $id),
'contain' => array(
'FromUser' => array('fields' => array('FromUser.username')),
),
));
$this->set('message', $message'); // or just $this->set(compact('message'));
}
In your view:
$message['FromUser']['username'];
You may also do a debug($message); either in the view or controller to see what exactly is inside the variable and the structure of it.
Assuming you have your relations set up correctly, this should retrieve the FromUser.username field associated with the message record.
More on containable: http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html (2.x) and http://book.cakephp.org/view/1323/Containable (1.3)

Categories