简体   繁体   中英

Symfony2/Doctrine: How to load only related objects for current logged in user?

My Symfony2 application displays songs and lets the user rate them. User Management is done with FOSUserBundle. I have defined an One-To-Many Relationship between two Objects (Song & Ratings) defined like this:

Entity/Song.php

/**
 * Song
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="[...]\[...]Bundle\Entity\SongRepository")
 */
class Song
{
[...]
    /**
     * @ORM\OneToMany(targetEntity="Rating", mappedBy="song")
     */
    protected $ratings;
[...]
}

and

Entity/Rating.php

/**
 * Rating
 *
 * @ORM\Table()
 * @ORM\Entity
 */
class Rating
{
[...]
    /**
     * @var float
     *
     * @ORM\Column(name="rating", type="float")
     */
    private $rating;

    /**
     * @ORM\ManyToOne(targetEntity="[...]\UserBundle\Entity\User", inversedBy="ratings")
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     */
    protected $user;

    /**
     * @ORM\ManyToOne(targetEntity="Song", inversedBy="ratings")
     * @ORM\JoinColumn(name="song_id", referencedColumnName="id", onDelete="CASCADE")
     */
    protected $song;
[...]

When loading all the songs (~40) in my application I can access them in the controller like this:

SongsController.php:

$songs = $em->getRepository('[...]Bundle:Song')->findAll();

Now, how can I efficiently make sure that I only get the rating of the currently logged in user, without iterating through all ratings and validating the user?

index.html.twig :

{% for rating in song.ratings %}
  {% if rating.user == app.user%}
    {{ rating.rating }}
  {% endif %}
{% endfor %}

EDIT:

I have this now:

public function getAllSongsWithRatings($user)
{
    $qb = $this->getEntityManager()->createQueryBuilder()
       ->select('s')
       ->from('[...]Bundle:Song','s')
       ->join('s.ratings','r')
       ->where('r.user = :user')
       ->setParameter('user', $user);

    return $qb->getQuery()->getResult();

}

But when I look what doctrine generates:

SELECT s0_.id AS id0, s0_.number [...] FROM Song s0_ LEFT JOIN Rating [...] WHERE r1_.user_id = ? AND [...]
SELECT t0.id AS id1, t0.rating AS [...] FROM Rating t0 WHERE t0.song_id = ?

It does not query for the rating, including the user_id... I think this is possibly imperformant...

You will want to use a repository function:

http://symfony.com/doc/current/book/doctrine.html#custom-repository-classes

Then we have to get a little primative to run a native MySQL query:

public function getSongsWithUserRatings($userid)
{
    $stmt = $this->_em->getConnection()->executeQuery(
        'SELECT * FROM Song s LEFT JOIN Rating r ON r.song = s.song_id AND r.user = :userid',
        array('userid'=>$userid),
        array('userid'=>'integer')
    );
    return $stmt->fetchAll();

Notice the leftJoin part. Using the WITH parameter will give you all the songs plus join that ratings IF they exists only from that user.

Then in your controller you call for this and pass that along to twig:

//Controller

$userRatedSongs = $em->getRepository('Bundle:Songs')->getSongsWithUserRatings($this->getUser()->getUserId())

Then your twig template:

{% for song in songs %}
<h3>{{song.name}}</h3>
    {% if song.rating != null %}
    <p> Rating: {{song.rating}}</p>
    {% endif %}
{% endfor %}

song.ratings will still be an array but if you have constraints on your ratings so that a user can only rate a song once then the first rating will be that users rating.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM