I have a bunch of models that I'm going to be creating, and these models will have different types of categories that they can belong to. For example, one model will HABTM one set of categories, another model will HABTM another set.
I've come up with one idea - creating a model called Category, and having a categories table with a 'model' field, that contains the name of the model that this category has a HABTM relationship with. Then the other models would have something like this:
public $hasAndBelongsToMany = array(
'Category'=>array(
'conditions'=>array(
'Category.model'=>'Modelname'
)
)
)
This seems OK but
this code will be repeated in every model which seems silly and
some categories will apply to several models, so there would be duplicate database entries for those categories.
Is there a better way? Thanks for your help!
You can use one category model and add a model field to the category model to specify the related model. So you can use different models with one category model... Like you said...
I think this is a good idea.
Related
My question is in relation this this answer.
https://stackoverflow.com/a/8773953/1297775
I have read at many places, what #deceze put as:
"To be quite honest, for any halfway complex application, relying on Cake's automagic handling of HABTM relationships can get quite fragile and hard to debug, so I always manage HABTM records myself."
Even the CakePHP book hints at it http://book.cakephp.org/2.0/en/models/saving-your-data.html#what-to-do-when-habtm-becomes-complicated
I want to ask, that when he says he 'manages' this HABTM records himself, does he...
1) Not create the HABTM relations in the models at all and create a model for the join table
OR
2) Create the HABTM relations in the models, but does not use it in the controller code by just using $this->FirstModel->JoinModel->saveAll($someData);
Thanks
Basically you need use a hasManyThrough relationship, which is essentially a HABTM setup manually which allows you to add extra data to the relationship (such as created/expiry dates). It's quite simple, you just need to create a model to join them and use the normal belongsTo and hasMany properties in the model, here is a simple user/membership/course setup:
class User extends AppModel {
public $hasMany = array('Membership');
}
class Course extends AppModel {
public $hasMany = array('Membership');
}
class Membership extends AppModel {
public $belongsTo = array('User', 'Course');
}
The only fields (at minimum) the memberships table needs is course_id and user_id.
You can now operate on the Membership model as a normal model, without Cake treating it as HABTM and using its auto-magic whenever you save records. You can also use cool counterCaches on the membership model to count how many users a course has etc.
For my recipe-sharing website, I want to create a database and CakePHP models for the recipes and their ingredients.
I created two tables: recipes and ingredients. The third table is a HABTM table for ingredients_recipes, which also stores the amount of ingredients needed for the recipes. How do I create a model for the third table?
The third table would look something like this:
recipe_id
ingredient_id
amount
measurement_unit
I also thought of adding another table for measurement_units to store all possible units (ex. tablespoon, tea spoon, cup, etc.). Would that be too much?
HABTM, within Cake's ORM, is actually an abstraction of the following two model association structures (using your example):
Recipe
-> hasMany IngredientsRecipe
-> belongsTo Ingredient
and
Ingredient
-> hasMany IngredientsRecipe
-> belongsTo Recipe
The IngredientsRecipe model is inferred, and used only to link the two first-class models, Ingredient and Recipe.
However, in your case, you actually want IngredientsRecipe to be a first-class model, which breaks the HABTM abstraction. In fact, what you need to do is explicitly define the two association structures above, so that Cake treats the IngredientsRecipe model as a first-class citizen, allowing you to query against it, save records to it, etc.
Also, no, I don't think it's too much to create an additional MeasurementUnit model. It will provide you much more flexibility down the line.
Think of it like this then treat it one chunk at a time:
`Recipe` (1)--(n) IngredientsRecipe (n)--(1) Ingredient
In Recipe, create the association to IngredientsRecipe
In Ingredient, create the association to IngredientsRecipe
In IngredientsRecipe create the association to Recipe
Still in IngredientsRecipe create the association to Ingredient
While you're doing it, forget about HABTM and think instead about hasMany and belongsTo
Incidentally, you are not bound to call the model IngredientsRecipe, that is just the default/convention.
When you're done, treat it like hasMany, belongsTo or HABTM as appropriate.
I have a model Content which belongsTo Categories, hasMany Publishers, and Publisher belongsTo city.
There is also a search form where someone selects from a drop-down box which category to view and which city.
But how can I combine these two in a single paginate condition? I mean I cannot do something like:
$this->paginate('Content',array('conditions' =>array('Category.id'=>$category,
'City.id'=>$city)));
because to get cities cake performs a different query.
Nor can I do something like:
$this->paginate('Content',array('conditions' =>array('Category.id'=>$category),
'contain'=>array('Publisher.City'=>array('conditions'=>array(City.id'=>$city)))));
because this will search according to category and filter the city results according to $city.
I know I can do something like:
$this->Content->Publisher->City->find(...)
but this will change the output of my paginated data.
What I usually do is write my custom query where I LEFT join all models and filter the results in WHERE. But I wanted to aks if there is a more cake (sic) way!
thanks
i've experienced the same thing when i first try to create a simple search engine with hasmany relationship.
i used multiple $this->find() and assigned it in a variable then on the paginate code i used$this->paginate(array_merge(name of the variables used in $this->find()));.
hope this will help you...
~gio
I was hoping someone with an understanding on CakePHP could shed some light on a question I've been having.
Here's my scenario, I have a User this User has a Company which in turn has many Department and many Address. If I were to get a User could I expect to have access to the Company and all models associated with that Company?
So would $user['Company']['Department'][0] or $user['Company']['Address'][0] be possible?
Which brings me back to the original question, how extensive is the linkage between models?
In plain-vanilla model, Cake's model linkage is determined by your models' recursive attribute. Your example model relationship looks something like this:
User
-> belongsTo Company
-> hasMany Department
-> hasMany Address
-> hasMany PhoneExtension
(I've added an additional relationship (User hasMany PhoneExtension) to flesh out the following explanation.)
There are three accepted values for Model::recursive: -1, 0, 1 and 2. Each value indicates to Cake's ORM a different depth to retrieve model records. I'll use $this->User->find('all') to illustrate the difference.
At recursive = -1, Cake retrieves only the specified model (ie. User). It parses none of the model associations.
At recursive = 0, Cake retrieves the specified model, and parses its belongsTo associations. $this->User->find('all') would retrieve all User records, as well as the Company record to which each User belongs.
At recursive = 1, Cake retrieves the specified model, and parses all of its direct associations. $this->User->find('all') would retrieve all User records, as well as the Company record to which each User belongs, and all PhoneExtension records belonging to the User.
At recursive = 2, Cake retrieves the specified model, parses all of its direct associations and all associations of its direct associations. $this->User->find('all') would retrieve everything in the example model relationship diagram: all User records, the Company records to which the User records belong, all PhoneExtension records belonging to the User, and all Department and Address records belonging to the Company.
Which is the very long way of saying that yes, you can achieve the results you indicate in your question, at recursive = 2.
If you wanted to go deeper than what recursive = 2 gets you, you'll have to use the Containable behaviour. Let's say that your Department model had an additional association: hasMany Group. Thus:
User
-> belongsTo Company
-> hasMany Department
-> hasMany Group
-> hasMany Address
-> hasMany PhoneExtension
To retrieve everything we got with a recursive = 2 retrieval, as well as all the associated Group records, you'd construct your Model::find call like this:
$this->User->find('all', array(
'contain' => array(
'PhoneExtension',
'Company' => array(
'Department' => array( 'Group' ),
'Address'
)
)
));
It's as extensive as you need/want it to be. Look into the recursive option of the find() family of methods. Also the Containable behavior. The specific references you list are possible, but directly under the user:
$user['Department'][0]
Think of it as the user having many departments through its company.
If you access a class/object and set "$this->recursive = -1" then it only returns the object without the dependencies!
I have checked out the cookbook web site of cakephp that there are four types Model relationship:
http://book.cakephp.org/view/79/Relationship-Types
Since the one I am more familiar with is belongsTo,
I am not sure when I need to use hasManay and HABTM.
What will be the result to my web site if I used a wrong Model relationship type?
Please advise.
belongsTo and hasMany are a pair and express a one-to-many relationship. One model belongs to another (i.e. it has a other_model_id field), while the other way around that other model has many records in the model that belongs to it. It's two sides of the same coin.
hasAndBelongsToMany is Cake's term for a many-to-many relationship. Here's a primer on this type of relationship. You use this when both models can have many of the other model, e.g. People-People friend relationships. A many-to-many relationship between two models entails three tables: model_a, model_b and model_a_model_b. If you're trying to use this type of relationship without the third table, you'll probably just get a bunch of errors.