Search code examples
phpsymfonycachinglistenercounter

Count cache in Symfony2 with a listener


Im building a blog in Symfony2, it has two entities: Blog and BlogComment (this is a simplified example). The Blog has an OneToMany relation with BlogComment. When a BlogComment is posted the property published is set to false. After the admin approved the BlogComment, it is set to true.

I want to create an overview with all my Blog-posts and show the number of BlogComment that are published = true and published = false in two seperate fields. It is possible to put all the BlogComment in a loop and count, but as it can be an very large overview of Blog-posts this isn't my preferred option.

I created two properties at Blog: published_comments_count and unpublished_comments_count. To update these fields, I created a listener to BlogComment:

class BlogCommentListener
{
    public function onFlush(OnFlushEventArgs $args)
    {
        $em = $args->getEntityManager();
        $uow = $em->getUnitOfWork();

        $entities = array_merge(
            $uow->getScheduledEntityInsertions(),
            $uow->getScheduledEntityUpdates(),
            $uow->getScheduledEntityDeletions()
        );

        foreach($entities as $entity){
            if($entity instanceof BlogComment){
                $Blog = $entity->getBlog();

                $published_comments_count = 0;
                $unpublished_comments_count = 0;

                foreach($Blog->getComments() as $BlogComment){
                    if($BlogComment->getPublished()){
                        $published_comments_count++;
                    } else {
                        $unpublished_comments_count++;
                    }
                }

                $Blog->setPublishedCommentsCount($published_comments_count);
                $Blog->setUnpublishedCommentsCount($unpublished_comments_count);

                $em->persist($Blog);

                $md = $em->getClassMetadata(get_class($Blog));
                $uow->recomputeSingleEntityChangeSet($md, $Blog);
            }
        }
    }
}

It works pretty well, but when I add a new comment, it is not in the ArrayCollection of $Blog->getComments() yet. Is there a way to compute the changes in this ArrayCollection?


Solution

  • I don't think it's possible to compute the changes in the arrayCollection but you can change your logic By adding or removing 1 for each your entity :

    class BlogCommentListener
    {
        public function onFlush(OnFlushEventArgs $args)
        {
            $em = $args->getEntityManager();
            $uow = $em->getUnitOfWork();
    
            $insertions = $uow->getScheduledEntityInsertions();
            $deletions = $uow->getScheduledEntityDeletions();
    
            foreach($insertions as $entity){
                if($entity instanceof BlogComment){
                    $Blog = $entity->getBlog();
    
                    $Blog->setUnpublishedCommentsCount($Blog->getUnpublishedCommentsCount()+1);//Here the comment inserted must be unpublished
    
                    $em->persist($Blog);
    
                    $md = $em->getClassMetadata(get_class($Blog));
                    $uow->recomputeSingleEntityChangeSet($md, $Blog);
                }
            }
        }
        foreach($deletions as $entity){
            if($entity instanceof BlogComment) {
                $Blog = $entity->getBlog();
    
                if ($entity->getPublished()) {
    
                    $Blog->setPublishedCommentsCount($Blog->getPublishedCommentsCount()-1);//Here the comment inserted must be unpublished
                } else {
                     $Blog->setUnpublishedCommentsCount($Blog->getUnpublishedCommentsCount()-1);
                }                
                $em->persist($Blog);
    
                $md = $em->getClassMetadata(get_class($Blog));
                $uow->recomputeSingleEntityChangeSet($md, $Blog);
            }
        }
    }
    

    And in bonus, i'll purpose to you a solution easier : Use the count filter in your twig template :

    {% for blog in blogs %}
        <li><h4>blog.title</h4>
        <p>comments published : {{ blog.comments.published|count }}</p>
        <p>comments non-published : {{ (not blog.comments.published)|count }}</p>
    {% endfor %}