Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8467971
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 10, 20262026-06-10T15:48:00+00:00 2026-06-10T15:48:00+00:00

Bounty: +500 rep bounty to a GOOD solution. I’ve seriously banged my head against

  • 0

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.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-10T15:48:01+00:00Added an answer on June 10, 2026 at 3:48 pm

    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 bindNodes and bindNode method).

    Each table we query we add an extra condition on node_type_id to only return results for that type of node. The id of NodeType is selected via getNodeTypeId() 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 foreignKey for node_id and associationForeignKey for other_node_id for my demo.

    Node (incomplete)

    <?php
    /**
     * @property Model NodeType
     */
    class Node extends AppModel {
    
        public $useTable = 'nodes';
    
        public $belongsTo = [
            'NodeType',
        ];
    
        public function findNodes($type = 'first', $query = []) {
            $node = ClassRegistry::init(['class' => 'Node', 'alias' => $query['node']]);
            return $node->find($type, $query);
        }
    
        // TODO: cache this
        public function nodeTypeId($name = null) {
            if ($name === null) {
                $name = $this->alias;
            }
            return $this->NodeType->field('id', ['name' => $name]);
        }
    
        public function find($type = 'first', $query = []) {
            $query = array_merge_recursive($query, ['conditions' => ["{$this->alias}.node_type_id" => $this->nodeTypeId()]]);
            if (!empty($query['contain'])) {
                $query['contain'] = $this->bindNodes($query['contain']);
            }
            return parent::find($type, $query);
        }
    
        // could be done better    
        public function bindNodes($contain) {
            $parsed = [];
            foreach($contain as $assoc => $deeperAssoc) {
                if (is_numeric($assoc)) {
                    $assoc = $deeperAssoc;
                    $deeperAssoc = [];
                }
                if (in_array($assoc, ['conditions', 'order', 'offset', 'limit', 'fields'])) {
                    continue;
                }
                $parsed[$assoc] = array_merge_recursive($deeperAssoc, [
                    'conditions' => [
                        "{$assoc}.node_type_id" => $this->nodeTypeId($assoc),
                    ],
                ]);
                $this->bindNode($assoc);
                if (!empty($deeperAssoc)) {
                    $parsed[$assoc] = array_merge($parsed[$assoc], $this->{$assoc}->bindNodes($deeperAssoc));
                    foreach($parsed[$assoc] as $k => $v) {
                        if (is_numeric($k)) {
                            unset($parsed[$assoc][$k]);
                        }
                    }
                }
            }
            return $parsed;
        }
    
        public function bindNode($alias) {
            $models = [$this->alias, $alias];
            sort($models);
            $this->bindModel(array(
                'hasAndBelongsToMany' => array(
                    $alias => array(
                        'className' => 'Node',
                        'foreignKey' => ($models[0] === $this->alias) ? 'foreignKey' : 'associationForeignKey',
                        'associationForeignKey' => ($models[0] === $alias) ? 'foreignKey' : 'associationForeignKey',
                        'joinTable' => 'node_associations',
                    )
                )
            ), false);
        }
    
    }
    

    Example

    $results = $this->Node->findNodes('all', [
        'node' => 'TvStation', // the top-level node to fetch
        'contain' => [         // all child associated nodes to fetch
            'TvShow' => [
                'Actor',
            ]
        ],
    ]);
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I'll add 500 of my own rep as a bounty when SO lets me.
Update after Bounty was awarded A new solution is coming up to this problem.
I'd really appreciate an answer to this, but can't afford a bounty (!). Here
I have started a bounty for this question ...because I really want the community's
Edit: I'm starting a bounty on this one, as I'm looking for a cleaner
Go bounty! This question has earned me a tumbleweed badge (7 views in 7
I'm going to bounty +100 this question when possible, even if it's already answered
I've now added a bounty which will be awarded to anyone who can take
From the original question (below), I am now offering a bounty for the following:
[answer auto-selected by bounty system against my will] I'm using subclipse, and always when

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.