CakePHP hasAndBelogsToMany using save() vs. saveAll() - cakephp

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.

Related

Plan and create database before baking any files?

I'm really new to cakephp (3). I have a general question about baking models, views and controllers.
Do I have to completely plan and create the database scheme before baking any m., v. or c?
I'm afraid of overwriting my codes, when I extend my database-scheme. I'm thinking of a situation like this:
I have two tables "Articles" and "Users". I baked mvc-s, having a small but complete website. I tweaked the Users-Model/Entity for some validator-reasons (e.g.).
Now, two weeks later, I want to add a "Group" table, where one user can belong to many groups.
Normally, I could rebake the Users-mvc-files. But that would overwrite my whole website.
Do I make a mistake? Do I have to manually modify the mvc-files when extending tables?
What is the best practice for extending a cakephp-website?
Many thanks in advance.
You could just bake the MVC for the new table, Groups (should be plural), and then update the associations, if any, in other tables. For example, if Users belongs to Groups, after baking the Groups table, you could add:
// in /src/Model/Table/UsersTable.php
$this->belongsTo('Groups', [
'foreignKey' => 'group_id'
]);
Of course, this assumes you've added the 'group_id' field to the Users table in your database.
This is a fine solution if your table is small and your data model remains fairly static. But if you are working with many tables and you are still regularly making changes to the model, I would suggest doing what I did after blowing away all my controller changes a couple of times.
CakePHP 3 makes very cool use of Elements in it's Controller templates. You can copy the cakephp vendor templates into your src directory, and then provide your additional controller methods (like login()/logout() in UsersController) as elements. Then test for the model and include the appropriate elements for that model.
This way you can bake away and not worry about losing all your changes.
Yes, generally you have to plan for database scheme before baking your MVC.
You should manually do the changes if any alter/changes become in the table otherwise the bake will override all the codes you did.
All the very best

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

How do variable Model names work in CakePHP tables?

Looking at the tables (aros, acos) generated by the ACL component and for example the Favorites plugin by CakeDC I see the favorites table with the fields Favorite.user_id, Favorite.model and Favorite.foreign_key.
The last two combined are replacing the good old Favorite.post_id (presuming model has the value 'Post'). It is in a way a HABTM pivot table with a dynamic modelname on one side.
I can see the general and useful idea here but would like to know more about the application of it.
My question(s):
Does this pattern have a name?
How does this work code-wise? I can see the abstract principle, but what kind of model-code is needed to make this work? Does it involve a patchwork of queries, or does this allow for a smooth one-query implementention? E.g.: I'd like to fetch all marked-as-favorite Posts in the system and their related Users in one go.
And does this work both ways? (querying from both the Post model as well as the User model)
I'd like to work towards an abstraction where I put a behavior in place to take care of this pattern.
I did look into the CakeDC code but could not figure out the principle. It's a little too cryptic for my current knowledge. Hence my question here.
kind regards,
Bart
Does this pattern have a name?
http://en.wikipedia.org/wiki/Junction_table aka Join Table.
How does this work code-wise? I can see the abstract principle, but
what kind of model-code is needed to make this work? Does it involve a
patchwork of queries, or does this allow for a smooth one-query
implementention? E.g.: I'd like to fetch all marked-as-favorite Posts
in the system and their related Users in one go.
It is just a regular join table, that's all. You can simply rely on what CakePHP is building up when using the HABTM assocs or refine your query by using joins. See http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#joining-tables.
The model field is not needed in that "pattern" but allows you to have just one join table that can be used for many different tables/models. For example instead of having tags and users and posts associated by tags_posts and tags_users we simply use the same table and filter by the model field.
And does this work both ways? (querying from both the Post model as
well as the User model)
Yes if you set the HABTM association up in both models.
Thanks for using our plugins. ;)

Implementing tag clouds with CakePHP

I did a little research on tag clouds, and I ended up choosing a schema similar to the Wordpress one seen in this page: http://www.pui.ch/phred/archives/2005/04/tags-database-schemas.html
Currently, the tables that I have created is: Posts, PostsTagMaps, PostsTags
1) Would I need to create a table for PostsTagMaps even if I don't plan on using a controller?
2) A Post hasMany PostTagMaps. I'm not sure where should I be defining this relationship. I think it should be the Post model, but then I would have to join the PostsTags table to PostTagsMaps then join it to Posts, so I wanted to ask for some advice.
The easy solution is to follow Cake conventions (which will look very similar to the solution you linked to). You'll have three tables:
posts, tags, posts_tags
Then your Post model HABTM Tag model. The join table will automatically be used by Cake to save and retrieve the information. Check the book for more information on setting up your schema.
Cake is flexible enough where you can do it however you want, but if it's a basic sort of posts-tags relationship, the convention method is the way to go.
If you want to use a custom model/table like PostsTagMaps, then do this by using the 'with' key in your HABTM relationship definition. The 'with' key tells Cake to use a specific model (and therefore a specific table) instead of an automatically generated version. In this case, your tag model sounds like it's PostsTags and your HABTM table is PostsTagMaps, so the 'with' key on Post HABTM PostsTag would be PostsTagMap.

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.

Resources