cant save associated new records in cakephp3 - cakephp

To save new records in associated data with a many to 1 and a many to many as below what do I do ? I cant get it to save my data .
I get an error on trying to save a to the guardian without an id but it is a new record and I couldn't this working
controller
if ($this->request->is(['patch', 'post', 'put'])) {
$mydata=$this->request->data;
$guardian =$this->Guardians->newEntity();
$guardian->guardian_first_name = $mydata['Guardians']['guardian_first_name'];
$guardian->guardian_last_name = $mydata['Guardians']['guardian_last_name'];
$guardian->guardian_mobile = $mydata['Guardians']['guardian_mobile'];
$guardian->guardian_email = $mydata['Guardians']['guardian_email'];
$student = $this->Students->newEntity( $mydata,['associated' => ['AvailabilityForStudents','Subjects','Guardians']] );
$student->guardians = [ $guardian];
$val= $this->Students->save($student,[
'validate' => false ]);
public function initialize(array $config)
{
parent::initialize($config);
$this->table('students');
$this->displayField('id');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'LEFT'
]);
$this->belongsTo('Guardians', [
'foreignKey' => 'guardian_id',
'joinType' => 'LEFT'
]);
$this->hasMany('AvailabilityForStudents', [
'foreignKey' => 'student_id'
]);
$this->belongsToMany('Subjects', [
'foreignKey' => 'student_id',
'targetForeignKey' => 'subject_id',
'joinTable' => 'students_subjects'
]);
}
http://book.cakephp.org/3.0/en/orm/saving-data.html#saving-with-associations
update-returned data
[
'first_name' => 'a1',
'last_name' => 'a2',
'student_mobile' => '',
'school' => '9',
'class_year' => '9',
'tutor_gender_preference' => 'No Preference',
'doing_assessment' => '0',
'tutoring_typest_id' => '1',
'address_street' => '1 gghh',
'address_suburb' => 'seagg',
'address_postcode' => '3111',
'address_state' => 'Vic',
'address_lat' => '',
'address_long' => '',
'school_address_street' => '',
'school_address_suburb' => '',
'school_address_postcode' => '',
'school_address_lat' => '',
'school_address_long' => '',
'Guardians' => [
'guardian_first_name' => 'aa',
'guardian_last_name' => 'aa',
'guardian_phone' => '03534535',
'guardian_mobile' => '0000000000',
'guardian_email' => 'a#ww.com'
],
'subjects' => [
'_ids' => [
(int) 0 => '1'
]
],
'AvailabilityForStudents' => [
'Monday' => [
'weekday' => 'Monday',
'not_available' => '0',
'start_time' => '9:00',
'end_time' => '10:00'
],
'Tuesday' => [
'weekday' => 'Tuesday',
'not_available' => '0',
'start_time' => '9:00',
'end_time' => '10:00'
],
'Wednesday' => [
'weekday' => 'Wednesday',
'not_available' => '0',
'start_time' => '9:00',
'end_time' => '10:00'
],
'Thursday' => [
'weekday' => 'Thursday',
'not_available' => '0',
'start_time' => '9:00',
'end_time' => '10:00'
],
'Friday' => [
'weekday' => 'Friday',
'not_available' => '0',
'start_time' => '9:00',
'end_time' => '10:00'
],
'Saturday' => [
'weekday' => 'Saturday',
'not_available' => '0',
'start_time' => '9:00',
'end_time' => '10:00'
],
'Sunday' => [
'weekday' => 'Sunday',
'not_available' => '0',
'start_time' => '9:00',
'end_time' => '10:00'
]
]
]
patched data
object(App\Model\Entity\Student) {
'first_name' => 'a1',
'last_name' => 'a2',
'student_mobile' => '',
'school' => '9',
'class_year' => (int) 9,
'tutor_gender_preference' => 'No Preference',
'doing_assessment' => (int) 0,
'tutoring_typest_id' => (int) 1,
'address_street' => '1 gghh',
'address_suburb' => 'seagg',
'address_postcode' => '3111',
'address_state' => 'Vic',
'address_lat' => '',
'address_long' => '',
'school_address_street' => '',
'school_address_suburb' => '',
'school_address_postcode' => '',
'school_address_lat' => '',
'school_address_long' => '',
'Guardians' => [
'guardian_first_name' => 'aa',
'guardian_last_name' => 'aa',
'guardian_phone' => '03534535',
'guardian_mobile' => '0000000000',
'guardian_email' => 'a#ww.com'
],
'subjects' => [
(int) 0 => object(App\Model\Entity\Subject) {
'id' => (int) 1,
'name' => 'English: Prep- Grade 3',
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Subjects'
}
],
'AvailabilityForStudents' => [
'Monday' => [
'weekday' => 'Monday',
'not_available' => '0',
'start_time' => '9:00',
'end_time' => '10:00'
],
'Tuesday' => [
'weekday' => 'Tuesday',
'not_available' => '0',
'start_time' => '9:00',
'end_time' => '10:00'
],
'Wednesday' => [
'weekday' => 'Wednesday',
'not_available' => '0',
'start_time' => '9:00',
'end_time' => '10:00'
],
'Thursday' => [
'weekday' => 'Thursday',
'not_available' => '0',
'start_time' => '9:00',
'end_time' => '10:00'
],
'Friday' => [
'weekday' => 'Friday',
'not_available' => '0',
'start_time' => '9:00',
'end_time' => '10:00'
],
'Saturday' => [
'weekday' => 'Saturday',
'not_available' => '0',
'start_time' => '9:00',
'end_time' => '10:00'
],
'Sunday' => [
'weekday' => 'Sunday',
'not_available' => '0',
'start_time' => '9:00',
'end_time' => '10:00'
]
],
'student_enq' => false,
'user_id' => (int) 7,
'student_inactive' => false,
'acknowledge' => (int) 0,
'is_email_notify' => (int) 0,
'days_joined' => (int) 0,
'satisfaction_rating' => (int) 0,
'student_unallocated' => false,
'admin_satisfaction_rating' => (int) 0,
'another_assessment' => (int) 0,
'numeracy_active' => (int) 0,
'is_waiting_assessment' => (int) 0,
'group_status' => (int) 0,
'address_billing' => '4',
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'first_name' => true,
'last_name' => true,
'student_mobile' => true,
'school' => true,
'class_year' => true,
'tutor_gender_preference' => true,
'doing_assessment' => true,
'tutoring_typest_id' => true,
'address_street' => true,
'address_suburb' => true,
'address_postcode' => true,
'address_state' => true,
'address_lat' => true,
'address_long' => true,
'school_address_street' => true,
'school_address_suburb' => true,
'school_address_postcode' => true,
'school_address_lat' => true,
'school_address_long' => true,
'Guardians' => true,
'subjects' => true,
'AvailabilityForStudents' => true,
'student_enq' => true,
'user_id' => true,
'student_inactive' => true,
'acknowledge' => true,
'is_email_notify' => true,
'days_joined' => true,
'satisfaction_rating' => true,
'student_unallocated' => true,
'admin_satisfaction_rating' => true,
'another_assessment' => true,
'numeracy_active' => true,
'is_waiting_assessment' => true,
'group_status' => true,
'address_billing' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Students'
}

I was also facing the similar problem and after all R&D, I tried this code and it worked. Hope you'll also get benefited. Here the code goes:
Let's say you have a controller "UsersController" associated with "RolesController" with "id". Then add the below line of code in your action under UsersController:
$this->loadModel('Roles');
$roles = $this->Roles->find('list');
$roles = $roles->toArray();
$this->set(compact('roles'));
Now make the following changes in your action's view file in Template folder:
echo $this->Form->select('role_id', $roles);
Here, we have assigned that variable to our form field.
With these changes, you should be able to do your task. If still face any issue, feel free to ask in comments.
KEEP CODING! HAVE FUN! :)

I suggest splitting the entity creation.
First, create a new entity with $student = $this->Students->newEntity();
And then patch you data to the entity like :
$student = $this->Student->patchEntity($mydata,['associated' => ['AvailabilityForStudents','Subjects','Guardians']]);
Maybe the patchEntity() method understand the association in a better manner.
Hope this solves your problem!

Related

CakePHP 4 multiple checkboxes

I want to populate a checkbox group with the array JournalEntry[strategy][strategies_conditions]
I then want to have the checkboxes selected that are contained in JournalEntry[journal_entries_strategy_conditions]
I have the checkboxes displaying but not linked correctly to the correct fields of name and id
This is the code I have in the edit.php
<?php
echo $this->Form->control('journal_id', ['options' => $journals]);
echo $this->Form->control('ticket_number');
echo $this->Form->control('strategy_id', ['options' => $strategies, 'empty' => true, 'onChange' => 'getComboA(this)']);
debug($journalEntry);
?>
<div id="stategy-condition">
<?php echo $this->Form->control('journal_entries_strategy_conditions.strategies_condition_id',
[
'type' => 'select',
'multiple' => 'checkbox',
'options' => $journalEntry[strategy][strategies_conditions],
'selected' => $journalEntry[journal_entries_strategy_conditions]
]); ?>
</div>
With this I get this output, as you can see it is using the object for the <label> and <input value="1" is set to the id value
<div class="input select">
<label for="strategy-id">Strategy</label>
<input type="hidden" name="strategy_id" value="">
<div class="checkbox">
<label for="strategy-id-0">
<input type="checkbox" name="strategy_id[]" value="0" id="strategy-id-0">{
"id": 1,
"strategy_id": 5,
"name": "zxcasd",
"level": "0",
"created": "2020-08-01T21:21:11+00:00",
"modified": "2020-08-01T21:21:11+00:00"
}</label>
</div>
<div class="checkbox">
<label for="strategy-id-1">
<input type="checkbox" name="strategy_id[]" value="1" id="strategy-id-1">{
"id": 2,
"strategy_id": 5,
"name": "zxcasd",
"level": "0",
"created": "2020-08-01T21:21:11+00:00",
"modified": "2020-08-01T21:21:11+00:00"
}</label>
</div>
<div class="checkbox">
<label for="strategy-id-2">
<input type="checkbox" name="strategy_id[]" value="2" id="strategy-id-2">{
"id": 3,
"strategy_id": 5,
"name": "zxcad",
"level": "0",
"created": "2020-08-01T21:21:11+00:00",
"modified": "2020-08-01T21:21:11+00:00"
}</label></div></div>
Here is the data:
object(App\Model\Entity\JournalEntry) {
'id' => (int) 20,
'journal_id' => (int) 1,
'ticket_number' => 'sdfsdfsdf',
'strategy_id' => (int) 5,
'timeframe' => '',
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2020-08-25 20:51:42.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\FrozenTime) {
'time' => '2020-08-25 20:51:42.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'journal_entries_strategy_conditions' => [
(int) 0 => object(App\Model\Entity\JournalEntriesStrategyCondition) {
'id' => (int) 8,
'journal_entry_id' => (int) 20,
'strategies_condition_id' => (int) 1,
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2020-08-25 20:51:42.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\FrozenTime) {
'time' => '2020-08-25 20:51:42.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
}
],
'strategy' => object(App\Model\Entity\Strategy) {
'id' => (int) 5,
'user_id' => (int) 1,
'name' => 'zxcasd',
'description' => 'zxcasd',
'one_hundred_trades' => (int) 0,
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2020-08-01 21:21:11.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\FrozenTime) {
'time' => '2020-08-01 21:21:11.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'strategies_conditions' => [
(int) 0 => object(App\Model\Entity\StrategiesCondition) {
'id' => (int) 1,
'strategy_id' => (int) 5,
'name' => 'zxcasd',
'level' => '0',
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2020-08-01 21:21:11.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\FrozenTime) {
'time' => '2020-08-01 21:21:11.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'[new]' => false,
'[accessible]' => [
'strategy_id' => true,
'name' => true,
'level' => true,
'created' => true,
'modified' => true,
'strategy' => true
]
},
(int) 1 => object(App\Model\Entity\StrategiesCondition) {
'id' => (int) 2,
'strategy_id' => (int) 5,
'name' => 'zxcasd',
'level' => '0',
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2020-08-01 21:21:11.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\FrozenTime) {
'time' => '2020-08-01 21:21:11.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
}
},
(int) 2 => object(App\Model\Entity\StrategiesCondition) {
'id' => (int) 3,
'strategy_id' => (int) 5,
'name' => 'zxcad',
'level' => '0',
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2020-08-01 21:21:11.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\FrozenTime) {
'time' => '2020-08-01 21:21:11.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
}
],
},
'[new]' => false,
This the request date for edit.php
[
'journal_id' => '1',
'ticket_number' => 'test con',
'pair' => '',
'buy_sell' => '',
'personal_notes' => '',
'entry_date_time' => '',
'entry_price' => '',
'strategy_id' => '5',
'journal_entries_strategy_conditions' => [
'strategies_condition_id' => [
(int) 0 => '1'
]
],
'timeframe' => '',
'position_size' => '',
'sl' => '',
'tp' => '',
'market_conditions' => '',
'entry_toughts' => '',
'close_date_time' => '',
'close_price' => '',
'profit' => '',
'fees' => '',
'high_price' => '',
'low_price' => '',
'exit_thoughts' => '',
'feeling_before' => '',
'feeling_after' => ''
]
add.php
[
'journal_id' => '1',
'ticket_number' => 'sdfdsf',
'pair' => '',
'buy_sell' => '',
'personal_notes' => '',
'entry_date_time' => '',
'entry_price' => '',
'strategy_id' => '5',
'journal_entries_strategy_conditions' => [
(int) 0 => [
'strategies_condition_id' => '1'
],
(int) 1 => [
'strategies_condition_id' => '2'
]
],
'timeframe' => '',
'position_size' => '',
'sl' => '',
'tp' => '',
'market_conditions' => '',
'entry_toughts' => '',
'close_date_time' => '',
'close_price' => '',
'profit' => '',
'fees' => '',
'high_price' => '',
'low_price' => '',
'exit_thoughts' => '',
'feeling_before' => '',
'feeling_after' => ''
]
There is no selected option, the option to provide values for selection is named val or value, and like the options option, it expects a flat key => value array (there's an exception if you want to provide attributes for <option> elements, in that case you can use nested arrays with text and value keys).
For options the array key will be use for the <option> element's value attribute, and the array value will be used for the content of the element.
For value the array value will be used for matching against the options array's keys, ie AFAICT you'd need an array of strategies_condition_id.
Often times values for select controls are being prepared using the list finder, you'll see that in baked controllers. However since you have nested associations, that's not necessarily feasible, and you're possibly better of converting the data after the fact, which can easily be done using collections.
For example in your view template:
<div id="stategy-condition">
<?php echo $this->Form->control('journal_entries_strategy_conditions', [
'type' => 'select',
'multiple' => 'checkbox',
'options' =>
collection($journalEntry['strategy']['strategies_conditions'])
->combine('id', 'name')
->toArray(),
'value' =>
collection($journalEntry['journal_entries_strategy_conditions'])
->extract('strategies_condition_id')
->toArray()
]); ?>
</div>
The combine() call should build a collection like:
[
1 => 'zxcasd',
2 => 'zxcasd',
3 => 'zxcad',
]
and the extract() should result in a collection containing this:
[
0 => 1
]
which should result in the first checkbox being checked.
Cookbook > Collections > combine()
Cookbook > Collections > extract()
As far as your control name problem goes, there are many ways to solve this, you could for example use a custom template where you hardcode the input name:
echo $this->Form->control('journal_entries_strategy_conditions', [
'type' => 'select',
// ...
'templates' => [
'checkbox' =>
'<input
type="checkbox"
name="journal_entries_strategy_conditions[][strategies_condition_id]"
value="{{value}}"
{{attrs}}>',
]
]);
Cookbook > Views > Helpers > Form > Options for Control
Cookbook > Views > Helpers > Form > Customizing the Templates FormHelper Uses
Or transform the data before marshalling (patching), using the beforeMarshal callback in your JournalEntriesTable class:
public function beforeMarshal(
\Cake\Event\EventInterface $event,
\ArrayObject $data,
\ArrayObject $options
) {
if (isset($data['journal_entries_strategy_conditions']['strategies_condition_id']) {
$ids = $data['journal_entries_strategy_conditions']['strategies_condition_id'];
$conditions = [];
foreach ($ids as $id) {
$conditions[] = [
'strategies_condition_id' => $id,
];
}
$data['journal_entries_strategy_conditions'] = $conditions;
}
}
This would transform the data from:
'journal_entries_strategy_conditions' => [
'strategies_condition_id' => [
'1',
'2'
],
],
to:
'journal_entries_strategy_conditions' => [
[
'strategies_condition_id' => '1'
],
[
'strategies_condition_id' => '2'
]
],
Cookbook > Database Access & ORM > Saving Data > Modifying Request Data Before Building Entities
Or even create the individual checkboxes manually, where you have proper control over every aspect of every form control, including the name:
echo $this->Form->label('journal_entries_strategy_conditions');
echo $this->Form->hidden('journal_entries_strategy_conditions', ['value' => '']);
$selectedIds = collection($journalEntry['journal_entries_strategy_conditions'])
->extract('strategies_condition_id')
->toArray();
foreach ($journalEntry['strategy']['strategies_conditions'] as $index => $condition) {
echo $this->Form->control(
"journal_entries_strategy_conditions.{$index}.strategies_condition_id",
[
'type' => 'checkbox',
'hiddenField' => false,
'label' => $condition['name'],
'value' => $condition['id'],
'checked' => in_array($condition['id'], $selectedIds, true),
]
);
}
This would create name attributes like:
journal_entries_strategy_conditions[0][strategies_condition_id]
journal_entries_strategy_conditions[1][strategies_condition_id]
...
Cookbook > Views > Helpers > Form > Creating Select, Checkbox and Radio Controls

BelongsToMany associated record won't save in join table

I have 2 tables "Descriptions" and "Phases", associated BelongsToMany using a join Table "DescriptionsPhases", as per Cakephp naming conventions. I've been struggling with this for the best part of a day now: I just cannot get the controller to save the id's of description and phase into my join table, which is basically just 3 columns, id / description_id / phase_id. Possibly more may follow for metadata.
Here is my code structure:
In Descriptions Table:
$this->belongsToMany('Phases', [
'foreignKey' => 'description_id',
'targetForeignKey' => 'phase_id',
'through' => 'DescriptionsPhases',
'saveStrategy' => 'append'
]);
In Phases Table:
$this->belongsToMany('Descriptions', [
'foreignKey' => 'phase_id',
'targetForeignKey' => 'description_id',
'through' => 'DescriptionsPhases'
]);
In DescriptionsPhases Table:
$this->belongsTo('Descriptions', [
'foreignKey' => 'description_id',
'joinType' => 'INNER'
]);
$this->belongsTo('Phases', [
'foreignKey' => 'phases_id',
'joinType' => 'INNER'
]);
In Entities of all 3 of the above (for now):
protected $_accessible = [
'*' => true
];
In Descriptions Controller, add() method. The id for $descriptions->phases is hardcoded for now, just to reduce complexity:
public function add() {
$description = $this->Descriptions->newEntity([
'associated' => ['Phases']]);
if ($this->request->is('post')) {
// additional data to be saved in new description
$description->user_id = $this->Auth->user('id');
$description->designvariant = 666;
// Hardcoded integer TO BE SAVED in associated model (via join table)
$description->phases = [['id' => 5]];
$patch = $this->Descriptions->patchEntity($description, $this->request->getData(), [
'associated' => ['Phases']]);
debug($patch);
$save = $this->Descriptions->save($patch);
debug($save);
if ($save) {
$this->Flash->success(__('The description has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The description could not be saved. Please, try again.'));
}
// pass arrays for dropdown menus
$this->set('trades', ($this->Descriptions->Trades->listLabels()));
$this->set('elements', ($this->Descriptions->Elements->listLabels()));
$this->set('allocations', ($this->Descriptions->Allocations->listLabels()));
$this->set(compact('description'));
$this->render();
}
I have debugged both after patchEntity and saveEntity, and get this data structure.
debug($patch);
object(App\Model\Entity\Description) {
'associated' => [
(int) 0 => 'Phases'
],
'user_id' => (int) 1,
'designvariant' => (int) 666,
'phases' => [
(int) 0 => [
'id' => (int) 5
]
],
'element_id' => 'A',
'shorttext' => 'title',
'longtext' => 'text',
'trade_id' => '0',
'allocation_id' => 'BMST',
'[new]' => true,
'[accessible]' => [
'trade_id' => true,
'element_id' => true,
'allocation_id' => true,
'shorttext' => true,
'longtext' => true,
'designvariant_id' => true,
'user_id' => true,
'created' => true,
'modified' => true,
'trade' => true,
'element' => true,
'allocation' => true,
'phase' => true,
'phases' => true,
'designvariant' => true,
'user' => true,
'amounts' => true,
'costs' => true,
'descriptions_phases' => true,
'descriptions_phase' => true,
'*' => true
],
'[dirty]' => [
'associated' => true,
'user_id' => true,
'designvariant' => true,
'phases' => true,
'element_id' => true,
'shorttext' => true,
'longtext' => true,
'trade_id' => true,
'allocation_id' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Descriptions'
}
debug($save);
object(App\Model\Entity\Description) {
'associated' => [
(int) 0 => 'Phases'
],
'user_id' => (int) 1,
'designvariant' => (int) 666,
'phases' => [
(int) 0 => [
'id' => (int) 5
]
],
'element_id' => 'A',
'shorttext' => 'title',
'longtext' => 'text',
'trade_id' => '0',
'allocation_id' => 'BMST',
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2019-01-11T10:04:32+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\FrozenTime) {
'time' => '2019-01-11T10:04:32+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'id' => (int) 133,
'[new]' => false,
'[accessible]' => [
'trade_id' => true,
'element_id' => true,
'allocation_id' => true,
'shorttext' => true,
'longtext' => true,
'designvariant_id' => true,
'user_id' => true,
'created' => true,
'modified' => true,
'trade' => true,
'element' => true,
'allocation' => true,
'phase' => true,
'phases' => true,
'designvariant' => true,
'user' => true,
'amounts' => true,
'costs' => true,
'descriptions_phases' => true,
'descriptions_phase' => true,
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Descriptions'
}
The record is saved smoothly into the Descriptions Table, but it just won't save any associated data.
Am I missing something obvious?
Any help is gratefully received. Also, if you need more info, plz ask as it's my 1st post here :-)
Only entities are being saved, ie phases must hold an array of entities, not nested arrays. The nested arrays are what you'd pass to newEntity()/patchEntity(), where they would be converted into entities accordingly.
$description->phases = [
$this->Descriptions->Phases->get(5)
];
See also
Cookbook > Database Access & ORM > Saving Data > Saving With Associations
Cookbook > Database Access & ORM > Saving Data > Saving Entities > Saving Associations

How to save additional data to join Table if joined entities are created new

i am trying to save additional data to a belongsToMany join Table.
I followed the instructions here
But they are just in case the entities already exist, because an id is used it seems. But my entities should be new created and additional join data should be saved.
My save data looks like this. Everything is persisted fine, except the additional field 'type_keys'
(int) 0 => object(Cloud\Model\Entity\MediaObject) {
'media_object_type_id' => 'image',
'title' => '1482842705_1_749145',
'relative_path' => '/optional_images/1/1/',
'extension' => 'jpg',
'size' => (int) 142683,
'original_title' => 'logo.jpg',
'_joinData' => [
'type_key' => 'optional_image_1'
],
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'media_object_type_id' => true,
'title' => true,
'relative_path' => true,
'extension' => true,
'size' => true,
'original_title' => true,
'_joinData' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Cloud.MediaObjects'
}
Unfortunately just the joind ids in the join table are saved, but not the joinData field 'type_keys'
I would be happy if someone can give me a clue.
On further testing i found out that the join data gets overwritten when saving.
For your information: I am setting the media object join data in the beforeSave callback.
object(Cloud\Model\Entity\Touchpoint) {
'title' => 'test',
'user_id' => (int) 1,
'tp_image' => [
'name' => '',
'type' => '',
'tmp_name' => '',
'error' => (int) 4,
'size' => (int) 0
],
'optional_image_1' => [
'name' => 'logo.jpg',
'type' => 'image/jpeg',
'tmp_name' => '/tmp/phpMeTwuQ',
'error' => (int) 0,
'size' => (int) 142683
],
'created' => object(Cake\I18n\Time) {
'time' => '2016-12-27T13:19:03+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\Time) {
'time' => '2016-12-27T13:19:03+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'brand_id' => (int) 1,
'media_objects' => [
(int) 0 => object(Cloud\Model\Entity\MediaObject) {
'media_object_type_id' => 'image',
'title' => '1482844743_1_988232',
'relative_path' => '/optional_images/1/1/',
'extension' => 'jpg',
'size' => (int) 142683,
'original_title' => 'logo.jpg',
'_joinData' => object(Cake\ORM\Entity) {
'touchpoint_id' => (int) 8,
'media_object_id' => (int) 8,
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'MediaObjectsTouchpoints'
},
'created' => object(Cake\I18n\Time) {
'time' => '2016-12-27T13:19:03+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\Time) {
'time' => '2016-12-27T13:19:03+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'id' => (int) 8,
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [
'_joinData' => [
'type_key' => 'optional_image_1'
]
],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Cloud.MediaObjects'
}
],
'id' => (int) 8,
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Cloud.Touchpoints'
}
So i know it gets overwritten, but i am not sure how to do this the right way?
Ok i found the solution now.
Since i saw an entity is created as _joinData i created an entity myself and did set the property in the entity myself, this way the _joinData does not get replaced, but just enriched with the ids.
$joinTable = TableRegistry::get('MediaObjectsTouchpoints');
$newMediaObject->_joinData = $joinTable->newEntity();
$newMediaObject->_joinData->type_key = 'something';
$entity->media_objects[] = $newMediaObject;

How to print _matchingData object value in cakephp 3

I have printed object and trying to print Tenats.stage
<?php foreach ($tenancies as $tenancy): ?>
<td><?= debug($tenancy); ?></td>
Print this =>
object(App\Model\Entity\Tenancy) {
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2016-03-18T15:57:40+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'tenants' => [],
'property' => object(Cake\ORM\Entity) {
'id' => (int) 4110,
'address1' => '119 Alan Moss Road',
'postcode' => 'le115ly',
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Properties'
},
'_matchingData' => [
'Tenants' => object(Cake\ORM\Entity) {
'stage' => (int) 2,
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Tenants'
}
],
I need to 'stage' value
Any help please
That's how to print _matchingData
<td><?= h($tenancy->_matchingData['Tenants']->stage); ?></td>
But if you specify the main parents field id (Tenancy.id) automaticaly your data will look much much better. for example my parent model is "Tenancy" Now I am getting Tenant.id and Tenancy.id and Property.id :
$tenancies = $this
->find()
->select([
'Tenancy.id', 'Tenancy.created', 'Tenancy.stage',
'Properties.id', 'Properties.address1', 'Properties.postcode',
'Tenants.stage',
])
->contain('Properties', function(\Cake\ORM\Query $query) {
return $query->where([
'Properties.active' => 1
]);
})
->contain([
'Tenants'
])
->matching('Tenants', function(\Cake\ORM\Query $query) {
return $query->where([
'Tenants.active' => 1
]);
})
->where([
'Tenancy.active' => 1,
$conditions
])
->order([
'Tenancy.created' => 'DESC',
'Tenants.tenancy_id'
]);
return $tenancies;
}
It prints the array with deep associations which is cool and I can get my tenants property like this:
<td><?= h($tenancy->tenants->stage); ?></td>
Prints=>
0 => object(App\Model\Entity\Tenancy) {
'id' => (int) 3923,
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2016-03-19T13:12:32+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'stage' => (int) 2,
'tenants' => [
(int) 0 => object(Cake\ORM\Entity) {
'id' => (int) 8903,
'user_id' => (int) 15318,
'tenancy_id' => (int) 3923,
'needs_guarantor' => true,
'guarantor_id' => null,
'holding_fee' => (float) 50,
Result: Make sure always you write your query properly so you access your data nice and tidy.

CakePHP filter a deep associated find

I have a deep associated find and one association is retrieving too many none related records for modules_employees.
I should see only one record for modules_employees under the course_modules but it retrieves many because they can be many with course_modules_id but only one with courses_employee_id.
The modules_employees table
'id' => (int) 18,
'courses_employee_id' => (int) 31,
'course_module_id' => (int) 7,
'completed_on' => null,
CoursesEmployee->course->course_modules->modules_employees
CoursesEmployeesController.php
public function player($id = null)
{
$coursesEmployee = $this->CoursesEmployees->get($id, [
'contain' =>
[
'Employees',
'Courses',
'CourseModules',
'Courses.CourseModules',
'Courses.CourseModules.ModulesEmployees',
'Courses.CourseFiles'
]
]);
$this->set('coursesEmployee', $coursesEmployee);
debug($coursesEmployee);
$this->set('_serialize', ['coursesEmployee']);
}
The current find object, you will see one of the course_modules has two modules_employees when I should have one.
object(App\Model\Entity\CoursesEmployee) {
'id' => (int) 31,
'employee_id' => (int) 3,
'course_id' => (int) 3,
'course_module_id' => (int) 7,
'course_module' => object(App\Model\Entity\CourseModule) {
'id' => (int) 7,
'course_id' => (int) 3,
'name' => 'Module 2',
},
'course' => object(App\Model\Entity\Course) {
'id' => (int) 3,
'name' => 'Treacys Hotel Induction Training',
'course_files' => [
(int) 0 => object(App\Model\Entity\CourseFile) {
'id' => (int) 2,
'name' => 'Manual_Handling_doc.txt',
'type' => 'doc',
}
],
'course_modules' => [
(int) 0 => object(App\Model\Entity\CourseModule) {
'id' => (int) 6,
'course_id' => (int) 3,
'name' => 'Module 1',
'module_order' => (int) 1,
'modules_employees' => [
(int) 0 => object(App\Model\Entity\ModulesEmployee) {
'id' => (int) 1,
'courses_employee_id' => (int) 0,
'course_module_id' => (int) 6,
'started_on' => object(Cake\I18n\Time) {
'time' => '2015-09-08T04:16:16+0000',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'completed_on' => object(Cake\I18n\Time) {
'time' => '2015-09-09T08:22:16+0000',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'completed' => true,
'deleted' => null,
'[new]' => false,
'[accessible]' => [
'employee_id' => true,
'module_id' => true,
'started_on' => true,
'completed_on' => true,
'completed' => true,
'employee' => true,
'module' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[repository]' => 'ModulesEmployees'
}
],
'[repository]' => 'CourseModules'
},
(int) 1 => object(App\Model\Entity\CourseModule) {
'id' => (int) 7,
'course_id' => (int) 3,
'name' => 'Module 2',
'module_order' => (int) 2,
'modules_employees' => [
(int) 0 => object(App\Model\Entity\ModulesEmployee) {
'id' => (int) 2,
'courses_employee_id' => (int) 31,
'course_module_id' => (int) 7,
'started_on' => object(Cake\I18n\Time) {
'time' => '2015-09-17T00:00:00+0000',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'completed_on' => null,
'[repository]' => 'ModulesEmployees'
},
(int) 1 => object(App\Model\Entity\ModulesEmployee) {
'id' => (int) 18,
'courses_employee_id' => (int) 32,
'course_module_id' => (int) 7,
'started_on' => object(Cake\I18n\Time) {
'time' => '2015-09-17T00:00:00+0000',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'completed_on' => null,
'[repository]' => 'ModulesEmployees'
}
],
'[repository]' => 'CourseModules'
},
],
},
'employee' => object(App\Model\Entity\Employee) {
'id' => (int) 3,
'user_id' => (int) 4,
},
'[repository]' => 'CoursesEmployees'
}
You should look into matching
http://book.cakephp.org/3.0/en/orm/query-builder.html#filtering-by-associated-data
$query = $this->CoursesEmployees->findById($id)
->contain(['Your_Models_You_Wanna_Contain'])
->matching('Courses.CourseModules.ModulesEmployees', function ($q) use ($id) {
return $q->where(['courses_employee_id' => $id]);
});
if nothing matches then you wont get a CourseEmployee back aswell, if you would still need that you could also use contain:
http://book.cakephp.org/3.0/en/orm/retrieving-data-and-resultsets.html#passing-conditions-to-contain
$query = $this->CoursesEmployees->findById($id)->contain([
'Courses.CourseModules.ModulesEmployees' => function ($q) use ($id) {
return $q
->where(['courses_employee_id' => $id]);
}
]);

Resources