Basically I would like to recreate this in CakePHP:
Many to many relationships with additional data on the relationship
or in other words, my intermediary table to have extra fields defining the type of relationship.
How do I set this up in CakePHP?
You can use as example:
model company:
id
name
person_id
model person:
id
name
model company_person:
company_id
person_id
position
thanx
if you want to have additional data in HABTM, use 2 hasMany relationships instead. So if A HABTM B, then set it up as: A hasMany A_B, B hasMany A_B, A_B belongsTo A, A_B belongsTo B. AFAIK, Cake support for HABTM is not very flexible.
class Person extends AppModel {
...
$hasAndBelongsToMany = array('Company', array('with' => 'CompanyPerson'));
....
}
Related
I apologize in advance... I'm a complete noob to cakephp. As such, I'm having great difficulty in getting my models related correctly. To be more specific, I'm having the most trouble with the "hasMany Through" relationship. I've been beating my head against the wall, and scouring the cookbook, but haven't found it as clear as i had hoped.
I'm currently writing a web app with many complex relationships. I'll start with stripped down versions of my tables.
Users:
id, name
Groups:
id, name, department_id
Departments:
id, name, district_id
Districts:
id, name
Roles
id, name
Note that roles are what I call my levels of access(user, admin, superuser).
groups_users
-group_id, user_id
departments_users:
department_id, user_id, role_id
RELATIONSHIPS:
User hasAndBelongsToMany Department
Department hasAndBelongsToMany User
User hasAndBelongsToMany Group
Group hasAndBelongsToMany User
Department hasMany Group
Group belongsTo Department
District hasMany Department
Department belongsTo District
Now, here's where i get a bit more confused.
Since users can be in multiple departments, in multiple districts, as well as have varying roles within each department (e.g. Department admin in DEPT A of DISTRICT A, and District Admin in DEPT B in DISTRICT B), I chose to place the user's level of access in the departments_users table. Since I'm storing more than foreign keys for the named models, ie role_id, this seems like a candidate for the hasMany through relationship, but cake's docs also mention a keepExisting option for the model relationship.
so i tried this, using the example from the cake site. leaving all of the initial datatables and relationships in place, I added a DepartmentPosition model and controller, like the following:
user.php
public $hasMany = array('DepartmentPosition');
department.php
public $hasMany = array('DepartmentPosition');
department_position.php
public $belongsTo = array('User','Department');
I also added a new data table:
department_position:
id, department_id, user_id, role_id
OK, so what i got, likely mistakenly from the cake docs, is that when I add a user to a department, i can include a role_id, and that will all be saved in the department_position data table, if I do saveAll().
I'm not sure if that is at all the right thing to do. I feel more confused now, having written this out, than i did before. lol. I'm very confused as to how to get these relationships correctly set-up. And, still, once I do, I feel i'll have trouble accessing data from associated models. I'm getting errors galore and am unable to retrieve much associated model data. eek. like I said, i'm not the strongest cake person.
Any suggestions, before i have an aneurysm?! lol.
Thanks loads. Any help will be a lot of help!
If a User can have different Role in different Group something is needed in between, HABTM or hasManyThrough table.
Before Cake version 2.1, it was not possible to have HABTM relation with extra colums, (here below role_id). The column would get lost when saving. This is not the case anymore if you set the unique param to keepExisting, but I prefer the hasManyThrough relation over HABTM. I guess it's a question of taste.
User hasMany GroupRole
GroupRole belongsTo User, GroupRole belongsTo Groups, GroupRole belongsTo Role (GroupRole is hasManyThrough table)
Group hasMany GroupRole, Group belongsTo Department
Department hasMany Group, Department belongsTo District
District hasMany Department
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.
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.
i think i have all 'baked' all my relationships correctly but my 'related' table shows id's instead of values.
my db has 3 tables. candidates, qualificationlookups, qualifications.
qualificationlookups, links candidates to qualifications using the id of the candidate and the id of the qualification.
in my view view, for 'candidates', i have a 'related qualificationlookups' table. (generated by baking a candidate hasmany qualificationlookups and a qualificationlookups belongsto candidate relationship)
in my edit view, for 'qualificationlookups', i can correctly set up the candidates and qualifications fields as dropdowns so i know 'qualificationlookups's relationships are fine.
So how do i ask cakephp to list the name of the qualification (from 'qualifications' table) in the 'related qualificationlookups' table on a candidate's page?
i must be missing something...
could someone please point me in the right direction?
Thanks, Vauneen
Whenever CakePHP automagically fetches lists from your tables, it uses the id key for the value and the $displayField for the text.
If your table has a name or title field, CakePHP automatically displays it as the display field. So, either rename the field that you want as your display field (say, candidate_name to just name) or set the $displayField variable in your model:
class Candidate extends AppModel {
var $displayField = 'candidate_name';
}
HTH.
If there is no other data being stored in the qualificationlookups table, change the relationship to candidates -> HABTM -> qualifications.
To do this, you first need to drop the qualificationlookups table. Create a new table called candidates_qualifications with two indexes, candidate_id and qualification_id.
In your Candidate and Qualification models, define a HABTM Relationship. You do not need to make a new CandidatesQualification Model, cake will do it on the fly.
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.