Cameron
Cameron

Reputation: 28783

CakePHP hasAndBelongsToMany not linking as expected

I have three tables: Users, Profiles, and Friends.

They are connected like so:

public $name = 'User';

    public $hasOne = 'Profile';

    public $hasMany = array(
        'Post',
        'Answer'
    );

    public $hasAndBelongsToMany = array(
        'User'=>array(
            'className'              => 'User',
            'joinTable'              => 'friends',
            'foreignKey'             => 'user_from',
            'associationForeignKey'  => 'user_to'
        )
    );

and the profile model is just a belongsTo user so I have not shown that, and the friends model is virtual by doing the association in the user model.

So to get a list of friends for a user. I pass the username like so in the friends controller:

public function index( $username )
{   
    $friends = $this->User->find('first', 
        array(
            'conditions'=>array(
               'User.username'=>$username
            ),
            'contain'=>array(
                'Profile',
                'Friend'=>array(
                    'conditions'=>array(
                        'Friend.status'=>1
                    ),
                    'contain'=>'Profile'
                )
            )
        )
    );

    $this->set('friends', $friends);
}

Which should pull the data for the friends and there associated Profile info!

However I get this error: Model "Friend" is not associated with model "Profile" [APP/Cake/Model/Behavior/ContainableBehavior.php, line 339] so something isn't working correctly...

Can anyone help?

I tried changing the name of he hasAndBelongsToMany to Friends in the model but that throw an error that the table wasn't unqiue... so that's not the solution either.

Tables:

**users**
id
username
password

**profiles**
id
name
user_id

**friends**
id
user_from
user_to
status

If I change the Model to become:

public $hasAndBelongsToMany = array(
        'Friend'=>array(
            'className'              => 'Friend',
            'joinTable'              => 'friends',
            'foreignKey'             => 'user_from',
            'associationForeignKey'  => 'user_to'
        )
    );

then I get this error:

Database Error
Error: SQLSTATE[42000]: Syntax error or access violation: 1066 Not unique table/alias: 'Friend'

SQL Query: SELECT `Friend`.`id`, `Friend`.`user_from`, `Friend`.`user_to`, `Friend`.`created`, `Friend`.`status`, `Friend`.`id`, `Friend`.`user_from`, `Friend`.`user_to`, `Friend`.`created`, `Friend`.`status` FROM `db52704_favorr`.`friends` AS `Friend` JOIN `db52704_favorr`.`friends` AS `Friend` ON (`Friend`.`user_from` = 6 AND `Friend`.`user_to` = `Friend`.`id`)

Upvotes: 1

Views: 989

Answers (3)

tigrang
tigrang

Reputation: 6767

I think you are looking for something like this:

User.php

public $hasAndBelongsToMany = array(
    'Friend'=>array(
        'className'              => 'User',
        'joinTable'              => 'friends',
        'foreignKey'             => 'user_from',
        'associationForeignKey'  => 'user_to'
    )
);

This will alias the User table as a Friend to itself in a habtm relationship. To keep with convention, the join table should be called friends_users with user_id and friend_id keys.

Here's the result I get with a test app I made (just add your conditions):

$this->User->contain(array(
            'Profile',
            'Friend' => array(
                'Profile',
            ),
        ));
debug($this->User->find('all', array('conditions' => array('User.id' => 1))));

array(
    (int) 0 => array(
        'User' => array(
            'id' => '1',
            'name' => 'user1'
        ),
        'Profile' => array(
            'id' => '1',
            'user_id' => '1'
        ),
        'Friend' => array(
            (int) 0 => array(
                'id' => '2',
                'name' => 'user2',
                'FriendsUser' => array(
                    'id' => '1',
                    'user_id' => '1',
                    'friend_id' => '2'
                ),
                'Profile' => array(
                    'id' => '2',
                    'user_id' => '2'
                )
            )
        )
    )
)

Upvotes: 1

thecodeparadox
thecodeparadox

Reputation: 87073

Your Friend model has no user_id and thats why User model can't make contact with Friend. Try with the following:

public function index( $username )
{   
    $this->User->Behaviors->attach('Containable');
    $friends = $this->User->find('first', 
        array(
            'conditions'=>array(
               'User.username'=>$username
            ),
            'contain'=>array(
                'Profile',
                'Friend'=>array(
                    'conditions'=>array(
                        'Friend.status'=>1
                    )
                )
            )
        )
    );

    $this->set('friends', $friends);
}

Upvotes: 1

ori
ori

Reputation: 7847

Models and aliases of related models should be unique. In your case the problem is:

public $name = 'User';
...
public $hasAndBelongsToMany = array(
    'User'=>array(
    ...

If friends is the join table name, than you could use Friend as the alias of the friend User.

Second thing is you're trying to contain both User => Profile and User => Friend => Profile which is probably also problematic.

In my experience, deep contains can get quite inefficient. You might consider dropping the Friend's Profile from the contain, and then extracting the friend user ids and getting the profiles separately.

Upvotes: 2

Related Questions