2
votes

I have 3 models (User, Message and Tag) with the following relations:

  • User hasMany Message
  • Message belongsto User
  • Message HABTM Tag
  • Tag HABTM Message

If a User is logged in he might want to see all Message tagged with something.

$messages = $this->Message->find('all', array(
    'conditions' => array("Message.user_id" => $this->uid),
    'contain' => array(
        'Tag' => array(
            'conditions' => array(
                'Tag.id' => $activetag['Tag']['id']
            )
         )
    ));

However, this find will return ALL messages of that user. (Containable behaviour is included in both models)

2
Did you actually run this code or did you write it here? Because you must have gotten a syntax error whilst running this code: array('Tag' = array(...))Jelmer
i did some changes, thats why there was that error -.-Alex Schneider

2 Answers

1
votes

Containable on child (tag) does not perform filter on the parent (message), that's why all the messages are returned. The containable only place condition on the tag itself, in your case, messages not matching $activeTag would still get returned but with empty tag array attached, while messages matching would return with an array containing only one tag, the $activeTag, but all messages would get returned.

For your purpose CakepHP recommend using join function for filtering with HABTM, it joins hasOne or belongsTo for you automatically but when it comes to HABTM you may need to perform the join yourself if needed.

assuming tables are named conventionally:

$this->Message->recursive = -1;

$options['joins'] = array(
    array('table' => 'messages_tags',
        'alias' => 'MessageTag',
        'type' => 'INNER',
        'conditions' => array(
            'Message.id = MessageTag.message_id',
        )
    ) );

$options['conditions'] = array(
    'MessageTag.tag_id' => $activetag['Tag']['id'],
    'Message.user_id' => $this->uid );

$message = $this->Message->find('all', $options);

more info here: http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#joining-tables

0
votes

In your Model Message add

**
 * @see Model::$actsAs
 */
    public $actsAs = array(
        'Containable',
    );

/**
 * @see Model::$belongsTo
 */
    public $belongsTo = array(
        'Message' => array(
            'className' => 'Message',
            'foreignKey' => 'message_id',
        ),
        'Tags' => array(
            'className' => 'Tag',
            'foreignKey' => 'tag_id',
        ),
    );

in your controller :

// $tagsId = tags ids
    $message = $this->MessageTag->find('all', array('conditions' => array('MessageTag.tag_id' => $tagsId),'contain' => array('Message')));

also is better follow cake naming convention, if you have tags(plural), message_tags(first singular second plural),messages(plural) tables you must have Tag,MessageTag,Message Models.