95 votes

Comment encoder les entités Doctrine en JSON dans une application Symfony 2.0 AJAX ?

Je développe une application de jeu et j'utilise Symfony 2.0. J'ai de nombreuses requêtes AJAX vers le backend. Et la plupart des réponses convertissent les entités en JSON. Par exemple :

class DefaultController extends Controller
{           
    public function launchAction()
    {   
        $user = $this->getDoctrine()
                     ->getRepository('UserBundle:User')                
                     ->find($id);

        // encode user to json format
        $userDataAsJson = $this->encodeUserDataToJson($user);
        return array(
            'userDataAsJson' => $userDataAsJson
        );            
    }

    private function encodeUserDataToJson(User $user)
    {
        $userData = array(
            'id' => $user->getId(),
            'profile' => array(
                'nickname' => $user->getProfile()->getNickname()
            )
        );

        $jsonEncoder = new JsonEncoder();        
        return $jsonEncoder->encode($userData, $format = 'json');
    }
}

Et tous mes contrôleurs font la même chose : récupérer une entité et encoder certains de ses champs en JSON. Je sais que je peux utiliser des normalisateurs et encoder toutes les entités. Mais que faire si une entité a des liens cycliques avec d'autres entités ? Ou si le graphe des entités est très grand ? Avez-vous des suggestions ?

Je pense à un schéma d'encodage pour les entités... ou à l'utilisation de NormalizableInterface pour éviter de faire du vélo..,

156voto

SparSio Points 611

Avec php5.4, vous pouvez maintenant faire :

use JsonSerializable;

/**
* @Entity(repositoryClass="App\Entity\User")
* @Table(name="user")
*/
class MyUserEntity implements JsonSerializable
{
    /** @Column(length=50) */
    private $name;

    /** @Column(length=50) */
    private $login;

    public function jsonSerialize()
    {
        return array(
            'name' => $this->name,
            'login'=> $this->login,
        );
    }
}

Et ensuite appeler

json_encode(MyUserEntity);

4 votes

C'est une excellente solution si vous cherchez à réduire au minimum vos dépendances envers d'autres paquets...

7 votes

Qu'en est-il des entités liées ?

8 votes

Cela ne semble pas fonctionner avec les collections d'entités (c'est-à-dire : OneToMany relations)

85voto

sofia Points 2499

Une autre option consiste à utiliser le JMSSerializerBundle . Dans votre contrôleur, vous faites alors

$serializer = $this->container->get('serializer');
$reports = $serializer->serialize($doctrineobject, 'json');
return new Response($reports); // should be $reports as $doctrineobject is not serialized

Vous pouvez configurer la façon dont la sérialisation est effectuée en utilisant des annotations dans la classe de l'entité. Voir la documentation dans le lien ci-dessus. Par exemple, voici comment exclure les entités liées :

 /**
* Iddp\RorBundle\Entity\Report
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="Iddp\RorBundle\Entity\ReportRepository")
* @ExclusionPolicy("None")
*/
....
/**
* @ORM\ManyToOne(targetEntity="Client", inversedBy="reports")
* @ORM\JoinColumn(name="client_id", referencedColumnName="id")
* @Exclude
*/
protected $client;

7 votes

Vous devez ajouter utiliser JMS \SerializerBundle\Annotation\ExclusionPolicy ; utiliser JMS \SerializerBundle\Annotation\Exclude ; dans votre entité et installez JMSSerializerBundle pour que cela fonctionne.

3 votes

Cela fonctionne très bien si vous le changez en : return new Response($reports) ;

7 votes

Puisque les annotations ont été déplacées hors du paquet, les déclarations d'utilisation correctes sont maintenant : use JMS \Serializer\Annotation\ExclusionPolicy ; utiliser JMS \Serializer\Annotation\Exclude ;

40voto

webda2l Points 3057

Vous pouvez encoder automatiquement en Json, votre entité complexe avec :

use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;

$serializer = new Serializer(array(new GetSetMethodNormalizer()), array('json' => new 
JsonEncoder()));
$json = $serializer->serialize($entity, 'json');

3 votes

Merci, mais j'ai une entité Player qui a un lien avec la collection d'entités Game et chaque entité Game a un lien avec les joueurs qui y ont joué. Quelque chose comme ceci. Et pensez-vous que GetSetMethodNormalizer fonctionnera correctement (il utilise un algorithme récursif) ?

2 votes

Oui c'est récursif et c'était mon problème dans mon cas. Donc, pour des entités spécifiques, vous pouvez utiliser le CustomNormalizer et son NormalizableInterface comme vous semblez le savoir.

2 votes

Lorsque j'ai essayé cela, j'ai obtenu "Fatal error : Allowed memory size of 134217728 bytes exhausted (tried to allocate 64 bytes) in /home/jason/pressbox/vendor/symfony/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php on line 44". Je me demande pourquoi ?

13voto

jerome Points 96

Pour compléter la réponse : Symfony2 est livré avec un wrapper autour de json_encode : Symfony/Component/HttpFoundation/JsonResponse

Utilisation typique dans vos contrôleurs :

...
use Symfony\Component\HttpFoundation\JsonResponse;
...
public function acmeAction() {
...
return new JsonResponse($array);
}

10voto

rkmax Points 3436

J'ai trouvé que la solution au problème de sérialisation des entités était la suivante :

#config/config.yml

services:
    serializer.method:
        class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer
    serializer.encoder.json:
        class: Symfony\Component\Serializer\Encoder\JsonEncoder
    serializer:
        class: Symfony\Component\Serializer\Serializer
        arguments:
            - [@serializer.method]
            - {json: @serializer.encoder.json }

dans mon contrôleur :

$serializer = $this->get('serializer');

$entity = $this->get('doctrine')
               ->getRepository('myBundle:Entity')
               ->findOneBy($params);

$collection = $this->get('doctrine')
               ->getRepository('myBundle:Entity')
               ->findBy($params);

$toEncode = array(
    'response' => array(
        'entity' => $serializer->normalize($entity),
        'entities' => $serializer->normalize($collection)
    ),
);

return new Response(json_encode($toEncode));

autre exemple :

$serializer = $this->get('serializer');

$collection = $this->get('doctrine')
               ->getRepository('myBundle:Entity')
               ->findBy($params);

$json = $serializer->serialize($collection, 'json');

return new Response($json);

vous pouvez même le configurer pour désérialiser les tableaux en http://api.symfony.com/2.0

3 votes

Il y a un article dans le livre de recettes sur l'utilisation du composant Serializer dans Symfony 2.3+, car vous pouvez maintenant activer le composant intégré : symfony.com/doc/current/cookbook/serializer.html

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X