Bounty:
+500 rep bounty to a GOOD solution. I’ve seriously banged my head against this wall for 2 weeks now, and am ready for help.
Tables/Models (simplified to show associations)
- nodes
- id
- name
- node_type_id
- node_associations
- id
- node_id
- other_node_id
- node_types
- id
- name
General Idea:
A user can create node types (example “TV Stations”, “TV Shows”, and “Actors”…anything). If I knew ahead of time what the node types were and the associations between each, I’d just make models for them – but I want this to be very open-ended so the user can create any node-types they want. Then, each node (of a specific node-type) can relate to any other node of any other node-type.
Description and what I’ve tried:
Every node should be able to be related to any/every other node.
My assumption is that to do that, I must have an association table – so I made one called “node_associations” which has node_id and other_node_id.
Then I set up my association (using hasMany through I believe):
(below is my best recollection of my set-up… it might be slightly off)
//Node model
public $hasMany = array(
'Node' => array(
'className' => 'NodeAssociation',
'foreignKey' => 'node_id'
),
'OtherNode' => array(
'className' => 'NodeAssociation',
'foreignKey' => 'other_node_id'
)
);
//NodeAssociation model
public $belongsTo = array(
'Node' => array(
'className' => 'Node',
'foreignKey' => 'node_id'
),
'OtherNode' => array(
'className' => 'Node',
'foreignKey' => 'other_node_id'
)
);
At first, I thought I had it – that this made sense. But then I started trying to retrieve the data, and have been banging my head against the wall for the past two weeks.
Example Problem(s):
Lets say I have a the following nodes:
- NBC
- ER
- George Clooney
- Anthony Edwards
- Tonight Show: Leno
- Jay Leno
- Fox
- Family Guy
How can I set up my data structure to be able to pull the all TV Stations, and contain their TV Shows, which contain their Actors (as example)? This would be SIMPLE with normal model setup:
$this->TvStation->find('all', array(
'contain' => array(
'TvShow' => array(
'Actor'
)
)
));
And then, maybe I want to retrieve all male Actors and contain the TV Show which contain the TV Station. Or TV Shows that start at 9pm, and contain it’s actor(s) and it’s station…etc etc.
But – with HABTM or HasMany Through self (and more importantly, and unknown data set), I wouldn’t know which field (node_id or other_node_id) the model is, and overall just can’t wrap my head around how I’d get the content.
The Idea
Let’s try to solve this with convention, node_id will be the model who’s alias comes alphabetically first and other_node_id will be the one that comes second.
For each contained model, we create a HABTM association on-the-fly to Node class, creating an alias for each association (see
bindNodesandbindNodemethod).Each table we query we add an extra condition on
node_type_idto only return results for that type of node. The id of NodeType is selected viagetNodeTypeId()and should be cached.For filtering results using condition in deeply related associations, you would need to manually add extra join, creating a join for each jointable with a unique alias and then joining each node type itself with an alias to be able to apply the conditions (ex. selecting all TvChannels that have Actor x). Create a helper method for this in Node class.
Notes
I used
foreignKeyfornode_idandassociationForeignKeyforother_node_idfor my demo.Node (incomplete)
Example