So, i have problem with saving data to DB
Model Table is
...
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
...
I want to store new Address in row and data structure is
object(App\Model\Entity\Address) {
'type' => (int) 1,
'Users' => [
'user_id' => '9'
],
'short_name' => 'Test',
'long_name' => 'test',
'additional_name' => 'test',
I have tried in controller
if ($this->request->is('post')) {
$address = $this->Addresses->patchEntity($address, $this->request->data, ['associated' => ['Users.user_id']]);
if ($this->Addresses->save($address)) {
$this->Flash->success(__('The address has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The address could not be saved. Please, try again.'));
}
And in my template view this is input field
echo $this->Form->input('Users.user_id', ['type' => 'select', 'options' => $groups, 'class' => 'form-control']);
echo $this->Form->input('short_name', ['class' => 'form-control']);
echo $this->Form->input('long_name', ['class' => 'form-control']);
So, what can I do? How to reorganize data stucture?
Thank you!
You are using the CakePHP <= 2.x styled naming conventions for your inputs, that's not how it works with CakePHP 3.x anymore.
Please refer to the docs on how to create inputs for associated data:
Cookbook > Views > Helpers > Form > Creating Inputs for Associated Data
You have to use the appropriate property name of the association, just as it appears in the entity when reading data. For belongsTo associations, this is by default the singular, underscored variant of the association alias, so in your case user, ie
echo $this->Form->input('user.user_id', [
'type' => 'select',
'options' => $groups,
'class' => 'form-control'
]);
See also
Cookbook > Database Access & ORM > Associations > BelongsTo Associations
Cookbook > Database Access & ORM > Saving Data > Saving BelongsTo Associations
Cookbook > Views > Helpers > Form > Field Naming Conventions
Related
I’m struggling with a self-referencing belongsToMany association. To be clear I have a Models table and each model can have multiple accessories, which are also models. So I have a linking table, Accessories, with a model_id (the “parent” model) and an accessory_id (the “child” model).
I finally found how to declare this in the ModelsTable :
$this->belongsToMany('AccessoryModels', [
'className' => 'Models',
'through' => 'Accessories',
'foreignKey' => 'model_id',
'targetForeignKey' => 'accessory_id'
]);
$this->belongsToMany('ParentAccessoryModels', [
'className' => 'Models',
'through' => 'Accessories',
'foreignKey' => 'accessory_id',
'targetForeignKey' => 'model_id'
]);
I also got it working to retrieve these data in the Models view.
But I now have some issues for the addAccessory (and deleteAccessory) method in the Models controller and views.
Here is it in the controller :
public function addAccessory($id = null)
{
$model = $this->Models->get($id, [
'contain' => []
]);
if ($this->getRequest()->is(['patch', 'post', 'put'])) {
$accessory = $this->getRequest()->getData();
if ($this->Models->link($model, [$accessory])) {
return $this->redirect(['action' => 'view', $model->id]);
}
}
$models = $this->Models
->find('list', ['groupField' => 'brand', 'valueField' => 'reference'])
->order(['brand' => 'ASC', 'reference' => 'ASC']);
$this->set(compact('model', 'models'));
}
The view is only a select dropdown with the list of all available models (I'm using a plugin, AlaxosForm, but it takes the original CakePHP control() function behaviour) :
echo $this->AlaxosForm->label('accessory_id', __('Accessory'), ['class' => 'col-sm-2 control-label']);
echo $this->AlaxosForm->control('accessory_id', ['options' => $models, 'empty' => true, 'label' => false, 'class' => 'form-control']);
echo $this->AlaxosForm->button(___('Submit'), ['class' => 'btn btn-default']);
The problem is that the addAccessory() function won't work when getting the submitted data from the form. I see the problem, as when posting the inserted values, only an array with one accessory_id is given (for example ['accessory_id' => 1] and the link() doesn't know what to do with it. So I think it’s an issue about data formatting but don’t see how to get it correctly.
Saving links (like any other ORM saving methods) requires to pass entities, anything else will either be ignored, or trigger errors.
So you have to use the accessory_id to obtain an entity first, something like:
$accessoryId = $this->getRequest()->getData('accessory_id');
$accessory = $this->Models->AccessoryModels->get($accessoryId);
Furthermore you need to use the model/table that corresponds to the target entities (second argument) that you want to link (to the first argument), ie you'd have to use AccessoryModels, like:
$this->Models->AccessoryModels->link($model, [$accessory])
See also
Coobook > Database Access & ORM > Saving Data > Associate Many To Many Records
In a edit-template, I created 2 forms. The first to edit a record from 'Table1' and a second form to add an record to the associated (belongsTo) table 'Table2'. I don't want to edit the associated record but add a new one in Table2 and change the bindingkey of the record from 'Table1'.
In the controller I use patchEntity(), but this way the original linked record in 'Table2' is edited.
The code for the second form look like this.
<?= $this->Form->create($machine, ['url' => ['action' => 'edit_foto']]);?>
<?= $this->Form->controls([
'id' => ['type' => 'hidden'],
'foto.omschrijving' => [
'type' => 'text',
'label' => __('naam van de machine'),
'value' => $result->type_machine
], []);?>
<?= $this->Form->button(__('save'), ['type' => 'submit']); ?>
<?= $this->Form->end(); ?>
In the controller for 'Table1' I use something like this.
public function editFoto($id) {
$data = $this->getRequest()->getData();
$machine = $this->Machines->get($id, [
'contain' => [
'Foto'
]
]);
if ($this->request->is([
'patch',
'post',
'put'
])) {
$machine = $this->Machines->patchEntity($machine, $data);
if ($this->Machines->save($machine)) {
$this->Flash->success(__('Machine {naam} has been saved.', [
'naam' => $machine['naam']
]));
return $this->redirect(['action' => 'edit', $id]);
}
}
}
How can I prevent the controller from editing the original record and instead force to add a new record in 'Table2' and change the bindingkey in 'Table1'?
Help is much appreciated.
added information:
the data send by the form looks like:
[
'id' => '87',
'foto' => [
'omschrijving' => 'WAKER 135 - ter - testje',
'foto' => [
'tmp_name' => 'C:\xampp\tmp\php6CFD.tmp',
'error' => (int) 0,
'name' => 'MAKITA DTD153RTJ accuschroevendraaier.jpg',
'type' => 'image/jpeg',
'size' => (int) 188971
],
'dir_id' => '5c9dd428cc4ef'
]
]
Instead of
$machine = $this->Machines->patchEntity($machine, $data);
$this->Machines->save($machine);
you should use
$machine = $this->Machines->newEntity($data, [
'associated' => ['Fotos']
]);
$this->Machines->save($machine);
to save the data. If the associations between Table1 and Table2 are correct in the models, this will also update the binding keys.
Make sure, the format of the ids in FormHelper is correct. See the docs, how to handle Saving With Associations. You can see this in $data. When submitting the form, $data should be in the following format:
$data = [
'id' => '...',
'foto' => [
// ... model data
],
];
I have a list of existing user roles in database for example like this:
+----+-------+-----------+
| id | level | label |
+----+-------+-----------+
| 1 | 0 | admin |
| 2 | 1 | moderator |
| 3 | 2 | blogger |
+----+-------+-----------+
And I would like to attach a role to a user when its created. What I've tried is following:
In my BcUsersController:
$bcUser = $this->BcUsers->patchEntity($bcUser, $this->request->data,
[
'associated' => [
'BcUserInfos',
'BcUserRoles'
]
]
);
if ($this->BcUsers->save($bcUser))...
Associations:
//BcUserRolesTable
$this->belongsToMany('BcUsers', [
'className' => 'BcUsers',
'foreignKey' => 'role',
'propertyName' => 'BcUserRoles'
]);
//BcUsersTable
$this->hasMany('BcUserRoles', [
'className' => 'BcUserRoles',
'foreignKey' => 'id',
'bindingKey' => 'role',
'propertyName' => 'BcUserRoles',
'joinTable' => 'bc_user_roles'
]);
And this is how I try to inject userRoleId into a user table:
<?= $this->Form->input('BcUserRoles.level', ['type' => 'hidden', 'value' => '0']); ?>
or
<?= $this->Form->input('BcUserRoles.id', ['type' => 'hidden', 'value' => '1']); ?>
When I try to save it gives me no error just refreshes a page and with post data inside fields. If I remove all the associations code, it saves user + userInfo. What am I missing here?
EDIT
I just found an error in my $bcUser variable:
'[errors]' => [
'role' => [
'_required' => 'This field is required'
]
],
And I think thats why it does not save BcUser, but also userRole associations is empty:
'BcUserRoles' => [],
so it wouldnt save userRole to user table even if I would would remove
->requirePresence('role', 'create') from BcUsersTable, would it ?
Just tested. It throws me SQL error because role isnt in query parameters and in database it has to be there, null is not allowed. All users should have a role.
Looks like there are a couple issues here.
Associations
If BcUsersTable contains the foreign key for BcUserRoles then is it a belongsTo relationship.
belongsTo: the current model contains the foreign key.
http://book.cakephp.org/3.0/en/orm/associations.html#belongsto-associations
Equally this would also mean that BcUserRoles has a hasMany relationship to BcUsers not a belongsTo relationship.
hasMany: the other model contains the foreign key.
http://book.cakephp.org/3.0/en/orm/associations.html#hasmany-associations
Post data
I assume that the foreign key for BcUserRolesTable in the BcUsersTable is role. That means you will need to submit a value for role that is the id of the user role you wish to associate.
The following would create a form element to do that:
$this->Form->input('role', ['type' => 'hidden', 'value' => $useRoleId])
Where $userRoleId is the id of the role you wish to associate.
Try updating your belongsTo Relations
$this->belongsTo('BcUsers', [
'className' => 'BcUsers',
'foreignKey' => 'role',
'bindingKey' => 'id',
'propertyName' => 'BcUserRoles'
]);
make it
$this->belongsTo('BcUserRoles', [
'className' => 'BcUsers',
'foreignKey' => 'role',
'bindingKey' => 'id',
]);
Hope your problem will be solved
I have added 4 more columns to my CakePHP Users table and I am trying to figure out how I can include these columns in the $this->Auth->user() object.
I've added the column information to my Users Model and Entity but still no joy. Currently my user object looks like this;
[
'id' => (int) 1,
'username' => 'admin',
'name' => 'Web Admin',
'email' => 'webteam#',
'role' => 'admin',
'created' => object(Cake\I18n\Time) {
'time' => '2016-02-09T16:04:46+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\Time) {
'time' => '2016-02-12T08:53:16+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
}
]
Where is this object created and is there a way I can add my custom values to it, without editing core CakePHP files?
Thanks for your help .
By default the built-in authenticators will fetch all fields in the tables schema.
You most probably just forgot to clear your cache (tmp/cache/models), which you should do whenever you make changes to your schemas.
In case one would want to specify what fields are being fetched, a custom finder would be needed.
See Cookbook > Controllers > Components > Authentication > Customizing Find Query
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'finder' => 'auth'
]
],
]);
In your UsersTable class
public function findAuth(\Cake\ORM\Query $query, array $options)
{
return $query
->select(['id', 'username', 'password', 'column_x', 'column_y']);
}
It should be noted that the fields required for authentication must always be included, ie like username and password!
I would like to be able to add a membership record at the same time I add a user. I can't seem to get it to work. I want to be able to select the membership level and have that data get sent to the controller where I can add the data, unless there is a way to just select the membership level and it will automatically create a membership record for the user that gets added.
The membership level contains the information for that level of membership and the membership record contains some information from the membership level and is associated to a user.
The add form I believe is where the problem lies, but just in case here are the important snippets of code. I tried to keep it as simple as possible.
Users Table Initialize Function:
public function initialize(array $config)
{
parent::initialize($config);
$this->table('users');
$this->displayField('id');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->hasOne('Memberships', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
}
Memberships Table Initialize Function:
public function initialize(array $config)
{
$this->table('memberships');
$this->displayField('id');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
$this->hasOne('MembershipLevels', [
'foreignKey' => 'membership_level_id',
'joinType' => 'INNER'
]);
}
Users Controller Add Method:
public function add()
{
$user = $this->Users->newEntity($this->request->data(), [
'associated' => [
'Memberships' => ['associated' => ['MembershipLevels']]
]
]);
if ($this->request->is('post')) {
$user = $this->Users->patchEntity($user, $this->request->data(), [
'associated' => [
'Memberships' => ['associated' => ['MembershipLevels']]
]
]);
if ($$this->Users->save($user)) {
$this->Flash->success(__('You have been added.'));
} else {
$this->Flash->error(__('You could not be added. Please, try again.'));
}
}
$membershipLevels = $this->Users->Memberships->MembershipLevels->find('list', ['limit' => 200]);
$this->set(compact('user', 'membershipLevels'));
$this->set('_serialize', ['user', 'membershipLevels']);
}
Add Users Form:
<div class="form">
<h1>Join Now</h1>
<?= $this->Form->create($user); ?>
<fieldset>
<legend><?= __('User Info') ?></legend>
<?= $this->Form->input('full_name', ['required' => false]); ?>
<?= $this->Form->input('username', ['required' => false]); ?>
<?= $this->Form->input('email', ['required' => false]); ?>
<?= $this->Form->input('password', ['required' => false]); ?>
<?= $this->Form->input('password_confirmation', ['type' => 'password', 'required' => false]); ?>
<?php
if ($current_user['role'] === 1 && isset($logged_in)) {
echo $this->Form->input('role', ['type' => 'select', 'options' => ['1' => 'Admin', '2' => 'Editor', '3' => 'Author', '4' => 'Reader'], 'default' => '4']);
}
?>
</fieldset>
<fieldset>
<legend><?= __('Membership Info') ?></legend>
<?= $this->Form->label('Membership Level'); ?>
<?= $this->Form->input('memberships.membership_levels._id', ['options' => $membershipLevels, 'required' => false]); ?>
</fieldset>
<?= $this->Form->button(__('Sign Up'));?>
<?= $this->Form->end();?>
</div>
I changed the code a bit thanks to the comments and answers. The membership levels now show up in the form and I pick a membership level when saving the user, but the membership doesn't get saved as well, just the user. Any further suggestions?
How about start reading the migration guide, or doing the blog tutorial for CakePHP3? Your trial and error approach is time consuming and frustrating. I can't recommend this enough: Always read documentation before shooting in the darkness. There has a lot changed in CakePHP3. This includes the way the dot notation is used. Reading saving associations might help as well.
You don't use any longer the model prefix on the first level.
Model.field
is now just
field
For nested associations it's now
associated_data.field
associated_data.second_level_data.field
Notice that this is inflected and singular or plural depending on the kind of association.
I have updated my code to get the closest I can to getting all the automagic to work.
Key points:
1.) In controller I got the membership levels variable to pass to the view by adding this line. (notice the calling of the users table then memberships and then membershiplevels).
$membershipLevels = $this->Users->Memberships->MembershipLevels->find('list', ['limit' => 200]);
2.) In the view I added:
<?= $this->Form->input('memberships.membership_levels._id', ['options' => $membershipLevels, 'required' => false]); ?>
This is not a complete answer however because I still have to grab the membership level in the controller with a query and then manually save the data from that level to the memberships table.
I can at least populate the membership level input in the form dynamically and submit it.
I had looked over the bookmarker tutorial that I had previously done since it uses associations, but with that tutorial they are able to add the bookmarks_tags record automatically when you add a bookmark and I am not sure if it is due to the functions in the table files or not.