Reciprocal afterFind generates infinite loop

I’m having a problem with a MANY_MANY relation, let’s say Posts and Tags. I’d like to use a checkbox list to assign tags from posts’ view and posts from tags’ view.

Post Model:


public function relations()

{

    'tags' => array(self::MANY_MANY, 'Tag', 'post_tag(id_post, id_tag)'),

    ....

}


    public function afterFind()

    {

        if(!empty($this->tags))

        {

            foreach($this->tags as $n=>$tag)

                /* This is needed by the checkbox list. It's not really important what I do here, 

                   it's just an example of using related Tag objects */

                $this->tagsIDs[]=$tag->id_tag; 

        }

    }

Tag Model:


public function relations()

{

    'posts' => array(self::MANY_MANY, 'post', 'post_tag(id_tag, id_post)'),

    ....

}


    public function afterFind()

    {

        if(!empty($this->posts))

        {

            foreach($this->posts as $n=>$post)

                /* This is needed by the checkbox list. It's not really important what I do here, 

                   it's just an example of using related Post objects */

                $this->postsIDs[]=$post->id_post; 

        }

    }

When I open ANY page (for example try to login) the apache process goes in infinite loop and aborts after 30 seconds (I can read this in the apache log; while Yii log is not updated because of the crash)

I think Yii loads a Post, then all related Tags (lazy loading), but each of them generates several lazy loadings of Post items (because of the afterFind function) and this generates the infinite loop. If I quit any of the afterFind functions I don’t get the error.

I think the solution would be to make an eager loading but how can I do this in the relations() function? there’s no reference about that. Is there any workaround for this?

I’ve found a workaround for this. It works but you need to hardcode table names. This is not the best practice but I hope somebody replies to this post with a better solution.

Tag model:


    public function afterFind()

    {

        $posts = Yii::app()->db->createCommand()

        ->select('id_post')

        ->from('post_tag')

        ->where('id_tag = :id_tag', array(':id_tag' => $this->id))

        ->queryAll();

        /* In this way I only select id_post and avoid lazy loading and consecuent infinite loop */

        

        foreach ($posts as $p)

            $this->postsIDs[] = $p['id_post'];

    }

Try:

  1. Replace “if(!empty($this->tags))” to “if($this->hasRelated(‘tags’))” and “if(!empty($this->posts))” to “if($this->hasRelated(‘posts’))”

  2. Use with(‘tags’) to retrieve Post where you need tagsIDs: e.g. Post::model()->with(‘tags’)->findByPk(<post_id>);

Hope, it will prevent loop.

It works!!! Thanks man, I’ve spent hours trying to fix it. Have you found another way to implement it?

still not :(