CakePHP 3 problem with update associated data

I use BlueImp jQuery plugin to upload multiple images in follow scenario:

  1. User open add or edit Albums page, select images and procces upload via ajax method. This work fine, image data (name, size, model, field,…) are stored in related images table without foreign key.

  2. Ajax return id of recent uploaded image, and js create (captions, position) inputs for that file.

  3. User fill other form fields, and click submit.

Problem, if user create new album or edit/update exist one wich not contain images, application does not update foriegn key for new images. But if album contain images, and user add new one to album, after save post cakephp add / update foreign key to new images records.

Debug output:

// first time add images, does not update FK
[
	'name' => 'A4',
	'images' => [
		(int) 116 => [
			'caption' => '',
			'position' => '1',
			'id' => '116'
		]
	],
	'id' => '4',
	'user_id' => '1',
	'active' => '1'
]

// album has one image, we add new one, CakePHP Update FK for new images
[
	'name' => 'A4',
	'images' => [
		(int) 117 => [
			'caption' => '',
			'position' => '1',
			'id' => '117'
		],
		(int) 118 => [
			'caption' => '',
			'position' => '2',
			'id' => '118'
		]
	],
	'id' => '4',
	'user_id' => '1',
	'active' => '1'
]

AlbumsController add method:

public function add()
{
    $album = $this->Albums->newEntity();
    if ($this->request->is('post')) {
        $album = $this->Albums->patchEntity($album, $this->request->data,['associated' => ['Images']]);
        if ($this->Albums->save($album)) {
            $this->Flash->success(__('The album has been saved.'));
            return $this->redirect(['action' => 'index']);
        } else {
            $this->Flash->error(__('The album could not be saved. Please, try again.'));
        }
    }
    $users = $this->Albums->Users->find('list', ['limit' => 200]);
    $this->set(compact('album', 'users'));
    $this->set('_serialize', ['album']);
}

Albums Table:

    $this->hasMany('Images', [
        'foreignKey' => 'foreign_key',
        'conditions' => [
            'Images.model' => 'Albums',
            'Images.field' => 'upload'
        ],
        'sort' => ['Images.position'  => 'ASC']
    ]);

Images Table:

    $this->belongsTo('Albums', [
        'foreignKey' => 'foreign_key',
        'joinType' => 'INNER'
    ]);

I use same approach in the cakephp 2 apps, and work without problems.

Video: http://screencast-o-matic.com/watch/cDhYYOinCX

Debug request data, Entity, and save.

debug($this->request->data);

[
	'name' => 'zzzzzzzzzzzz',
	'description' => '<p>sdgfsdgsdgsdg</p>',
	'_wysihtml5_mode' => '1',
	'images' => [
		(int) 131 => [
			'caption' => '',
			'position' => '1',
			'id' => '131'
		]
	],
	'id' => '16',
	'user_id' => '1',
	'active' => '1'
]

//

$album = $this->Albums->patchEntity(
    $album,
    $this->request->data,
    ['associated' => ['Images' => ['accessibleFields' => ['id' => true, 'foreign_key' => true]]]]
);
debug($album);

//

  object(App\Model\Entity\Album) {

	'id' => (int) 16,
	'user_id' => (int) 1,
	'name' => 'zzzzzzzzzzzz',
	'description' => '<p>sdgfsdgsdgsdg</p>',
	'created' => object(Cake\I18n\Time) {

		'time' => '2016-05-21T09:42:43+02:00',
		'timezone' => 'Europe/Berlin',
		'fixedNowTime' => false
	
	},
	'modified' => object(Cake\I18n\Time) {

		'time' => '2016-05-21T09:50:11+02:00',
		'timezone' => 'Europe/Berlin',
		'fixedNowTime' => false
	
	},
	'active' => true,
	'images' => [
		(int) 0 => object(App\Model\Entity\Image) {

			'caption' => '',
			'position' => (int) 1,
			'id' => (int) 131,
			'[new]' => true,
			'[accessible]' => [
				'*' => true,
				'id' => true,
				'foreign_key' => true
			],
			'[dirty]' => [
				'caption' => true,
				'position' => true,
				'id' => true
			],
			'[original]' => [],
			'[virtual]' => [],
			'[errors]' => [],
			'[invalid]' => [],
			'[repository]' => 'Images'
		
		}
	],
	'_wysihtml5_mode' => '1',
	'[new]' => false,
	'[accessible]' => [
		'*' => true
	],
	'[dirty]' => [
		'_wysihtml5_mode' => true,
		'images' => true
	],
	'[original]' => [
		'images' => []
	],
	'[virtual]' => [],
	'[errors]' => [],
	'[invalid]' => [],
	'[repository]' => 'Albums'

}

//

$s = $this->Albums->save($album);
debug($s);

object(App\Model\Entity\Album) {

	'id' => (int) 16,
	'user_id' => (int) 1,
	'name' => 'zzzzzzzzzzzz',
	'description' => '<p>sdgfsdgsdgsdg</p>',
	'created' => object(Cake\I18n\Time) {

		'time' => '2016-05-21T09:42:43+02:00',
		'timezone' => 'Europe/Berlin',
		'fixedNowTime' => false
	
	},
	'modified' => object(Cake\I18n\Time) {

		'time' => '2016-05-21T09:51:51+02:00',
		'timezone' => 'Europe/Berlin',
		'fixedNowTime' => false
	
	},
	'active' => true,
	'images' => [
		(int) 0 => object(App\Model\Entity\Image) {

			'caption' => '',
			'position' => (int) 1,
			'id' => (int) 131,
			'foreign_key' => (int) 16,
			'modified' => object(Cake\I18n\Time) {

				'time' => '2016-05-21T09:51:51+02:00',
				'timezone' => 'Europe/Berlin',
				'fixedNowTime' => false
			
			},
			'[new]' => false,
			'[accessible]' => [
				'*' => true,
				'id' => true,
				'foreign_key' => true
			],
			'[dirty]' => [
				'caption' => true,
				'position' => true,
				'id' => true,
				'foreign_key' => true,
				'modified' => true
			],
			'[original]' => [],
			'[virtual]' => [],
			'[errors]' => [],
			'[invalid]' => [],
			'[repository]' => 'Images'
		
		}
	],
	'_wysihtml5_mode' => '1',
	'[new]' => false,
	'[accessible]' => [
		'*' => true
	],
	'[dirty]' => [],
	'[original]' => [
		'images' => [],
		'modified' => object(Cake\I18n\Time) {

			'time' => '2016-05-21T09:50:11+02:00',
			'timezone' => 'Europe/Berlin',
			'fixedNowTime' => false
		
		}
	],
	'[virtual]' => [],
	'[errors]' => [],
	'[invalid]' => [],
	'[repository]' => 'Albums'

}

I’m not sure is this correct way, but it’s work for me.

AlbumsTable

public function afterSave(Event $event, Entity $entity, ArrayObject $options)
{
   if (isset($entity)) {
        $images = TableRegistry::get('Images');
        $images->query()
            ->update()
            ->set(['foreign_key' => $entity->id])
            ->where([
                'foreign_key IS NULL',
                'model' => 'Albums'
                ])
            ->execute();// conditions
           }
}