CakePHP 3 Tutorial 17: Adding Associated Records

Submitted by naidim on Tue, 10/25/2016 - 11:42

In the previous tutorial (CakePHP 3 Tutorial 16: Table Relationships) we set up associating tables and displaying associated data, but there's no point to displaying associated data if you can't add it first.

There are multiple ways to save associated data. We'll start with the easiest way first.

1. Save Associated Records Discretely

In our previous tutorial we set up "phone_numbers" which belong to "users". So we can simply add "phone_numbers" by themselves and identify the related "user".

In the phone numbers controller (src/Controller/PhoneNumbersController.php) in the add() function you need to load a list of users to choose from.

public function add()
    $users = $this->PhoneNumbers->Users->find('list');
    $this->set(compact('phoneNumber', 'users'));

And in our phone numbers "add" view (src/Templates/PhoneNumbers/add.ctp) we have a field to select the associated user.

<?php echo $this->Form->create($phoneNumber);
    echo $this->Form->input('user_id');

CakePHP will automatically populate the user_id field with the list of users stored in $users.

2. Save Associated Records Discretely part 2

An immediate issue with the method above is you are forced to select the user every time you add a phone number since the user is a required field in our example. To simplify this, you can view a user, then have an "add" link there take you to your phone number add() function, passing the user_id.

In the users "view" view (/src/Template/Users/view.ctp) we add the link:

<h4><?php echo __('Phone Numbers'); ?></h4>
<?php echo $this->Html->link(__('Add Phone Number'), ['controller' => 'PhoneNumbers', 'action' => 'add', $user->id]); ?>

Then we modify the phone numbers add() function in the controller (src/Controller/PhoneNumbersController.php) to accept the user_id and pass it to the view.

public function add($user_id = null)
   $this->set(compact('phoneNumber', 'users', 'user_id'));

And lastly we set the value of the user_id in the "add" view (src/Templates/PhoneNumbers/add.ctp).

echo $this->Form->input('user_id', ['value' => $user_id]);

Now we can add phone numbers by selecting the user from the list, or by going directly from the user view and pre-selecting the user.

3. Adding Phone Numbers from the User view

We can also add associated records directly from the current model.

First, we add the fields to the users "add" view (src/Template/Users/add.ctp) to add the phone numbers.

<h4><?php echo __('Phone Numbers'); ?></h4>
        <td><?php echo $this->Form->input('phone_number.type', ['options' => ['H' => 'H', 'W' => 'W', 'M' => 'M']]); ?></td>
        <td><?php echo $this->Form->input('phone_number.number'); ?></td>

When you save your user, CakePHP will automatically save one level of associated data with the new user_id as long as the conventions are followed.

Next: Autocomplete


Submitted by marlon on Sat, 11/12/2016 - 04:03


Hello Naidim,

thank you for you're great tutorials. Realy a nice job, learning a lot from it.

Got some error with the user/view part. Does is needs to be defined in the user controller?

<?php echo $this->Html->link(__('Add Phone Number'), ['controller' => 'PhoneNumbers', 'action' => 'add', $user_id]); ?>

Notice (8): Undefined variable: user_id [APP/Template/Users/view.ctp, line 83]

Submitted by Dwayne on Mon, 11/28/2016 - 12:20


How would the code look like, if I have a dedicated form for "phone number" on the user page?

In my case it's a comment to a certain article. The associations are set properly. Article hasMany Comment and Comment belongsTo Article.

I couldn't find a tutorial explaining such behaviour, although the scenario seems to be quite common (like for a blog).

Somehow I managed it to save new comments with my form, but only if all form data was "valid" according to the comment model. Plus I didn't even know, how the form could work, since the entity within the form->create was not set properly.
The validation doesn't work, if one of the form inputs is invalid.

Could you give me a hint on where to have a look into (in the documentation)? I suppose my solution is to be found here, however I am not sure, if I am missing something, since the examples are quite short rather than detailed (at least in my opinion).

Thanks :)

Add new comment