Hi I've got a couple of questions regarding associations and how they play in cases
of complex find() calls.
If I've got a model Post and a model Comment with the latter having a parent_id field
and I want to fetch a post with its associated comments threaded, do I simply perform a
find('threaded', ...) on Post itself or will this result in an error because Post doesn't
have a parent_id? I am particularly concerned about a Containable + Threaded find. (the
example I give here is a simplified version of what I actually need to implement)
What is the significance of Association Names? Does Containable work on association names
or model names? (the reason I want to know this is because I'm trying to implement polymorphic models by having condition-specific model associations, often having multiple associations to the same model)
I think that containable would not support threaded finds on the contained models. EG you might have a post_id column in your comments table, and do something like:
$this->Comment->find('threaded', array(
'conditions' => array(
'post_id' => 5)));
Although the book may say that Containable uses the model name, I have regularly used the association name in Containable when I have multiple joins between the same models. For simple projects, the model name is usually the same as the association name, so that may be why the book says that.
Related
I have an idea of how to do this but it doesn't seem like proper convention. I have a Submission model and a Revision model each with their similarly named tables. Each Submission can have one or more Revisions associated to it in a $hasMany relationship. The Revision model hence has a $belongsTo relationship linking back to the Submission.
In addition to having this relationship, the Submission model needs to have another association (called activeRevision) to a particular Revision in a $hasOne style of relationship. However, the $hasOne type requires the foreign key to be in the Revision table. I want it to be in the Submission table, so I don't need to query all of the Submission's Revisions to find the active one. I realized just specifying a $belongsTo relationship in the Submission would do what I want, but this feels wrong to me as now the two models "belong to eachother".
Is there a better way to go about this?
I wouldn't worry too much because of the 'naming' of the relation. By having the foreign key of the Revision inside the Submission table, CakePHP is, in fact, right that you've created a 'belongsTo' relation.
Although not strictly the 'right' (?) relation, in this situation, this looks like it's an easy way to achieve what you want. Just be sure to add an additional condition to your relation to prevent that a revision of another submission can be set as the current revision, i.e.;
public $belongsTo = array(
'Revision' => array(
'conditions' => array(
'Revision.submission_id = Submission.id',
)
)
);
However, make sure to add a proper comment in your code to prevent confusion if you're (or somebody else is) looking at your code in a later stage. (e.g. "note: using belongsTo relation, because its easier to maintain)
If you really want to convert it to a hasOne relation, you'll have to add an additional column to the 'Revisions' table, for example 'is_curreny_revision'. However, you will also need to be sure that only one revision of a submission can be set to be the current revision.
If you're using PostgreSQL, this can be achieved using a 'partial unique' index (see this question on StackOverflow; PostgreSQL: Conditional unique constraint). MySQL does not support partial indexes, so you're out of luck there
I have four very simple models. Offer, Employer, Employee and Response.
Offer hasOne Employer
Employer hasMany Offer
Response hasOne Employee
Employee hasMany Offer
And now when I make a find() on Offer, it nicely makes a JOIN query and returns Employer details.
But when I make a find() on Response, it doesn't attempt to retrieve Employee's data.
I reviewed the code many times, stripped the models off of any additional properties etc., and still nothing. Those models are now nearly identical, their SQL tables too, but Response behaves like it has no relation to Employee defined.
Any pitfall with this that I might be trapped in? I'm ready to report this as a bug at this moment.
I can post complete (which are short, anyway) model definitions here if it helps anything.
Check the class of your model instances
I.e.
debug(get_class($this->Response));
If it outputs AppModel - the reason your code doesn't appear to be being used, is because: it isn't being used. That being the case, check for typos in the filename/location of your model files - as CakePHP will silently use an AppModel instance if your model files don't exist.
I have three models that are as follows:
Location hasMany SportType hasMany Sport, then
Sport belongsTo SportType belongsTo Location
In the SportType model, the belongsTo Location has a condition 'Location.status' => true, such that it only retrieves records where the Location status is true. Works fine.
When retrieving records via a plain old find() in the Sport model, I would assume that it would not return records where the associated SportType's associated Location was false, but that is not the case.
I believe I could use containable behavior or explicitly constructed joins in my controller to get what I want, but I'm wondering if this is something I can achieve purely through the model relationships. Perhaps not.
You can either use Joins or change the model you're doing the search on and do it through the restricting model (ie Location).
$this->Location->find('all', array(
'conditions' => array(
'Location.status' => true
),
'contain' => array(
'SportType' => array(
'Sport'
)
)
));
But you cannot narrow the results of the searching model based on conditions within contained models.
Update:
Joins also allow you to add more conditions to other models...etc, as opposed to Contain which does not, so I supposed I'd lean toward going with Joins as that leaves you more flexibility moving forward.
Also, a JOIN will do one more-complicated query, while a contain will do many simpler queries... so depending on your database structure, that could be considered.
Bottom line, it's preference - both are just fine and whichever works for you is great.
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.
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.