cakephp 3 save empty belongsToMany relation - cakephp

I have a Shops table which can have Products. Other Shops can have the same Products so I used a belongsToMany relation table ShopsProducts.
I can add an infinite number of Products to a Shop and remove them by saving the Shop entity including the relation.
All works fine, but if I want to unlink all Products from a Shop in my form and save, the relation is of cause empty so the Shop will always have 1 Product that I can't delete over the relation but only directly.
This is what the request looks like from the Shops form with a Product
data => [
'name' => 'some',
'is_active' => '1',
'slug' => 'some',
'product_id' => '',
'products' => [
(int) 5 => [
'id' => '5',
'_joinData' => [
'priority' => '0'
]
]
],
]
And this is the request without
data => [
'name' => 'some',
'is_active' => '1',
'slug' => 'some',
'product_id' => '',
]
What is the cake way to handle this issue?

What's your save strategy?
https://book.cakephp.org/3.0/en/orm/saving-data.html#saving-belongstomany-associations
Try replace instead of append.
If that doesn't work for you, check in the before() or afterSave() if products->get('products') is empty, if it is manually call a deleteAll() on the join table for that product an shop.

You will need to set your association to [] to blank it out.
$shops->products = [];
$shops->setDirty('products ', true);

Related

Unable to save associated (hasOne) data

I am creating a simple API with CakePHP 4, and I am having some issues saving some POST data. This is a new project, so the models are newly baked, based on my 3 tables: elaborations, details, jobs
The ElaborationTable table has these relations:
$this->hasOne('Details', [ // this (and the next) is the only thing that I've manually changed here: hasOne instead of hasMany
'foreignKey' => 'elaboration_id',
]);
$this->hasOne('Jobs', [
'foreignKey' => 'elaboration_id',
]);
While both DetailsTable and JobsTable have this:
$this->belongsTo('Elaborations', [
'foreignKey' => 'elaboration_id',
'joinType' => 'INNER',
]);
In my controller, this is what I'm using to retrieve the POST data and save it to each database table:
$data = $this->request->getData();
$data['user_id'] = $this->Auth->user('id');
$elaboration = $this->Elaborations->newEntity($data, [
'associated' => [
'Details',
'Jobs'
]
]);
Finally, my POST data is something like this:
APP/Controller/ElaborationsController.php (line 85)
[
'detail' => [
'regione' => 'Lazio',
'provincia' => 'RM',
'comune' => 'Roma',
],
'job' => [
'serramenti' => '1',
],
'user_id' => (int) 2,
]
Now, the user_id field gets saved in my elaborations table, no errors are issued, but still the details and jobs tables remains empty, no data is saved.
What am I doing wrong?

CakePHP How to Get Associated Data and COUNT/GROUP BY

I'm having some trouble with Cakephp Query builder and try to get results I'm looking for. I have three tables 'Users', 'Items', 'Owns', where Owns belongs to both 'Users' and 'Items'.... A user can own many items (and many of the same items).
So I'm wanting to return the "owned" items by a user along with the count... However whenever I add count into my query I lose the associated Items data.
example - this returns the user data along with associated owns data and associated items data as shown below the query
$owns = $this->Users->get($id, [
'contain' => [
'Owns' => function($q) { return $q->find('all')->group(['Owns.item_id']); },
'Owns.Items'
],
]);
SQL Generated from this is
SELECT
Owns.id AS Owns__id,
Owns.item_id AS Owns__item_id,
Owns.user_id AS Owns__user_id,
Items.id AS Items__id,
Items.name AS Items__name,
Items.description AS Items__description,
Items.created AS Items__created,
Items.modified AS Items__modified
FROM
owns Owns
INNER JOIN items Items ON Items.id = (Owns.item_id)
WHERE
Owns.user_id in (1)
GROUP BY
Owns.item_id
Data results look like this:
'owns' => [
'id' => (int) 1,
'username' => 'myusername',
'owns' => [
(int) 0 => object(App\Model\Entity\Own) id:0 {
'id' => (int) 2
'item_id' => (int) 1
'user_id' => (int) 1
'item' => object(App\Model\Entity\Item) id:1 {
'id' => (int) 1
'name' => 'Item Name'
'description' => 'Item Description'
However in the 'owns' query I want to add in the count (i.e. the number owned). I can get the count by using this query but then I lose the associated item object from my results. I've tried this many different ways but always seems that if I want to use SQL count I can't get the associated data.
$owns2 = $this->Users->get($id, [
'contain' => [
'Owns' => function($q) { return $q->select(['count' => $q->func()->count('Owns.id'),'Owns.id', 'Owns.user_id', 'Owns.item_id'])->group(['Owns.item_id']); },
'Owns.Items'
],
]);
SQL Generated from this is
SELECT
(COUNT(Owns.id)) AS count,
Owns.id AS Owns__id,
Owns.user_id AS Owns__user_id,
Owns.item_id AS Owns__item_id
FROM
owns Owns
INNER JOIN items Items ON Items.id = (Owns.item_id)
WHERE
Owns.user_id in (1)
GROUP BY
Owns.item_id
Data results look like this:
'owns2' => [
'id' => (int) 1,
'username' => 'myusername',
'owns' => [
(int) 0 => object(App\Model\Entity\Own) id:36 {
'count' => (int) 4
'id' => (int) 2
'user_id' => (int) 1
'item_id' => (int) 1
Any insight into how I can get "count" into the first $user query or the associated items into the $user2 would be appreciated.
To keep the association table's fields that are returned in the $owns = find('all') query along with the SQL count function I needed to add the associated fields into the select statement
$owns2 = $this->Users->get($id, [
'contain' => [
'Owns' => function($q) { return $q->select(['count' => $q->func()->count('Owns.id'),'Owns.id', 'Owns.item_id','Owns.user_id', 'Items.id', 'Items.collection_id','Items.name', 'Items.description'])->group(['Owns.item_id']); },
'Owns.Items'
],
]);

CakePHP 3.x OR In Find Condition With Multiple Same Fields

I would like to query the database for
Plates.page_number => 1 OR Plates.page_number => Cover
I am attempting to use the following code, but I am not getting the results I am looking for because of a duplicate array key, how can I search the same field for two different values?
$query = $this->Ledgers->find('all', array(
'contain' => array(
'Plates' => [
'conditions' => [
'OR' => [
'Plates.plate_title' => 'Front Cover',
'Plates.page_number' => '1',
'Plates.page_number' => 'Cover' // Duplicate Array Key
]
]
], 'Plates.PlateImages', 'Tribes'
),
'conditions' => array(
'Ledgers.disabled' => 'n', 'Ledgers.id IN' => $ledgerIds
)
))->orderAsc('ledger_title');
Please try wrapping your conditions in separate arrays, eg:
'OR' => [
['Plates.page_number' => '1'],
['Plates.page_number' => 'cover'],
...
]
More info can be found in CakePHP docs:
Query Builder -> Advanced Conditions

CakePHP 3: How to update foreignKey?

I use BlueImp jQuery plugin to upload multiple images in follow scenario:
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.
Ajax return id of recent uploaded image, and js create (captions, position) inputs for that file.
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
Q: How to update foreignKey?
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
}
}

CakePHP Save data in HABTM Table

I am trying to save an array like this
$myData = array(
'User' => array('id' => 17),
'Group' => array(
array('group_id' => 2),
array('group_id' => 3),
array('group_id' => 4),
array('group_id' => 5),
array('group_id' => 6)
)
);
In my HABTM join table (groups_users). I tried the following save calls, but none of them worked.
$this->User->save($myData);
$this->User->saveAssociated($myData);
$this->User->saveAll($myData);
$this->User->GroupsUsers->save($myData);
$this->User->GroupsUsers->saveAll($myData);
Before you ask: Yes, my associations are set-up correctly and I was able to save data by calling:
$this->User->GroupsUsers->saveAll(array(
0 => array(
'GroupsUsers' => array('user_id' => $id, 'group_id' => 1)
),
1 => array(
'GroupsUsers' => array('user_id' => $id, 'group_id' => 2)
)
));
BUT only one of both records are saved, although I set unique to false in the model's HABTM relationship definition.
Where is the error? Is the structure of my array invalid?
The problem is that your data array is incorrectly formatted.
Your Group model will probably have an id field, and your join model GroupsUser will have a group_id field.
So you need to change your group_id to id
try
$myData = array(
'User' => array('id' => 17),
'Group' => array(
'Group' => array(
0 => 2,
1 => 3,
2 => 4,
3 => 5,
4 => 6
)
)
);
Just to answer your question even if it's old.
You doesn't have to call your 'GroupsUsers' model in your saveAll call, this is precisely the goal of associating Models, cakePHP does that for you.
You should pass this array to a saveAll function on your User/Group model:
array{
array{
'User' => array {
'id' => 25
},
'Group' => array {
'id' => 2
}
}
array{
'User' => array {
'id' => 47
},
'Group' => array {
'id' => 2
}
}
}
Both your User and Group Models should have and HABTM relation with each other, so that you just have to do that:
From the User/Group controller:
$this->User/Group->SaveAll($myData);
From the User/Group Model:
$this->SaveAll($myData);
And that's it, the GroupsUsers table will save 2 records, by saving by yourself in the HABTM relation table, you are not using the power of cakePHP.
And the records in both User and Group tables will be created if the ID doesn't exist, updated if not.
The only reason to save in an HABTM table is if you have some extra information you want to save in this table such as a 'validated' fields if you want to validate a member after he asked to join a group for example.

Resources