# Saving BelongsTo Associations (CakePHP 5.1)

**URL:** <https://discourse.cakephp.org/t/saving-belongsto-associations-cakephp-5-1/12612>\
**Category:** Need Help\
**Tags:** release, behavior\
**Created:** [July 7, 2025, 10:59am UTC](https://discourse.cakephp.org/t/saving-belongsto-associations-cakephp-5-1/12612 "2025-07-07T10:59:29Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Benj](https://avatars.discourse-cdn.com/v4/letter/b/919ad9/32.png) [@Benj](https://discourse.cakephp.org/u/Benj)\
**Post date:** [July 7, 2025, 10:59am UTC](https://discourse.cakephp.org/t/saving-belongsto-associations-cakephp-5-1/12612/1 "2025-07-07T10:59:29Z")

</div>

Hello,  
I have a behavior problem when saving a belongsTo association.

In my project I have a missions table and a contacts table with :

```auto
// MissionsTable.php
$this->belongsTo('Contacts')
	->setForeignKey('contact_id');

// missions
// - id
// - contact_id
// - ...

// contacts
// - id
// - firstname
// - lastname
// - email
// - ...

```

I would like to be able to add the contact and modify it if necessary. For example, to be able to add the contact’s email if it is not filled in on the existing entity. So I can’t just edit `$mission->contact_id`

My problem occurs when I edit a mission that has no contact and add an existing contact.  
Saving will create a new contact entity despite the presence of the ID in the data.

```auto
// MissionsController.php
public function test($id)
{
	$associated = ['Contacts'];
        
	$mission = $this->Missions->get($id, contain: $associated);
	// $mission->contact = null, because $mission->contact_id = null

	$data = [
		// ...
		'contact' => [
			'id' => '1',
			'firstname' => 'John',
			'lastname' => 'Doe',
			'email' => 'john.doe@example.com',
		]
	];
	
	$mission = $this->Missions->patchEntity($mission, $data, ['associated' => $associated]);
	debug($mission);
	die;
}

```

`debug($mission)` result :

```auto
object(App\Model\Entity\Mission) id:0 {
 	'id' => (int) 1
	...
 	'contact' => object(App\Model\Entity\Contact) id:1 {
 		'firstname' => 'John'
 		'lastname' => 'Doe'
 		'email' => 'john.doe@example.com'
 		'[new]' => true
		...
 	}
 	'[new]' => false
}

```

We can see that after the patchEntity(), the contact is a new entity despite the presence of the id in the data.

I found some solutions to my problem, but I’m wondering if the behavior is normal or if there would be another way to not create a new entity with the id in the data.

Looking at the code of `\Cake\ORM\Marshaller.php`

1. The ‘contact’ field launches the `_mergeAssociation()` callback
2. If the original is null (here, `if $mission->contact == null`) `_mergeAssociation()` returns to `_marshalAssociation()`, then `one()` because the association type is ‘manyToOne’.
3. In `one()` method, a new entity is created without checking the presence of the primary key in `$data`.

**Solution 1 : Edit the Core ORM Marshaller**

In `_mergeAssociation()`, shouldn’t we check for the presence of the primary key in $value before redirecting to `_marshalAssociation()` which will create a new entity?  
Or perhaps upstream, in `_buildPropertyMap()` shouldn’t we check for the presence of the primary key in $value to set the $original?

```auto
// cakephp/src/ORM/Marshaller.php
protected function _buildPropertyMap(array $data, array $options): array
{
	// ...
	if (isset($options['isMerge'])) {
		$callback = function (
			$value,
			EntityInterface $entity,
		) use (
			$assoc,
			$nested,
		): array|EntityInterface|null {
			$options = $nested + ['associated' => [], 'association' => $assoc];
			// edit part
			if (isset($value[$assoc->getBindingKey()])) $original = $this->_table->{$assoc->getClassName()}->get($value[$assoc->getBindingKey()]);
			else $original = $entity->get($assoc->getProperty());

			return $this->_mergeAssociation($original, $assoc, $value, $options);
			// end edit part
	} else {
		// ...
	}
	// ...
}

```

With the above modification, the behavior reacts well as I want.

**Solution 2 : Edit associated entity in afterMarshal()**

Otherwise, for now, what I can do is act in `MissionsTable::afterMarshal()`  
Check for the presence of the id in $data and replace the contact entity.  
But I need to do this for each belongsTo association.

```auto
// MissionsTable.php
public function afterMarshal(
	EventInterface $event,
	EntityInterface $entity,
	ArrayObject $data,
	ArrayObject $options
): void {
	// belongsTo Contacts
	if ($entity->isDirty('contact') && isset($data['contact']['id']) && !empty($data['contact']['id'])) {
		// we do not create a new entity
		$contact_entity = $this->Contacts->get($data['contact']['id']);
		$contact_entity = $this->Contacts->patchEntity($contact_entity, $data['contact']);
		$entity->contact = $contact_entity;
	}
}

```

Thanks for reading

---

<div class="post-metadata">

**Author:** ![jmcd73](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/jmcd73/32/481_2.png) [@jmcd73](https://discourse.cakephp.org/u/jmcd73)\
**Post date:** [July 8, 2025, 2:05am UTC](https://discourse.cakephp.org/t/saving-belongsto-associations-cakephp-5-1/12612/2 "2025-07-08T02:05:43Z")

</div>

This will allow you to update the Contact:

```php
        $mission = $this->Missions->patchEntity($mission, $data, [
            'associated' =>
            [
                'Contacts' => [
                    'accessibleFields' => ['id' => true]
                ]
            ]
        ]);

```

> **[Saving Data - 5.x](https://book.cakephp.org/5/en/orm/saving-data.html#changing-accessible-fields)**

---

<div class="post-metadata">

**Author:** ![Benj](https://avatars.discourse-cdn.com/v4/letter/b/919ad9/32.png) [@Benj](https://discourse.cakephp.org/u/Benj)\
**Post date:** [July 8, 2025, 10:39am UTC](https://discourse.cakephp.org/t/saving-belongsto-associations-cakephp-5-1/12612/3 "2025-07-08T10:39:34Z")

</div>

It seemed to me that there was certainly a simpler way 🙂  
THANKS !

I had tried to put the contact ID accessible in `Model/Entity/Contact.php`  
but I still saw `[new] => true` on the contact entity after the patchEntity and I didn’t go any further.

But indeed, after saving, `$this->Missions->save($mission);`  
the `$mission->contact` entity has kept the id and we have `[new] => false`.

Thanks for help
