Delete a belongsTo Association - cakephp

There's surprisingly little information to be found about this and I guess I've never run into it before, but I'm in a situation where I have a model with a belongsTo relationship. The model is the key and when I delete a record, I need to ensure that its associated record is also deleted.
Specifically, I have a Building model that belongsTo an Address. When I delete the building, I need to be sure that the associated address is also deleted.
I can't flag the association as dependent, of course, so is a callback the best way to ensure that the address record gets deleted or is there a better way? This is one of those cases where I know I can do it with a callback, but at a visceral level, it seems like there should be a better way. I'm wondering whether that's the case.
Thanks.

why not using Foreign Keys in the database and select on DELETE CASCADE and let the database do the work...
[Based on the comment] if you the Address is attached to other models that you dont want to delete, you can set those FK to ON DELETE RESTRICT and the building wont be deleted.
And if you need something more complex you can add the beforeDelete() callback in your model, there's an example in the doc
Good Luck

In cakephp 3.x you can use the unlink method like described here:
http://api.cakephp.org/3.3/class-Cake.ORM.Association.HasMany.html#_unlink
This would set the foreign key field in the database to null
In your case it should be something like this everytime when you want to delete records from buildings so integrate it in your delete function. Of course it requires properly associations in the models.
$address = $this->Addresses->get('address_id'):
$building = $this->Buildings->find()->where(['building_id' => 'some_id'])->toArray();
if ($this->Addresses->association('Buildings')->unlink($address, $building)) {
$this->Flash->success(__('Success!'));
return $this->redirect($this->referer());
} else {
$this->Flash->error(__('Error.'));
return $this->redirect($this->referer());
}

Related

Saving a user in CakePHP with ACL group name specified rather than group id

I have set up a Cake 2.3.1 app with ACL following this tutorial so I have a users table and a groups table.
Everything is working fine, but I'm a little confused about how I should go about saving new users since I don't want the user to be able to select their own group. The group will be chosen based on a number of things that the user inputs on the form so it will be determined by conditional statements in the controller.
At the moment I'm doing this:
$this->request->data['User']['group_id'] = '2'; (or 1, 3 or 4 depending on conditional statements in the controller).
Then, I'm doing a save on $this->data. This works but I'd like to be able to specify the group name rather than hardcode the id so that when we move to production and clear the database, it won't matter if the groups are added in a different order.
I see two ways of doing this.
The first I haven't tried, but it would be to change the primary key of the group table to the name of the group and set the primaryKey variable in the Group model and all subsequent reference and associations with this table in all the other models. That would probably bring you more than a headache and I wouldn't recommend it because you'd probably break the ACL component one way or another.
The other way is what I'd do, and it requires a bit of programming on your part (Cake doesn't have an easy way of doing this that I know of).
In the User model, put a beforeSave and in that function, create a dummy variable with the group name, do the lookup of the name and insert the correct id, and delete that variable.
On User.ctp model
public function beforeSave() {
if (isset($this->data[$this->alias]['group_name'])) {
//do the lookup with the group_name
$group = $this->Group->find('first', array('conditions'=>array('name'=>$this->data[$this->alias]['group_name'])));
if (count($group) <= 0)
//... throw an Exception or handle any way you want this case
$real_group_id = $group['Group']['id'];
$this->data[$this->alias]['group_id'] = $real_group_id;
unset($this->data[$this->alias]['group_name']);
}
return true; //remember this is important! otherwise the save will stop
}
When you want to change the user's group or add another user, just do this in the controller
$data_to_save['User']['group_name'] = 'webuser';
and the beforeSave function will take care of the rest.
I realize you asked for a Cake-easy-way of doing this, but I guess there's none (not sure though, feel free to correct me), the beforeSave function could save you a lot of repetitive code on the controllers. The only thing you'd have to be careful when clearing the database or changing group ids is to reassociate them correctly with the users already in the database, but since is a "moving to production" change, those users already predefined should be few or none.

CakePHP model association based on field values?

I've got three tables (there's actually several more, but I only need the three for this problem). Applications, Appattrs and Appcats. In CakePHP parlance (as best as I can since I'm still learning the framenwork) Applications hasMany Appattrs and Appattrs belongsTo Applications. Easy.
The problem comes when I want to associate Appattrs and Appcat - the association is predicated on a field value and a corresponding foreign key in Appattrs. For instance:
If appattrs.type = 'appcatid' then appattrs.value would point to a record in the Appcat table.
The appattrs table holds static data appattrs.type='dateadded' and value='201201011300' as well as foreign key references. I'd rather not get into a discussion as to why data is stored this way, I just want to figure out how to create associations that will let me pull an application record, the associated attr records and then an attr record with its associated row from the appropriate table. Dynamically.
It seems to me that I should be able to create a model based on a query and then associate that model - I just can't seem to figure out how to do that.
--
If I need to post schema for the three tables, I can. I can also post my current model code, but honestly, right now it's just association variables so I don't think it'll get anyone anywhere.
Thow I do not understand the logic behind this design, I thing what you are looking for
is Creating and Destroying associations on the fly.
On this section of CakePHP Docs, it describes how you can associate models from within the corresponding controller.
So, for example, when you want to save specific data to Appattr model you can do some data checking and create your association using bind() method.
A very abstract approach to the above would be something like this
public function yourmethod() {
...
if ($this->request->data['Appattr']['type'] == 'sometype') {
$this->Appattr->bindModel(
array(/*Your association*/ => array(/* Your attributes...*/)
);
/* Rest of the logic follows */
}
}
This way you get your job done, but it's very possible to end up having very complicated
data in your database and thus having very complicated code.
I hope this helps

Deleting association from HABTM table

I have a Users and a Reports table, connected with HABTM relationship.
I can save a report that creates a correct record in reports_users table, I can view the relationship table by using $this->User->ReportsUser->.., and so on.
Now I want to delete a specific row in reports_users table, but I can't seem to do it.
I have tried the following:
$this->User->ReportsUser->deleteAll(array(
'ReportsUser.report_id' => $this->data['Report']['report_id'],
'ReportsUser.user_id' => $this->data['Report']['user_id']
));
..but it deletes all the rows with the given user_id, with this query:
What am I doing wrong here. Is it a bad data in the deleteAll call?
I don't want to delete any users or reports, only the relationship between them.
I can confirm that the table names are correct, and that the variables exist and are set.
It seems no error with your code. Either your $this->data['ReportUser']['report_id'] is having some problem. The best way is to first try to print your $this->data. And check whether it exists?
For the safety reasons, use the second argument $cascade = false and also unbind all the ReportUser model Associationship using $this->ReportUser->unbindModel() method.
The other reason seems to be deleting data through
$this->User->ReportUser. Try to use $this->ReportUser->deleteAll('your conditions', false); directly.
Please ask if it not worked for you.

cakePHP HABTM, am I getting it all wrong?

I understood that every new row, causes the deletion of the rows that were there before?
What is the idea behind it? I don't believe that it is ..
So, what am i getting wrong?
Edit A
I have a form that adds a store to the Stores table. the store have a column named owner_id which is associated to the Users table through a belongsTo relationship.
There is also a table named stores_users that supposed to store the mangers for each store, using the HABTM relationship.
For this table there is a form with an email field, that connects the user to the store by saving the record directly to the stores_users table.
So, there is no full HABTM save anywhere, if I understand the term correctly.
So, my questions are:
Should I expect problems using it this way?
Can you advice me about how to it, if my method is not the proper way?
How can I use the stored data, using $this->User->find(...) to get all the stores that the user can manage?
yes, thats the default behavior of HABTM in cakephp
although this is not on "every row", but "every HABTM save".
this is working IF you always provide all HABTM values.
and with baked views according to the specifications for such HABTM this is all working out of the box.
if you change the default behavior (old ones get not deleted) you will need to make sure that there are no duplicates. there are behaviors out there, I think, which try to accomplish that.
but I would recommend for you to build your forms the way that the default behavior of cake can do its job.
Example:
IS: 1,3,6 (in DB for this key)
NEW: 2,3,6 (coming from form)
(cake deletes 1,3,6 and adds 2,3,6)
=> overall result (forgetting about different primary keys): "1" deleted, "2" added
so it might not be the most resource sparing way but its sure the easiest and fastest.

CakePHP hasAndBelogsToMany using save() vs. saveAll()

I am using a very intrinsic database with a CakePHP application and so far my multi-models views and controllers are working fine. I have a singular table (Entity) that have it's id on several other tables as the Foreign Key entity_id
Some tables are one to one relations (Like a Company is one Entity) and some are one to many (Entity can have several Addresses) and so on.
I won't/can't change the database model, so this is the structure.
I have been using saveAll() to save data on those tables with input names like:
Entity.type='x' (hidden inside the view)
Company.name
Address.0.street
Address.0.city
Address.1.street
Address.1.city
... and so on ...
and my save all is doing all the hard job, BEGIN TRANSACTION, all INSERTs and a final COMMIT ...
But now I've created a EntityCategory that is a n to n relation and created the full HABTM relation inside the model.
It works when I save() it but just the HABTM relation, and it saves everthing when I use saveAll() (just as before) except for the HABTM relation.
Am I missing something ? How I make this work correctly ? I am using the following code today:
if (!empty($this->data)) {
$this->Entity->saveAll($this->data);
$this->Entity->save($this->data);
}
The saveAll() saves all data in several tables, saves the id in Entity->id and the save() saves the HABTM relations, but I am not sure if it is correct or if it can bring me problems if I change some structure/model.
Is this the best way to use it? Is there a correct way to save that relations inside CakePHP ? What your experience/knowledge can tell me ?
This is fixed if you download the nightly.
Be careful though, something else might break.
The problem with saveAll() and HABTM associations is a known CakePHP issue, and has not been resolved as of 1.2 RC2.
As fas as best pratices for saving related model data goes, according to the CakePHP cookbook:
"When working with associated models, it is important to realize that saving model data should always be done by the corresponding CakePHP model. If you are saving a new Post and its associated Comments, then you would use both Post and Comment models during the save operation."
However, using saveAll() and save() should work, and IMHO is a more flexible/generic solution.

Resources