Apologies if this has been asked before. All of the examples I can find are old or apply to legacy versions of CakePHP, e.g. cakephp: saving to multiple models using one form is 7 years old.
I have an application in CakePHP 4.1.6. Two of the tables in the database are called tbl_users
and tbl_orgs
("orgs" in this case means "Organisations").
When I add an Organisation I also want to create a User who is the main contact within the Organisation. This involves saving to both the tbl_orgs
and tbl_users
tables when the form is submitted.
The problem I'm experiencing is how to get the form working in a way where it will run the validation rules for both tbl_users
and tbl_orgs
when submitted.
This is how our application is currently structured:
There is a Controller method called add()
in src/Controller/TblOrgsController.php
. This was generated by bake
and was initially used to insert a new Organisation into the tbl_orgs
table. At this point it didn't do anything in terms of tbl_users
however it worked in terms of saving a new Organisation and running the appropriate validation rules.
One validation rule is that every companyname
record in tbl_orgs
must be unique. If you try to insert more than 1 company with the name "My Company Limited" it would give the validation error "This company name already exists":
// src/Model/Table/TblOrgsTable.php
public function buildRules(RulesChecker $rules): RulesChecker
{
$rules->add(
$rules->isUnique(['companyname']),
[
'errorField' => 'companyname',
'message' => 'This company name already exists',
]
);
return $rules;
}
Whilst the above applies to TblOrgs
we also have an buildRules()
in TblUsers
which applies similar logic on an email
field to make sure that all email addresses are unique per user.
In the add()
Controller method we start by specifying a new empty entity for TblOrgs
:
// src/Controller/TblOrgsController.php
public function add()
{
$org = $this->TblOrgs->newEmptyEntity();
// ...
$this->set(compact('org'));
}
When the form is created we pass $org
:
// templates/TblOrgs/add.php
<?= $this->Form->create($org) ?>
<?= $this->Form->control('companyname') ?>
<?= $this->Form->end() ?>
When the TblOrgs
fields are rendered by the browser we can inspect the HTML and see these are obeying the corresponding Model. This is clear because of things such as required="required"
and maxlength="100"
which correspond to the fact that field is not allowed to be empty and is a VARCHAR(100)
field in the database:
<input type="text" name="companyname" required="required" id="companyname" maxlength="100">
It also works in terms of the rules specified in buildRules
for TblOrgs
. For example if I enter the same company name twice it shows the appropriate error in-line:
I then tried to introduce fields for TblUsers
. I prefixed the form fields with dot notation, e.g. this is intended to correspond to tbl_users.email
input field:
<?= $this->Form->control('TblUser.email') ?>
When inspecting the HTML it doesn't do the equivalent as for TblOrgs
. For example things like maxlength
or required
are not present. It effectively isn't aware of TblUsers
. I understand that $org
in my Controller method is specifying a new entity for TblOrgs
and not TblUsers
. I reviewed the CakePHP documentation on Saving With Associations which says
The
save()
method is also able to create new records for associations
However, in the documentation the example it gives:
$firstComment = $articlesTable->Comments->newEmptyEntity();
// ...
$tag2 = $articlesTable->Tags->newEmptyEntity();
In this case Tags
is a different Model to Comments
but newEmtpyEntity()
works for both. With this in mind I adapted my add()
method to become:
$org = $this->TblOrgs->TblUsers->newEmptyEntity();
But this now gives an Entity for TblUsers
. It seems you can have either one or the other, but not both.
The reason this doesn't work for my use-case is that I can either run my Validation Rules for TblOrgs
(but not TblUsers
) or vice-versa.
How do you set this up in a way where it will run the validation rules for both Models? It doesn't seem to be an unreasonable requirement that a form may need to save data to multiple tables and you'd want the validation rules for each of them to run. I get the impression from the documentation that it is possible, but it's unclear how.
For reference there is an appropriate relationship between the two tables:
// src/Model/Table/TblOrgsTable.php
public function initialize(array $config): void
{
$this->hasMany('TblUsers', [
'foreignKey' => 'o_id',
'joinType' => 'INNER',
]);
}
and
// src/Model/Table/TblUsersTable.php
public function initialize(array $config): void
{
$this->belongsTo('TblOrgs', [
'foreignKey' => 'o_id',
'joinType' => 'INNER',
]);
}