phpsymfonytagstag-cloud

Symfony2 - TagWeights not being set and weighted in array


I have a method setup to weigh the tags that are being used the most. I am using the example from here as a learning exercise: http://tutorial.symblog.co.uk/docs/customising-the-view-more-with-twig.html (see Tag Cloud) I am doing this slightly different as I am using Tags as an entity and not a string property of Blog Entity based on the link.

Tag is setup as a ManyToMany/ManyToMany to Blog entity, see below.

The problem I am having is tags are being retrieved as intended but however it's not set apparently therefore all tags are returning as an integer of 1. How can I correct this? Am I calling the tags incorrectly to where it's not taking account for which ones are being used more than once?

$tagWeights[$tag['tag']] = (isset($tagWeights[$tag['tag']])) ? $tagWeights[$tag['tag']] + 1 : 1;

getTagWeights (var dumping $tagWeights[$tag['tag']] returns an int of 1)

public function getTags()
{
    $tags = $this->createQueryBuilder('t')
        ->select('t.tag')
        ->getQuery()
        ->getResult();

    return $tags;
}

public function getTagWeights($tags)
{
    $tagWeights = array();

    if (empty($tags))
        return $tagWeights;

    foreach ($tags as $tag)
    {
        $tagWeights[$tag['tag']] = (isset($tagWeights[$tag['tag']])) ? $tagWeights[$tag['tag']] + 1 : 1;
        var_dump($tagWeights[$tag['tag']]);
    }

    // Shuffle the tags
    uksort($tagWeights, function() {
        return rand() > rand();
    });

    $max = max($tagWeights);

    // Max of 5 weights
    $multiplier = ($max > 5) ? 5 / $max : 1;

    foreach ($tagWeights as &$tag)
    {
        $tag = ceil($tag * $multiplier);
    }

    return $tagWeights;
}

Tag entity

class Tag
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name="tag", type="string", length=255)
 */
private $tag;

/**
 * @ORM\ManyToMany(targetEntity="Blog", mappedBy="tags")
 */
protected $blogs;

public function __construct()
{
    $this->blogs = new ArrayCollection();
}

/**
 * Get id
 *
 * @return integer 
 */
public function getId()
{
    return $this->id;
}

/**
 * Set tag
 *
 * @param string $tag
 * @return Tag
 */
public function setTag($tag)
{
    $this->tag = $tag;

    return $this;
}

/**
 * Get tag
 *
 * @return string 
 */
public function getTag()
{
    return $this->tag;
}

/**
 * Add blogs
 *
 * @param \AcmeBundle\Entity\Blog $blogs
 * @return Tag
 */
public function addBlog(\AcmeBundle\Entity\Blog $blogs)
{
    $this->blogs[] = $blogs;

    return $this;
}

/**
 * Remove blogs
 *
 * @param \AcmeBundle\Entity\Blog $blogs
 */
public function removeBlog(\AcmeBundle\Entity\Blog $blogs)
{
    $this->blogs->removeElement($blogs);
}

/**
 * Get blogs
 *
 * @return \Doctrine\Common\Collections\Collection 
 */
public function getBlogs()
{
    return $this->blogs;
}
}

Blog entity

/**
 * @ORM\ManyToMany(targetEntity="Tag", inversedBy="blogs")
 * @ORM\JoinColumn(name="tag_id", referencedColumnName="id")
 */
protected $tags;

public function __construct()
{
    $this->tags = new ArrayCollection();
}

/**
 * Add tags
 *
 * @param \AcmeBundle\Entity\Tag $tags
 * @return Blog
 */
public function addTag(\AcmeBundle\Entity\Tag $tags)
{
    $this->tags[] = $tags;

    return $this;
}

/**
 * Remove tags
 *
 * @param \AcmeBundle\Entity\Tag $tags
 */
public function removeTag(\AcmeBundle\Entity\Tag $tags)
{
    $this->tags->removeElement($tags);
}

/**
 * Get tags
 *
 * @return \Doctrine\Common\Collections\Collection 
 */
public function getTags()
{
    return $this->tags;
}

Solution

  • The problem of your function is returning 1 always is because you do the following

    foreach ($tags as $tag)
    {
        $tagWeights[$tag['tag']] = (isset($tagWeights[$tag['tag']])) ? $tagWeights[$tag['tag']] + 1 : 1;
        var_dump($tagWeights[$tag['tag']]);
    }
    

    And you iterate once for each tag, therefore $tagWeights[$tag['tag']] is not acceded more than once per tag, hence always has the value 1.

    One way to do this, and thus you wouldn't have to change much code, would be use the blogs instead the tags as below:

    foreach ($blogs as $blog)
    {
        foreach($blog->getTags() as $tag){
            $tagWeights[$tag->getTag()] = (isset($tagWeights[$tag->getTag()])) ? $tagWeights[$tag->getTag()] + 1 : 1;
            var_dump($tagWeights[$tag->getTag()]);
        }
    }
    

    And you'd need to pass the $blogs variable to the function instead $tags

    public function getTagWeights($blogs)
    {
        ...
    }
    

    The code above is not tested, but it would be something similar.

    I think that another problem is that in your Tag entity, you doesn't use the unique annotation for tag, and in this way, it's possible that there are some tags with exactly the same name, and it's not the expected behavior for the tags cloud. You should do something like this:

    /**
     * @var string
     *
     * @ORM\Column(name="tag", type="string", length=255, unique=true)
     */
    private $tag;