Symfony2, FOSRestBundle. How to use group with JMSSerializerBundle?

Vynder picture Vynder · Feb 16, 2016 · Viewed 8.5k times · Source

I have entity:

<?php
namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\Groups;
//...


 /**
   * @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
  **/
 class User extends BaseUser
 {
   /**
    * @ORM\Id
    * @ORM\Column(type="integer")
    * @ORM\GeneratedValue(strategy="AUTO")
    * @Groups({"default"})
   */
protected $id;

/**
 * @ORM\ManyToMany(targetEntity="StorageBundle\Entity\File")
 * @ORM\JoinTable(name="users_photos",
 *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="photo_id", referencedColumnName="id", onDelete="CASCADE")}
 *      )
 * @Groups({"default"})
 */
protected $photoFiles;

/**
 * @ORM\Column(type="string", length=255, nullable=true, unique=false)
 * @Groups({"notification"})
 */
protected $deviceId;

 /**
 * @ORM\Column(type="string", length=255, nullable=true, unique=false)
  *     @Groups({"default"})
 */
protected $name;

/**
 * @ORM\OneToOne(targetEntity="AppBundle\Entity\Car", mappedBy="driver")
 * @Groups({"driver"})
 */
private $car;

/**
 * @var \DateTime $created
 *
 * @Gedmo\Timestampable(on="create")
 * @ORM\Column(type="datetime")
 * @Groups({"default"})
 */
protected $created;

/**
 * @var \DateTime $updated
 *
 * @Gedmo\Timestampable(on="update")
 * @ORM\Column(type="datetime")
 * @Groups({"default"})
 */
protected $updated;


}

My Controller:

<?php
namespace AppBundle\Controller;

use AppBundle\Entity\User;
//...

class UsersController extends AppController{

    /**
     * Get list of Admins
     * @Annotations\Get("/api/v1/admins", name="list_of_admins")
     * @return \FOS\RestBundle\View\View
     */
    public function getAdminsAction(){

        if(!$this->isGranted('ROLE_ADMIN')){
            throw new AccessDeniedException('Only for Admin');
        }

        $admins = $this->getDoctrine()->getRepository('AppBundle:User')->findByRole('ROLE_ADMIN');

        return $this->view(['data' => $admins], Codes::HTTP_OK);

    }

}

A user with role ROLE_ADMIN does not have the car (car have only users with role ROLE_DRIVER). When I try to get all admins I get this:

{
  "id": 4,
  "username": "[email protected]",
  "email": "[email protected]",
  "roles": [
    "ROLE_ADMIN"
  ],
  "photoFiles": [],
  "name": "Andrii",
  "car": null
}

I tried add Groups. What I need to add my controller to receive data only from group default and notification

Answer

chalasr picture chalasr · Feb 16, 2016

As reminder, Groups in JMSSerializer are used as exclusion strategy.

An exclusion strategy consists to a specific approach used to expose/exclude properties depending on condition/context.

In order to use them correctly, the first thing you have to do is exclude all properties of your entity :

// ...
use JMS\Serializer\Annotation as JMS;

/**
 * @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
 * @JMS\ExclusionPolicy("all")
 */
class User extends BaseUser

Now, you have explicitly expose properties depending on Groups :

/**
 * @ORM\Column(type="string", length=255, nullable=true, unique=false)
 * @JMS\Groups({"default"}) // Idem for all properties you want render in group "default"
 */
protected $name;

/**
 * @ORM\Column(type="string", length=255, nullable=true, unique=false)
 * @JMS\Groups({"notification"})
 */
protected $deviceId;

Then, you have to define the used Group in your controller action :

/**
 * Get list of Admins
 * @Annotations\Get("/api/v1/admins", name="list_of_admins")
 * @Annotations\View(serializerGroups={"default", "notification"}) // Here set the Group used
 * @return \FOS\RestBundle\View\View
 */
public function getAdminsAction()
{
    if(!$this->isGranted('ROLE_ADMIN')){
        throw new AccessDeniedException('Only for Admin');
    }

    $admins = $this->getDoctrine()->getRepository('AppBundle:User')->findByRole('ROLE_ADMIN');

    return $this->view(['data' => $admins], Codes::HTTP_OK);
}

Now, $admins contains only the properties exposed to the group default and notification.

You can also do it manually instead of use an annotation :

$view = $this->view($admins, 200);
$view->setSerializerGroups(array('default', 'notification'));

return $this->handleView($view);

For more informations, see Exclusion strategies .

I hope it's clear for you.