Jak kodować encje doktryny do JSON w aplikacji Symfony 2.0 AJAX?

Rozwijam aplikację do gry i korzystam z Symfony 2.0. Mam wiele żądań AJAX do zaplecza. A Więcej odpowiedzi konwertuje encję na JSON. Na przykład:

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');
    }
}

I wszystkie moje Kontrolery robią to samo: pobierają encję i kodują niektóre z jej pól do JSON. Wiem, że mogę używać normalizatorów i kodować wszystkie entitities. Ale co, jeśli jednostka cyklicznie łączy się z inną jednostką? Czy Wykres Bytów jest bardzo duży? Masz jakieś sugestie?

Myślę o jakimś kodowaniu schemat dla Bytów... lub za pomocą NormalizableInterface, aby uniknąć jazdy na rowerze..,

Author: likeitlikeit, 2011-07-15

12 answers

Inną opcją jest użycie JMSSerializerBundle. W kontrolerze wykonujesz

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

Możesz skonfigurować sposób serializacji za pomocą adnotacji w klasie encji. Zobacz dokumentację w linku powyżej. Na przykład, oto jak można wykluczyć powiązane encje:

 /**
* 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;
 79
Author: Sofia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-03 09:22:07

Z php5. 4 Teraz możesz zrobić:

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,
        );
    }
}

A następnie wywołaj

json_encode(MyUserEntity);
 141
Author: SparSio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-04 08:23:37

Możesz automatycznie kodować do Json, Twojej złożonej jednostki za pomocą:

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');
 39
Author: webda2l,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-31 03:06:41

Aby uzupełnić odpowiedź: Symfony2 zawiera wrapper wokół json_encode: Symfony / Component/HttpFoundation / JsonResponse

Typowe użycie w kontrolerach:

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

Hope this helps

J

 11
Author: jerome,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-09 15:38:15

Znalazłem rozwiązanie problemu serializacji Bytów było następujące:

#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 }

W moim kontrolerze:

$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));

Inny przykład:

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

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

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

return new Response($json);

Możesz nawet skonfigurować go do deserializacji tablic w http://api.symfony.com/2.0

 10
Author: rkmax,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-21 17:04:39

Musiałem rozwiązać ten sam problem: JSON-kodowanie encji ("użytkownik") mającej dwukierunkowy związek jeden do wielu z inną encją ("lokalizacja").

Próbowałem kilku rzeczy i myślę, że teraz znalazłem najlepsze akceptowalne rozwiązanie. Pomysł polegał na użyciu tego samego kodu, co napisany przez Davida, ale w jakiś sposób przechwycić nieskończoną rekurencję, nakazując Normalizatorowi, aby w pewnym momencie przestał.

Nie chciałem implementować niestandardowego normalizera, ponieważ ten GetSetMethodNormalizer jest miłym podejście moim zdaniem (oparte na refleksji itp.). Więc zdecydowałem się na podklasę, co nie jest trywialne na pierwszy rzut oka, ponieważ metoda, aby powiedzieć, czy dołączyć właściwość (isGetMethod) jest prywatna.

Ale można nadpisać metodę normalizacji, więc przechwyciłem w tym momencie, po prostu wyłączając właściwość, która odwołuje się do "Location" - więc pętla inifinite jest przerywana.

W kodzie wygląda to tak:

class GetSetMethodNormalizer extends \Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer {

    public function normalize($object, $format = null)
    {
        // if the object is a User, unset location for normalization, without touching the original object
        if($object instanceof \Leonex\MoveBundle\Entity\User) {
            $object = clone $object;
            $object->setLocations(new \Doctrine\Common\Collections\ArrayCollection());
        }

        return parent::normalize($object, $format);
    }

} 
 6
Author: oxy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-27 17:41:25

Miałem ten sam problem i postanowiłem stworzyć własny koder, który sam sobie poradzi z rekurencją.

Stworzyłem klasy, które implementują Symfony\Component\Serializer\Normalizer\NormalizerInterface, oraz usługę, która przechowuje wszystkie NormalizerInterface.

#This is the NormalizerService

class NormalizerService 
{

   //normalizer are stored in private properties
   private $entityOneNormalizer;
   private $entityTwoNormalizer;

   public function getEntityOneNormalizer()
   {
    //Normalizer are created only if needed
    if ($this->entityOneNormalizer == null)
        $this->entityOneNormalizer = new EntityOneNormalizer($this); //every normalizer keep a reference to this service

    return $this->entityOneNormalizer;
   }

   //create a function for each normalizer



  //the serializer service will also serialize the entities 
  //(i found it easier, but you don't really need it)
   public function serialize($objects, $format)
   {
     $serializer = new Serializer(
            array(
                $this->getEntityOneNormalizer(),
                $this->getEntityTwoNormalizer()
            ),
            array($format => $encoder) );

     return $serializer->serialize($response, $format);
}

Przykład Normalizatora:

use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

class PlaceNormalizer implements NormalizerInterface {

private $normalizerService;

public function __construct($normalizerService)
{
    $this->service = normalizerService;

}

public function normalize($object, $format = null) {
    $entityTwo = $object->getEntityTwo();
    $entityTwoNormalizer = $this->service->getEntityTwoNormalizer();

    return array(
        'param' => object->getParam(),
        //repeat for every parameter
        //!!!! this is where the entityOneNormalizer dealt with recursivity
        'entityTwo' => $entityTwoNormalizer->normalize($entityTwo, $format.'_without_any_entity_one') //the 'format' parameter is adapted for ignoring entity one - this may be done with different ways (a specific method, etc.)
    );
}

}

W kontrolerze:

$normalizerService = $this->get('normalizer.service'); //you will have to configure services.yml
$json = $normalizerService->serialize($myobject, 'json');
return new Response($json);

Pełny kod znajduje się tutaj: https://github.com/progracqteur/WikiPedale/tree/master/src/Progracqteur/WikipedaleBundle/Resources/Normalizer

 6
Author: Julien Fastré,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-16 17:11:12

To jest bardziej aktualizacja (dla Symfony v:2.7 + i JmsSerializer v:0.13.*@dev) , Aby uniknąć tego, że Jms próbuje załadować i serializować cały wykres obiektu (lub w przypadku relacji cyklicznych ..)

Model:

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\ExclusionPolicy;  
use JMS\Serializer\Annotation\Exclude;  
use JMS\Serializer\Annotation\MaxDepth; /* <=== Required */
/**
 * User
 *
 * @ORM\Table(name="user_table")
///////////////// OTHER Doctrine proprieties //////////////
 */
 public class User
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected   $id;

    /**
     * @ORM\ManyToOne(targetEntity="FooBundle\Entity\Game")
     * @ORM\JoinColumn(nullable=false)
     * @MaxDepth(1)
     */
    protected $game;
   /*
      Other proprieties ....and Getters ans setters
      ......................
      ......................
   */

Wewnątrz akcji:

use JMS\Serializer\SerializationContext;
  /* Necessary include to enbale max depth */

  $users = $this
              ->getDoctrine()
              ->getManager()
              ->getRepository("FooBundle:User")
              ->findAll();

  $serializer = $this->container->get('jms_serializer');
  $jsonContent = $serializer
                   ->serialize(
                        $users, 
                        'json', 
                        SerializationContext::create()
                                 ->enableMaxDepthChecks()
                  );

  return new Response($jsonContent);
 6
Author: mboullouz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-06 21:15:33

W Symfony 2.3

/app / config / config.yml

framework:
    # сервис конвертирования объектов в массивы, json, xml и обратно
    serializer:
        enabled: true

services:
    object_normalizer:
        class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer
        tags:
        # помечаем к чему относится этот сервис, это оч. важно, т.к. иначе работать не будет
          - { name: serializer.normalizer }

I przykład dla kontrolera:

/**
 * Поиск сущности по ИД объекта и ИД языка
 * @Route("/search/", name="orgunitSearch")
 */
public function orgunitSearchAction()
{
    $array = $this->get('request')->query->all();

    $entity = $this->getDoctrine()
        ->getRepository('IntranetOrgunitBundle:Orgunit')
        ->findOneBy($array);

    $serializer = $this->get('serializer');
    //$json = $serializer->serialize($entity, 'json');
    $array = $serializer->normalize($entity);

    return new JsonResponse( $array );
}

Ale problemy z polem typu \ DateTime pozostaną.

 5
Author: Lebnik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-28 15:04:11

Jeśli używasz Symfony 2.7 lub nowszego , i nie chcesz dołączać żadnego dodatkowego pakietu do serializacji, może możesz wykonać ten sposób, aby seializować encje doktryny do json-

  1. W moim (wspólnym, rodzicowym) kontrolerze mam funkcję, która przygotowuje serializer

    use Symfony\Component\Serializer\Encoder\JsonEncoder;
    use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
    use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
    use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
    use Symfony\Component\Serializer\Serializer;
    
    // -----------------------------
    
    /**
     * @return Serializer
     */
    protected function _getSerializer()
    {  
        $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
        $normalizer           = new ObjectNormalizer($classMetadataFactory);
    
        return new Serializer([$normalizer], [new JsonEncoder()]);
    }
    
  2. Następnie użyj go do serializacji obiektów do JSON

    $this->_getSerializer()->normalize($anEntity, 'json');
    $this->_getSerializer()->normalize($arrayOfEntities, 'json');
    
Zrobione! Ale możesz potrzebować drobnego dostrojenia. Na przykład -
 4
Author: Anis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-06 09:07:04

Kiedy musisz utworzyć wiele punktów końcowych REST API na Symfony, najlepszym sposobem jest użycie następującego stosu wiązek:

  1. JMSSerializerBundle do serializacji Bytów doktrynalnych
  2. FOSRestBundle pakiet do odsłuchu odpowiedzi. Może również generować definicję tras na podstawie nazwy kontrolera/akcji.
  3. NelmioApiDocBundle do automatycznego generowania dokumentacji online i sandboxa (który pozwala przetestować punkt końcowy bez żadnych narzędzie zewnętrzne).

Kiedy skonfigurujesz wszystko poprawnie, kod encji będzie wyglądał następująco:

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;

/**
 * @ORM\Table(name="company")
 */
class Company
{

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255)
     *
     * @JMS\Expose()
     * @JMS\SerializedName("name")
     * @JMS\Groups({"company_overview"})
     */
    private $name;

    /**
     * @var Campaign[]
     *
     * @ORM\OneToMany(targetEntity="Campaign", mappedBy="company")
     * 
     * @JMS\Expose()
     * @JMS\SerializedName("campaigns")
     * @JMS\Groups({"campaign_overview"})
     */
    private $campaigns;
}

Następnie, kod w kontrolerze:

use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use FOS\RestBundle\Controller\Annotations\View;

class CompanyController extends Controller
{

    /**
     * Retrieve all companies
     *
     * @View(serializerGroups={"company_overview"})
     * @ApiDoc()
     *
     * @return Company[]
     */
    public function cgetAction()
    {
        return $this->getDoctrine()->getRepository(Company::class)->findAll();
    }
}

Korzyści z takiego ustawienia to:

  • @JMS\Expose() adnotacje w encji mogą być dodawane do prostych pól i do dowolnych typów relacji. Istnieje również możliwość ujawnienia wyniku wykonania jakiejś metody (użyj adnotacji @JMS\VirtualProperty ())
  • z grupami serializacyjnymi możemy sterować polami odsłoniętymi w różnych sytuacjach.
  • Kontrolery są bardzo proste. Metoda akcji może bezpośrednio zwracać encje lub tablicę encji i będą one automatycznie serializowane.
  • i @Apidoc () pozwala przetestować punkt końcowy bezpośrednio z przeglądarki, bez żadnego klienta REST lub kodu JavaScript
 3
Author: Maksym Moskvychev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-13 15:32:05

Teraz możesz również użyć Doctrine ORM Transformations do konwersji encji na zagnieżdżone tablice skalarów i z powrotem

 2
Author: ScorpioT1000,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-17 08:25:05