BelongsToMany, duplicate entries (solved)

Hi, I’m developing my app with Cakephp 3.5.11, the current version

I have the tables sell_regions (inside plugin Sell), countries and provinces (outside plugins), and these are its Tables:
plugins/Sell/Model/Table/RegionsTable.php:
class RegionsTable extends Table {
public function initialize(array $config) {
parent::initialize($config);

	$this->setTable('sell_regions');

	$this->belongsToMany('Countries', [
		'joinTable' => 'sell_countries_regions'])
		->setForeignKey('region_id');
	$this->belongsToMany('Provinces', [
		'joinTable' => 'sell_provinces_regions'])
		->setForeignKey('region_id');

	$this->addBehavior('Timestamp');
}

src/Model/Table/CountriesTable.php:
class CountriesTable extends Table {
public function initialize(array $config) {
parent::initialize($config);

	$this->hasMany('Provinces')
		->setDependent(true)
		->setCascadeCallbacks(true)
		->setSort(['Provinces.title' => 'asc']);
	$this->belongsToMany('Sell.Regions', [
		'joinTable' => 'sell_countries_regions'])
		->setTargetForeignKey('region_id');

	$this->addBehavior('Timestamp');
}

src/Model/Table/ProvincesTable.php:
class ProvincesTable extends Table {
public function initialize(array $config) {
parent::initialize($config);

	$this->belongsTo('Countries');
	$this->belongsToMany('Sell.Regions', [
		'joinTable' => 'sell_provinces_regions'])
		->setTargetForeignKey('region_id');

	$this->addBehavior('Timestamp');
}

plugins/Sell/Template/Regions/edit.ctp:
<?= $this->Form->create($region);?>
<?= $this->Form->control('countries._ids', ['label' => __('Countries')]);?>
<?= $this->Form->control('provinces._ids', ['label' => __('Provinces')]);?>
<?= $this->Form->control('title', ['label' => __('Title')]);?>
<?= $this->Form->button(__('Save changes'), ['type' => 'submit']);?>
<?= $this->Form->end();?>

plugins/Sell/Controller/RegionsController.php:
public function edit($id = null) {
$region = $this->Regions->get($id, [
‘contain’ => [
‘Countries’ => function (\Cake\ORM\Query $query) { return $query->select([‘id’]); },
‘Provinces’ => function (\Cake\ORM\Query $query) { return $query->select([‘id’]); }
]
]);
if ($this->request->is([‘patch’, ‘post’, ‘put’])) {
$region = $this->Regions->patchEntity($region, $this->request->getData());
debug($region);
if ($this->Regions->save($region)) {
$this->Flash->success((‘Changes saved’));
return $this->redirect([‘action’ => ‘view’, $region->id]);
} else {
$this->Flash->error(
(‘Changes not saved. Please, try again.’));
//$this->Flash->error(json_encode($region->errors()));
}
}
$countries = $this->Regions->Countries->find(‘list’, [‘order’ => ‘title’]);
$provinces = $this->Regions->Provinces->find(‘list’, [‘order’ => ‘title’]);
$this->set(compact(‘region’, ‘countries’, ‘provinces’));
}

debug($region) shows:
object(Ventas\Model\Entity\Region) {

'id' => '779da13e-3ea4-428b-97b3-c0d8b34c47ce',
'title' => 'Near',
'created' => object(Cake\I18n\FrozenTime) {

	'time' => '2018-01-23T15:28:55+01:00',
	'timezone' => 'Europe/Madrid',
	'fixedNowTime' => false

},
'modified' => object(Cake\I18n\FrozenTime) {

	'time' => '2018-02-06T14:04:47+01:00',
	'timezone' => 'Europe/Madrid',
	'fixedNowTime' => false

},
'provinces' => [
	(int) 0 => object(App\Model\Entity\Province) {

		'id' => (int) 15,
		'title' => 'A Coruña',
		'pais_id' => (int) 73,
		'hc_key' => 'es-c',
		'created' => object(Cake\I18n\FrozenTime) {

			'time' => '1970-01-01T00:00:00+01:00',
			'timezone' => 'Europe/Madrid',
			'fixedNowTime' => false
		
		},
		'modified' => object(Cake\I18n\FrozenTime) {

			'time' => '1970-01-01T00:00:00+01:00',
			'timezone' => 'Europe/Madrid',
			'fixedNowTime' => false
		
		},
		'[new]' => false,
		'[accessible]' => [
			'*' => true,
			'id' => false
		],
		'[dirty]' => [],
		'[original]' => [],
		'[virtual]' => [],
		'[errors]' => [],
		'[invalid]' => [],
		'[repository]' => 'Provinces'
	
	}
],
'countries' => [
	(int) 0 => object(App\Model\Entity\Country) {

		'id' => (int) 2,
		'title' => 'Afganistán',
		'isonum' => (int) 4,
		'iso2' => 'AF',
		'iso3' => 'AFG',
		'created' => object(Cake\I18n\FrozenTime) {

			'time' => '1970-01-01T00:00:00+01:00',
			'timezone' => 'Europe/Madrid',
			'fixedNowTime' => false
		
		},
		'modified' => object(Cake\I18n\FrozenTime) {

			'time' => '1970-01-01T00:00:00+01:00',
			'timezone' => 'Europe/Madrid',
			'fixedNowTime' => false
		
		},
		'[new]' => false,
		'[accessible]' => [
			'*' => true,
			'id' => false
		],
		'[dirty]' => [],
		'[original]' => [],
		'[virtual]' => [],
		'[errors]' => [],
		'[invalid]' => [],
		'[repository]' => 'Countries'
	
	}
],
'[new]' => false,
'[accessible]' => [
	'*' => true,
	'id' => false
],
'[dirty]' => [
	'countries' => true,
	'provinces' => true
],
'[original]' => [
	'countries' => [
		(int) 0 => object(App\Model\Entity\Country) {

			'id' => (int) 2,
			'_joinData' => object(Cake\ORM\Entity) {

				'id' => 'd78a21ca-9d36-47dc-82ba-c8c1d2de18ae',
				'country_id' => '2',
				'region_id' => '779da13e-3ea4-428b-97b3-c0d8b34c47ce',
				'[new]' => false,
				'[accessible]' => [
					'*' => true
				],
				'[dirty]' => [],
				'[original]' => [],
				'[virtual]' => [],
				'[errors]' => [],
				'[invalid]' => [],
				'[repository]' => 'SellCountriesRegions'
			
			},
			'[new]' => false,
			'[accessible]' => [
				'*' => true,
				'id' => false
			],
			'[dirty]' => [],
			'[original]' => [],
			'[virtual]' => [],
			'[errors]' => [],
			'[invalid]' => [],
			'[repository]' => 'Countries'
		
		}
	],
	'provinces' => [
		(int) 0 => object(App\Model\Entity\Province) {

			'id' => (int) 15,
			'_joinData' => object(Cake\ORM\Entity) {

				'id' => '59c875cc-6485-4947-bf05-8912017664ee',
				'province_id' => '15',
				'region_id' => '779da13e-3ea4-428b-97b3-c0d8b34c47ce',
				'[new]' => false,
				'[accessible]' => [
					'*' => true
				],
				'[dirty]' => [],
				'[original]' => [],
				'[virtual]' => [],
				'[errors]' => [],
				'[invalid]' => [],
				'[repository]' => 'SellProvincesRegions'
			
			},
			'[new]' => false,
			'[accessible]' => [
				'*' => true,
				'id' => false
			],
			'[dirty]' => [],
			'[original]' => [],
			'[virtual]' => [],
			'[errors]' => [],
			'[invalid]' => [],
			'[repository]' => 'Provinces'
		
		}
	]
],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Sell.Regions'

}

When I edit a Region and I dont change any Country or Province, app creates a new registry in SellCountriesRegions or SellProvincesRegions.

I`ve created many associations like these in other apps without problems. But this time, I don’t find the error or problem.

Hey.

As you can see in you debug-output of $region, both fields (countries and provinces) as marked as dirty, which is why I think you should have a look at the saveStrategy (which is replace by default) See ‘Saving BelongsToMany Assoc’ in order to resolve your issue.

Best regards

Fields have to be marked as dirty.

I finally found the problem, after all day looking for it. Mini error. All primary keys (id) are uuid’s, but only country and province have integers as id.

In sell_countries_regions and sell_provinces_regions I build foreign key country_id and province_id as uuid :frowning: :frowning:

Thanks for the help, it estimulates me to find the problem