CakePHP 2.4.2: Contain Not Working as Expected

2019-08-12 15:45发布

I have a model, Comment, that belongsto Module, Photo, Review, Article, and User. In turn, each of those models havemany Comment.

User belongsto Avatar and Avatar hasmany User

In the code below I successfully retrieve the latest 5 comments regardless of what model they come from (Photo, Article, Review) and I display some information about the User (username, online status).

$recentComments = $this->find('all',
array(
    'limit' => '5',
    'order' => array('Comment.id' => 'desc'),
    'conditions' => array(
        'Comment.page_id' => $page_id
    ),
    'contain' => array(
        'Photo' => array(
            'fields' => array('Photo.id'),
            'conditions' => array(
                'Comment.module_id' => 8
            ),
        ),
        'Review' => array(
            'fields' => array('Review.title', 'Review.id'),
            'conditions' => array(
                'Comment.module_id' => 2
            ),
        ),
        'Article' => array(
            'fields' => array('Article.title', 'Article.id'),
            'conditions' => array(
                'Comment.module_id' => 3
            ),
        ),
       'Module' => array(
            'fields' => array('Module.model', 'Module.post_title'),
        ),
      'User' => array(
            'fields' => array('User.username', 'User.online'),
        ),
    )
));

Unfortunately there are some issues with this. As you can see above I don't contain User->Avatar and all expected data is retrieved successfully.

$recentComments = Array
(
    [0] => Array
        (
            [Comment] => Array
                (
                    [id] => 179
                    [page_id] => 2
                    [module_id] => 3
                    [page] => 
                    [user_id] => 29
                    [post_id] => 9
                    [title] => test comment for article 9
                    [content] => here is the content for the comment
                    [created] => 2013-04-24 00:00:00
                    [redeemed] => 0
                    [status] => 0
                )

            [User] => Array
                (
                    [username] => bowlerae
                    [online] => 0
                )

            [Module] => Array
                (
                    [model] => Article
                    [post_title] => title
                )

            [Article] => Array
                (
                    [title] => Test title for article 9
                    [id] => 9
                    [likes] => 0
                    [dislikes] => 0
                    [comments] => 2
                )

            [Photo] => Array
                (
                    [id] => 
                )

            [Review] => Array
                (
                    [title] => 
                    [id] => 
                )

        )

However, if I DO contain User->Avatar like this...

   'User' => array(
        'fields' => array('User.username', 'User.online'),
        'Avatar' => array(
            'fields' => array('Avatar.file')
        )
    ),

Then the recursion basically becomes unlimited, ALL models related to Comment, User, Module, Article, Photo and Review are also retrieved which is A LOT.

Can anyone explain why this is happening? I will be happy to submit more code from some of the models if needed but I don't see any issues there.

Take a look at another example that works successfully...Below I am retrieving the 5 most recent articles. All information including the user Avatar is successfully retreived.

        $this->set('articles_sidebar', ClassRegistry::init('Article')->find('all',
array(
    'limit' => '5',
    'order' => array('Article.id' => 'desc'),
    'conditions' => array('
        Article.page_id' => $page_id
    ),
    'contain' => array(
       'User' => array(
            'fields' => array('User.username', 'User.online'),
            'Avatar' => array(
                'fields' => array('Avatar.file')
            )
        ),
    )
)));

Please note these two finds are performed in the AppController in the beforeRender given that $page_id > 0. $page_id is set in whatever the current controller is. I know people would probably ask about it but that's not what the issue is as I mentioned that example 2 retrieving the recent articles currently works.

EDIT: I discovered that it has something to do with my afterfind callback in the Article model. Is there a way I can tweak the queries in the afterfind and/or the $recentComments query so that they still work without breaking my contain? I don't need the likes, dislikes or comments virtual fields in my $recentComments query which is why they are not listed as one of the contained fields.

function afterFind($results, $primary = false) {
parent::afterFind($results, $primary);


          foreach($results as $key => $val){
               if (isset($val['Article']['id'])){    

                        $results[$key]['Article']['likes'] = $this->Like->find('count', array('conditions' => array('Like.post_id' => $results[$key]['Article']['id'], 'Like.module_id' => 3, 'Like.status' => 0)));


                        $results[$key]['Article']['dislikes'] = $this->Like->find('count', array('conditions' => array('Like.post_id' => $results[$key]['Article']['id'], 'Like.module_id' => 3, 'Like.status' => 1)));


                        $results[$key]['Article']['comments'] = $this->Comment->find('count', array('conditions' => array('Comment.post_id' => $results[$key]['Article']['id'], 'Comment.module_id' => 3, 'Comment.status < 2')));
               }
          } // end for each


      return $results;

} // end afterfind

2条回答
女痞
2楼-- · 2019-08-12 16:33

My guess is your problem is this:

When using ‘fields’ and ‘contain’ options - be careful to include all foreign keys that your query directly or indirectly requires.

Make sure you're including whatever field(s) are used to join User and Avatar models.

(read here)

查看更多
狗以群分
3楼-- · 2019-08-12 16:45

For now I changed (in the Article model afterFind)

 if (isset($val['Article']['id']))

to

 if (isset($val['Article']['id']) && $val == "article")

Seems to be working in current controller but need further testing. Is there a better solution?

查看更多
登录 后发表回答