How to add associated data while adding User simultaneously CakePHP 3.x? - cakephp

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.

Related

CakePHP 4 Saving Data Twice

CakePHP is somehow saving the same data twice. For some reason I want to implement this add method such that $dummy is saved right away as someone goes directly to domain.com/recordings/add
It looks pretty straight forward and I've been scratching my head. I've checked for validation errors; I've tried disabling validation; I've tried using patchEntity() instead.
Though, one strange thing is that, if you go to domain.com/recordings/add by hitting the add recording button in domain.com/recordings/index (instead of typing the url out on the browser), the data is saved just once.
Controller:
public function add()
{
$dummy = [
"user_id" => 1,
"title" => "tgfbthgdthb",
"body" => "rgcvfghfhdxcgb",
"published" => 0,
];
$recording = $this->Recordings->newEntity($dummy);
$this->Recordings->save($recording);
}
Model/table:
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('recordings');
$this->setDisplayField('title');
$this->setPrimaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER',
]);
$this->hasMany('Words', [
'foreignKey' => 'recording_id',
]);
}
Model/entity:
protected $_accessible = [
'user_id' => true,
'title' => true,
// 'slug' => true,
'body' => true,
'published' => true,
'created' => true,
'modified' => true,
'user' => true,
'words' => true,
];
The view:
<?php
/**
* #var \App\View\AppView $this
* #var \App\Model\Entity\Recording $recording
*/
?>
<div class="row">
<aside class="column">
<div class="side-nav">
<h4 class="heading"><?= __('Actions') ?></h4>
<?= $this->Html->link(__('List Recordings'), ['action' => 'index'], ['class' => 'side-nav-item']) ?>
</div>
</aside>
<div class="column-responsive column-80">
<div class="recordings form content">
<?= $this->Form->create($recording) ?>
<fieldset>
<legend><?= __('Add Recording') ?></legend>
<?php
echo $this->Form->control('user_id', ['options' => $users]);
echo $this->Form->control('title');
echo $this->Form->control('body');
echo $this->Form->control('published');
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
</div>
</div>
Do not try to accommodate to people's lazyness, do not allow them to save data by simply visiting a URL, ie via a GET request, that's just going to cause trouble, and on top of that it's bad application design.
Implement at least a proper safeguard that only saves data for POST requests.
Browsers may issue multiple requests in various scenarios, from preflight OPTIONS requests to weird quirks, like for example Firefox aborting the request if doesn't find any encoding information in the first x bytes of response data, and then issuing a new request that assumes a specific encoding for the response.
public function add()
{
if ($this->request->is('post')) {
// save data here
}
}

set value in controller to associated model befor save in cakephp

I have 'seller_businessestable andseller_business_categories` table. I am using a single form to save values to sellers and seller_businesses.
The form is like
<?= $this->Form->create() ?>
<?= $this->Form->input('company_name') ?>
<?= $this->Form->input('logo', ['type' => 'file']) ?>
<?= $this->Form->input('seller_business_categories.category_id') ?>
<?= $this->Form->button('Submit', ['type' => 'submit']) ?>
<?= $this->Form->end() ?>
The seller_business_categories has a column seller_id and I have to save the logged in user id in it.
I know to save value to table seller_businesses in controller like
$seller_business = $this->SellerBusinesses->newEntity();
$seller_business->seller_id = $this->Auth->user('id');
$this->SellerBusinesses->patchEntity($seller_business, $this->request->data, [
'associated' => ['SellerBusinessCategories']
]);
But how to save default value to associated model SellerBusinessCategories within controller ?
You need make relationship
SellerBusinessCategorie.php MODEL
public function initialize(array $config)
{
parent::initialize($config);
$this->table('seller_business_categories');
$this->hasMany('seller_businesses', [
'foreignKey' => 'seller_id'
]);
}
SellerBusinesses.php MODEL
public function initialize(array $config)
{
parent::initialize($config);
$this->belongsTo('seller_business_categories', [
'foreignKey' => 'category_id',
'joinType' => 'INNER'
]);
}
Controller
$seller_business = $this->SellerBusinesses->newEntity();
$seller_business->seller_id = $this->Auth->user('id');
$this->SellerBusinesses->patchEntity($seller_business, $this->request->data]);
NOW TRY..it works

How to save associated joinData in cakephp 3.x

I have Problems and Fields in a many-to-many relationship. The join table fields_problems has a field named fieldvalue I am trying to have a form that will insert a problem record and also multiple records into fields_problems.
/src/Model/Table/ProblemsTable.php
class ProblemsTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
$this->table('problems');
$this->displayField('id');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsToMany('Fields', [
'foreignKey' => 'problem_id',
'targetForeignKey' => 'field_id',
'joinTable' => 'fields_problems'
]);
}
...
/src/Model/Table/FieldsTable.php
class FieldsTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
$this->table('fields');
$this->displayField('name');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsToMany('Problems', [
'foreignKey' => 'field_id',
'targetForeignKey' => 'problem_id',
'joinTable' => 'fields_problems'
]);
}
...
/src/Model/Table/FieldsProblemsTable.php
class FieldsProblemsTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
$this->table('fields_problems');
$this->displayField('id');
$this->primaryKey('id');
$this->belongsTo('Fields', [
'foreignKey' => 'field_id',
'joinType' => 'INNER'
]);
$this->belongsTo('Problems', [
'foreignKey' => 'problem_id',
'joinType' => 'INNER'
]);
}
...
And I want to Add a new problem, link it to fields, and add values to the fieldvalue field in the join table.
So I have this /src/Template/Problems/add.ctp
<div class="problems form large-10 medium-9 columns">
<?= $this->Form->create($problem) ?>
<fieldset>
<legend><?= __('Add Problem') ?></legend>
<?php
echo $this->Form->input("Problems.id");
echo $this->Form->input('Problems.summary');
echo $this->Form->input('Problems.Fields.0._ids', [
'type' => 'select',
'multiple' => false,
'options' => $fields,
]);
echo $this->Form->input('Problems.Fields.0._joinData.fieldvalue');
echo $this->Form->input('Problems.Fields.1._ids', [
'type' => 'select',
'multiple' => false,
'options' => $fields,
]);
echo $this->Form->input('Problems.Fields.1._joinData.fieldvalue');
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
And this add() in /src/Controller/ProblemsController.php
public function add()
{
$problem = $this->Problems->newEntity();
if ($this->request->is('post')) {
$problem = $this->Problems->patchEntity($problem, $this->request->data, ['associated'=>['Fields._joinData']] );
//$problem->dirty('fields',true);
if ($this->Problems->save($problem)) {
$this->Flash->success(__('The problem has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The problem could not be saved. Please, try again.'));
}
}
$fields = $this->Problems->Fields->find('list', ['limit' => 200]);
$this->set(compact('problem', 'fields'));
$this->set('_serialize', ['problem']);
}
When I fill out and submit the app form, the Problem record is saved, but the association is not, nothing gets inserted into fields_problems.
What am I doing wrong that is preventing the associated joinData from being saved?
Despite the cookbook (http://book.cakephp.org/3.0/en/views/helpers/form.html) saying to use the special _ids key, don't!
Changing "_ids" to "id" fixed the form and now it functions properly saving the data into the jointable.
Here is the example from the cookbook that I built my app with
echo $this->Form->input('tags.0.id');
echo $this->Form->input('tags._ids', [
'type' => 'select',
'multiple' => true,
'options' => $tagList,
]);
Here is how it should be
echo $this->Form->input('tags.0.id', [
'type' => 'select',
'multiple' => false,
'options' => $tagList,
]);

cakephp 3 multi level associated save

I have a Users model which has a hasOne relationship to an Employees model. When I save a user I also add a record in the Employees table with the user_id. This works fine.
The Employee can be associated to belongsToMany Courses through CoursesEmployees table. This saves ok when I only save the Employees.
My problem is I want to save all 3 three but the Courses do not get saved.
Save user -> save employee with new user_id -> save courses chosen for new employee with employee_id in courses_employees
EmployeesTable.php
public function initialize(array $config)
{
$this->table('employees');
$this->displayField('name');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
]);
$this->belongsTo('Hotels', [
'foreignKey' => 'hotel_id',
'joinType' => 'INNER'
]);
$this->belongsToMany('Courses', [
'foreignKey' => 'employee_id',
'targetForeignKey' => 'course_id',
'joinTable' => 'courses_employees'
]);
}
users add.ctp:
<?= $this->Form->create($user) ?>
<fieldset>
<legend><?= __('Add User') ?></legend>
<?php
echo $this->Form->input('role_id', ['options' => $roles, 'empty' => true]);
echo $this->Form->input('email');
echo $this->Form->input('active');
echo $this->Form->input('activation_key');
echo $this->Form->input('password');
echo $this->Form->input('employee.name');
echo $this->Form->input('employee.email');
echo $this->Form->input('employee.surname');
echo $this->Form->input('employee.employee_num');
echo $this->Form->input('employee.courses._ids', ['options' => $courses]);
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
Here is the post data sent to the add method for save. With this the user and employee get saved correctly but no recored in the courses_employees.
If I save the employee form the employee controller the courses_employees get add ok so I know the associations are setup ok.
[
'role_id' => '1',
'email' => 'asdasd#asdasd.com',
'active' => '11111111111',
'activation_key' => '1',
'password' => 'asdasdadasdasdasda',
'employee' => [
'name' => 'asdasdasdasd',
'email' => 'asdasdasd2dasd.com',
'surname' => 'asdasdads',
'employee_num' => 'asdasdadasdas',
'courses' => [
'_ids' => [
(int) 0 => '2'
]
]
]
]
UserController.php
public function add()
{
$this->loadModel('Courses');
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
$user = $this->Users->patchEntity($user, $this->request->data, [
'associated' => ['Employees', 'Courses']
]);
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The user could not be saved. Please, try again.'));
}
}
$roles = $this->Users->Roles->find('list', ['limit' => 200]);
$courses = $this->Courses->find('list', ['limit' => 200]);
$this->set(compact('user', 'roles', 'courses'));
$this->set('_serialize', ['user']);
}
I have found the solution. I have read the cookbook again and I noted in one of the examples snippets 'associated' => ['Tags', 'Comments.Users']
I have applied this to my controller and it now works. It is saving on all levels.
$user = $this->Users->patchEntity($user, $this->request->data, [
'associated' => ['Employees', 'Employees.Courses']
]);

Cakephp 1.3 select field options from list or enter new

I have a cakephp 1.3 app which get's a person's consent to participate in a study. I'm trying to link inclusion criteria and exclusion criteria for the study on the Add study page. The HABTM relationship is setup and working(If there's only one way to select them) What I'm trying to do now is display the current Inclusion Criteria as a list of checkboxes the person adding the study can select, but I also want a field where new criteria can be entered as a comma seperated list. Right now it will save the checkboxes, but I want it to also add the text entered in the field as well. Screen showing the Add study as it sits now is below. I don't really know how I would go about doing that. Is there a way to do this or will I have to take the data entered, sort through it and then add it to $this->data[inclusion field data]? An example would be nice. :) I've added the code I have so far below the image.
Controller:
/**
* Add a study.
*/
function add() {
if (!empty($this->data)) {
//get the inclusions from the text data
//Sort through the comma seperated list and add it to $this->data['Study']['Inclusions']???
$this->Study->create();
if ($this->Study->save($this->data)) {
$this->Session->setFlash(__('The study has been saved', true));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The study could not be saved. Please, try again.', true));
}
}
$this->set('inclusions', $this->Study->Inclusion->find('list', array(
'fields' => array('id', 'name'),
'order' => 'Inclusion.name',
'recursive' => 0,
)));
$this->set('exclusions', $this->Study->Exclusion->find('list', array(
'fields' => array('id', 'name'),
'order' => 'Exclusion.name',
'recursive' => 0,
)));
$this->set('forms', $this->Study->Form->find('list', array('order' => 'Form.name','recursive' => 0,)));
}
View:
<div class="studies form">
<?php echo $this->Form->create('Study', array('type' => 'file'));?>
<fieldset>
<legend><?php __('Add Study'); ?></legend>
<?php
echo $this->Form->input('studyname', array('label'=>'Study Name','required' => true));
echo $this->Form->input('studynumber', array('label'=>'Study Number','required' => true));
echo $this->Form->input('file', array('label'=>'Select Electronic Consent','type' => 'file'));
echo $this->Form->input('consent_form_date');
?>
<fieldset class="inclusion">
<legend><?php __('Inclusions'); ?></legend>
<div class="IncExc">
<?php
echo $this->Form->input('Inclusion',array(
'label' => false,
'type' => 'select',
'multiple' => 'checkbox',
'options' => $inclusions,
'selected' => $html->value('Inclusion.Inclusion'),
));
?>
</div>
<?php
echo $form->input('Inclusion.inclusions',array(
'type' => 'text',
'label' => __('Add New Inclusions',true),
'after' => __('Seperate each new Inclusion with a comma. Eg: family, sports, icecream',true)
));
?>
</fieldset>
<fieldset>
<legend><?php __('Exclusions'); ?></legend>
<div class="IncExc">
<?php
echo $this->Form->input('Exclusion',array(
'label' => false,
'type' => 'select',
'multiple' => 'checkbox',
'options' => $exclusions,
'selected' => $html->value('Exclusion.Exclusion'),
));
?>
</div>
</fieldset>
<fieldset style="width: 700px;">
<legend><?php //__('Forms'); ?></legend>
<?php /*
echo $this->Form->input('Form',array(
'label' => false,
'type' => 'select',
'multiple' => 'checkbox',
'options' => $forms,
'selected' => $html->value('Form.Form'),
));
*/ ?>
</fieldset>
</fieldset>
<?php echo $this->Form->end(__('Submit', true));?>
</div>
You are trying to do pretty much the same as he wanted to do: Saving tags into a database table in CakePHP
What you have to do is:
Check if the manual inclusion field is empty or not. If the field is empty ignore it and go to step 5
If the field is not empty, split it at comma or whatever sign you want to use as a seperator. You can now validate it if you want to.
Use all the inclusions of step 3 and save them in your custom or real inclusion table. You should keep track of those custom entries in some way, so you don't mess them up with those you provided. Save all the ids generated while saving within an array.
Merge the collected ids of step 3 with those you got from the form which were given by you.
Save all collected and given data to database
The answere in the post given above including the code there can be used to do that. You should get along with it pretty well if you read the whole post + code comments ;)
If you have any further questions, just ask ;)
Greetings
func0der

Resources