Так вышло, что всю свою недолгую карьеру я занимаюсь разработкой API для мобильных приложений и сайтов на Symfony2. Каждый раз открываю для себя все новые знания, которые кому-то покажутся очевидными, а кому-то помогут сэкономить не мало времени. Об этих знаниях и пойдет речь.

Формы


Вообще использовать дефолтные формы для API не лучшая идея, но если вы все же решились, то вам необходимо не забывать о некоторых особенностях. Изначально формы в symfony делались для обычных сайтов, где фронтенд и бекенд объединены.

Первая проблема возникает с entity type. Когда вы отсылаете запрос к методу, который использует entity type в формах – сначала достаются все сущности указанного класса, и только потом запрос на получение нужной сущности по отправленному id. Многие не знают об этом и очень удивляются, почему метод работает так долго.

Пример решения
EntityType.php
<?php

namespace App\CommonBundle\Form\Type;

use App\CommonBundle\Form\DataTransformer\EntityDataTransformer;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class EntityType extends AbstractType
{
    private $em;

    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults([
            'field' => 'id',
            'class' => null,
            'compound' => false
        ]);

        $resolver->setRequired([
            'class',
        ]);
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addModelTransformer(new EntityDataTransformer($this->em, $options['class'], $options['field']));
    }

    public function getName()
    {
        return 'entity';
    }
}


EntityDataTransformer.php
<?php

namespace App\CommonBundle\Form\DataTransformer;

use Doctrine\ORM\EntityManager;
use Symfony\Component\Form\DataTransformerInterface;

class EntityDataTransformer implements DataTransformerInterface
{
    private $em;
    private $entityName;
    private $fieldName;

    public function __construct(EntityManager $em, $entityName, $fieldName)
    {
        $this->em = $em;
        $this->entityName = $entityName;
        $this->fieldName = $fieldName;
    }

    public function transform($value)
    {
        return null;
    }

    public function reverseTransform($value)
    {
        if (!$value) {
            return null;
        }

        return $this->em->getRepository($this->entityName)->findOneBy([$this->fieldName => $value]);
    }
}


services.yml
    common.form.type.entity:
        class: App\CommonBundle\Form\Type\EntityType
        arguments: [@doctrine.orm.entity_manager]
        tags:
            - { name: form.type, alias: entity }



Вторая проблема возникает с checkbox type, который пытаются использовать для булевых значений, но особенность работы этого типа такова, что если ключ существует и он не пустой, то вернется true.

Пример решения
BooleanType.php
<?php

namespace App\CommonBundle\Form\Type;

use App\CommonBundle\Form\DataTransformer\BooleanDataTransformer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;

class BooleanType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addViewTransformer(new BooleanDataTransformer());
    }

    public function getParent()
    {
        return 'text';
    }

    public function getName()
    {
        return 'boolean';
    }
}


BooleanDataTransformer.php
<?php

namespace App\CommonBundle\Form\DataTransformer;

use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;

class BooleanDataTransformer implements DataTransformerInterface
{
    public function transform($value)
    {
        return null;
    }

    public function reverseTransform($value)
    {
        if ($value === "false" || $value === "0" || $value === "" || $value === 0) {
            return false;
        }

        return true;
    }
}


services.yml
    common.form.type.boolean:
        class: App\CommonBundle\Form\Type\BooleanType
        tags:
            - { name: form.type, alias: boolean }



JMS Serializer


Во всех статьях про создание API советуется именно это замечательное расширение. Люди смотрят простенький пример, где у сущностей есть две serialization groups: details и list, и начинают у каждой сущности использовать именно эти названия и все замечательно работает, пока не попадется какая-нибудь связанная сущность, у которой группы названы точно так же и выводится очень много лишней, не нужной информации. Также это может уводить в бесконечный цикл при сериализации, если обе модели выводят связь друг с другом.

Пример неправильного использования
News.php
<?php

use JMS\Serializer\Annotation as Serialization;

class News
{
    /**
     * @Serialization\Groups({"details", "list"})
     */
    protected $id;

    /**
     * @Serialization\Groups({"details", "list"})
     */
    protected $title;

    /**
     * @Serialization\Groups({"details", "list"})
     */
    protected $text;

    /**
     * Связь с сущностью User
     *
     * @Serialization\Groups({"details", "list"})
     */
    protected $author;
}


User.php
<?php

use JMS\Serializer\Annotation as Serialization;

class User
{
    /**
     * @Serialization\Groups({"details", "list"})
     */
    protected $id;

    /**
     * @Serialization\Groups({"details", "list"})
     */
    protected $name;

    /** Огромный список полей отмеченных группами list и details */
}


NewsController.php
<?php

class NewsController extends BaseController
{
    /**
     * @SerializationGroups({"details"})
     * @Route("/news/{id}", requirements={"id": "\d+"})
     */
    public function detailsAction(Common\Entity\News $entity)
    {
        return $entity;
    }
}




В примере видно, что при получении новости в поле author будут все поля, которые в User с группой details, что явно не входит в наши планы. Казалось бы, очевидно, что так делать нельзя, но, к моему удивлению, так делают многие.

Я советую именовать группы как %entity_name%_details, %entity_name%_list и %entity_name%_embed. Последняя нужна как раз для тех случаев, когда есть связанные сущности и мы хотим вывести какую-то связанную сущность в списке.

Пример правильного использования
News.php
<?php

use JMS\Serializer\Annotation as Serialization;

class News
{
    /**
     * @Serialization\Groups({"news_details", "news_list"})
     */
    protected $id;

    /**
     * @Serialization\Groups({"news_details", "news_list"})
     */
    protected $title;

    /**
     * @Serialization\Groups({"news_details", "news_list"})
     */
    protected $text;

    /**
     * Связь с сущностью User
     *
     * @Serialization\Groups({"news_details", "news_list"})
     */
    protected $author;
}


User.php
<?php

use JMS\Serializer\Annotation as Serialization;

class User
{
    /**
     * @Serialization\Groups({"user_details", "user_list", "user_embed"})
     */
    protected $id;

    /**
     * @Serialization\Groups({"user_details", "user_list", "user_embed"})
     */
    protected $name;

    /** Огромный список полей, которые отмечены группами user_list и user_details */
}


NewsController.php
<?php

class NewsController extends BaseController
{
    /**
     * @SerializationGroups({"news_details", "user_embed"})
     * @Route("/news/{id}", requirements={"id": "\d+"})
     */
    public function detailsAction(Common\Entity\News $entity)
    {
        return $entity;
    }
}




При таком подходе будут только необходимые поля, к тому же это можно будет использовать в других местах, где тоже нужно вывести краткую информацию о пользователе.

Конец


На самом деле, подобных советов еще очень много и если вам будет интересно, я с радостью ими поделюсь.

Комментарии (40)


  1. Fesor
    15.05.2015 00:29
    +2

    Когда открывал статью то думал что первым же пунктом увижу что-то по поводу форм. Обрадовался когда увидел хотя бы упоминания что формы юзать в контексте API не ок (да, когда-то я думал что ок).

    JMS Serializer — самое распространенное решение не лишенное проблем. По сути оно навязывает решение проблем, которое само и привносит.

    Далее я надеялся увидеть альтернативные подходы… но увы и ах, конец… на самом интересном месте.

    Я все же думаю что стоит выкинуть JmsSerializer и выкидывать простенькие DTO из и в сервисный слой. Хоть в виде ассоциативных массивов, хоть в виде классов с сеттерами, хоть в виде stdObject.

    Я бы хотел увидеть, скажем… отложенное формирование view во фронт контроллере, что бы можно было флаш доктрины туда же вынести.


    1. lowadka Автор
      15.05.2015 00:39

      А можно пример какой-нибудь, как вы работаете без JMS Serializer?

      Насчет форм:
      У нас все запросы, которые содержат в себе какие-либо параметры, обязательно мапятся на модели. Даже если это какой-нибудь список с фильтрами. Поэтому мы отказались полностью от форм и просто в моделях пишем что-то подобное:

      class IntakeFilter
      {
          protected $user;
      
          /**
           * @RequestMapper(type="integer")
           */
          protected $limit = 20;
      
          /**
           * @RequestMapper(type="integer")
           */
          protected $offset = 0;
      
          /**
           * @RequestMapper(type="boolean")
           */
          protected $isCompleted;
      
          /**
           * @RequestMapper(type="string")
           * @Assert\Length(min=2)
           */
          protected $query;
      }
      


      И в контроллере:
      public function actionList() 
      {
          $model = $this->handleRequest(new App\Model\IntakeFilter($this->getUser());
      
          return $repository->findByFilter($model);
      }
      


      1. Fesor
        15.05.2015 01:08
        +1

        Ну не сказать что я отказался от JmsSerializer, только на домашних проектах пока что. Но как-то так:

        class SomeEntity {
            public function getShortInfoView() {
                
                // думаю эту штуку можно сократить для пущей читабельности. 
                // А если у нас будет много вьюшек, можно DRY-ить эти вещи
                // или если дела будут совсем плохи, делать нормальные объекты DTO.
                return [
                     'id' => $this->id,
                     'name' => $this->name,
                     'some_info' => $this->someInfo,
                     'connected_entities' => $this->connectedEntities->map(function (SomeConntectedEntity $entity) {
                          return $entity->getShortInfoView();
                     })->toArray();
                ];
            }
        }
        
        class SomeController {
        
            public function addSomeAction(Request $request) { 
                 // обычно это происходит во фронт контроллере для всех запросов с Content-type application/json
                 // тут просто для наглядности
                 $dto = json_encode($request->getContent());
        
                 // у меня умерла под конец дня фантазия, так что простите за именования в духе some creator
                 $shortInfoView = $this->get('app.some_creator')->createSome($dto);
         
                 // это так же происходит во фронт контроллере но мне лень
                 // что до ситуация с ID и их получением до flush
                 // я использую postgresql и тамошние последовательности, которые прекрасно это дело разруливают 
                 // а автоинкремент mysql это зло
                 $this->get('doctrine.orm.entity_manager')->flush();
        
                 // обычно из контроллера я возвращаю только данные или какой-то View объект в духе FostRest
                 return new JsonResponse($shortInfoView);
            }
        }
        
        class SomeCreator 
        {
             private $someRepository;
        
             public function __construct(SomeRepository $repository) {
                  $this->someRepository = $repository;
             }
        
             public function create($dto) {
             
                 // тут можно провалидировать $dto но лень
                 // всеравто чуть что конструктор энтити бросит исключение
                 // ну и лучше что бы $dto был \stdObject. 
                 // Тогда потом можно будет в случае усложнения логики 
                 // переделать его на нормальный DTO объект со своим типом
                 $entity = new SomeEntity($dto['name'], $dto['short_info']);
        
                 $this->someRepository->add($entity);
                 
                 return $entity->getShortInfoView();
             }
        }
        


        вот как-то так. Заметте что внутри сервисного слоя заключена все знание о том как наша бизнес логика работает и ни одного упоминания о доктрине. Так же, поскольку все энтити крутятся в unit-of-work, а коммит транзакции происходит вне оного, не очень безопасно возвращать в контроллер саму энтити, так как можно случайно там поменять состояние оной. Лучше плюнуть наружу DTO, что мы и делаем. А уж с простым массивом справится и json_encode.

        Как-то так. Пока вариантов лучше я не придумал и не знаю…


        1. Fesor
          15.05.2015 01:16

          Ммм… опечатался слегка… вместо

          $entity = new SomeEntity($dto['name'], $dto['short_info']);
          

          надо
          $entity = new SomeEntity($dto['name'], $dto['some_info']);
          


        1. lowadka Автор
          15.05.2015 16:55

          Интересный вариант, схема правда та же самая, но инструменты другие. Мне нравится, что это работает довольно быстро, ибо выбрасываем jms serializaer, но больше плюсов, честно говоря, не вижу :(


          1. Fesor
            15.05.2015 17:13

            Как же не видите? Все явно, можно полностью проследить всю логику от момента получения данных запроса до вывода наружу. И все это из сервисного слоя приложения. Полностью вся бизнес логика по одной фиче в одном месте. И все можно покрыть тестами. Как по мне это намного более весомый аргумент чем производительность.


          1. Fesor
            15.05.2015 17:18

            А ну и еще, я указывал это в комментах в коде но может вы не обратили внимание. У меня обычно flush выполняется непосредственно перед отправкой ответа, во фронт контроллере. И если мы выплюнем сущность из сервиса в контроллер, кто-то может изменить (случайно или специально) состояние сущности и мы получим баги. При моем варианте же возвращается DTO, и если мы чего поменяем в нем, то как бы и пофигу.


    1. lowadka Автор
      15.05.2015 00:58

      И еще очень интересно, о каких именно проблемах с JMS Serializer вы говорите, ибо пока что это очень удобно. Контроллеры выглядят примерно так:

          /**
           * @Common\Annotation\SerializationGroup("clinic_details")
           * @Common\Annotation\HttpStatus(201)
           *
           * @Method("POST")
           * @Route
           */
          public function addAction()
          {
              $entity = $this->handleRequest(new Common\Entity\Clinic($this->getUser());
              $this->persist($entity, true);
      
              return $entity;
          }
      


      1. Fesor
        15.05.2015 01:12

        группы сериализации. Это очень мощный инструмент но он вносит в умы людей много смуты. Они начинают оперировать целыми сущностями в рамках какой-то бизнес логики, а не частью оной. То есть по сути необходимость в них есть только за счет того что никто не хочет писать скучный код формирующий DTO или вообще не понимает зачем это нужно и в чем крутость. У меня вот и symfony/validation не используется). Ну а для сложной иерархии состояний можно подключить еще и Value Object-ы, и разруливать это дело на уровне контроллера. Код можно DRY-ить, он явный, можно посадить любого человека знающего PHP и он сразу сможет писать бизнес логику. Ну и все можно покрыть юнит тестами, со штуками типа JMSSerializer, FosRest и т.д. спасают только интеграционные и функциональные тесты.


        1. alxsad
          15.05.2015 12:32

          Мне кажется у JMSSerializer слишком много плюшек, чтобы от него отказываться, а проблему с сериализационными группами надо решать не выделением имен по каким-то действиям (list, details), а используя предметную область (default, secure).


          1. korotovsky
            15.05.2015 12:37

            Мы у себя сделали _fields парметр, как во всех API. Оно ни в коем случае не заменяет группы, которые мы используем чтобы разделить рендеринг приватной информации от общей.


          1. Fesor
            15.05.2015 13:59

            Проблемы с выделением групп вообще нет, просто JmsSerializer позволяет тебе выплюнуть наружу целиком твои сущности и дальше разруливать сериализацию гурппами, вместо того что бы выплюнуть DTO где есть все что нужно и ничего лишнего. Оно конечно удобно, позволяет тебе не писать туповатый бойлерплейт, но многие слишком много на него вешают. Доходит до того что весь контроллер состоит из десериализации и персиста, а вся логика размазана по хэндлерам и т.д


    1. ruFog
      18.05.2015 15:31

      Можно вкратце объяснить почему формы в контексте API лучше не использовать? Это же просто меппер реквеста на объект. Медленно?


      1. Fesor
        18.05.2015 19:27

        Не медленно, у вас нету формы, потому использовать Symfony/forms вне форм не целесообразно. Обычно их используют только из-за хорошей интеграции с доктриной из коробки.


        1. sqrt
          19.05.2015 11:06

          эээ что вы подразумеваете под

          хорошей интеграции с доктриной из коробки
          ?


          1. Fesor
            19.05.2015 11:49

            Угадывание типов на основе доступной информации о мэппинге подтянутой из доктрины. Я вот о чем.


            1. sqrt
              19.05.2015 12:36

              да ну, это фича дополнительного характера. Для меня в формах в разрезе API то что я могу описать правила на получаемые данные, провалидировать их и замапить на сущность или дто.


      1. lowadka Автор
        18.05.2015 23:51

        Причин много, во первых в API нет форм, как уже говорили, во вторых, для того, чтобы формы начали более менее работать – нужно множество костылей, которых накапливается такое количество, что в какой-то момент задаешься вопросом «Ну и нафига все это?», в третьих это действительно очень медлено


        1. sqrt
          19.05.2015 11:13

          что для вас формы?


          1. lowadka Автор
            19.05.2015 11:36
            +1

            Обычные html формы, у которых другие типы данных, есть отображение, данные берутся из $_GET/$_POST


            1. sqrt
              19.05.2015 11:47

              ну вас никто не заставляет их рендерить в html. Я не вижу особых проблем почему для API не стоит использовать формы. Они хорошо подходят для маппинг, валидация реквест данных. Конечно в какой-то момент они избыточны, но согласитесь, что писать велосипед дороже. Хотя есть еще вариант использовать OptionsResolver, но не использовал, не могу по нему сказать какие плюсы или минусы.


              1. sqrt
                19.05.2015 11:48

                вот нашел линку тыц в принципе выглядит неплохо. надо попробовать


              1. Fesor
                19.05.2015 11:55

                Ок, допустим мы выбрали формы. И флоу будет таким:
                — Приняли запрос
                — Во фронт контроллере сделали json_encode тела запроса
                — Забиндили результат работы json_encode на энтити через формы
                — Валидируем состояние нашей сущности
                — забираем из формы готовую сущность, куда формы запихали все через сеттеры. (ненавижу тупые сеттеры в сущностях)

                А как можно:
                — Приняли запрос
                — Во фронт контроллере сделали десериализацию напрямую в сущность через JmsSerializer тела запроса, При этом JmsSerializer достаточно гибок, и имеет меньше ограничений.
                — Валидируем состояние нашей сущности
                — Как бы все, можно это делать в ParamConverter, и тогда в контроллер придет уже все готовое

                (хоть я и не одобряю такой подход)

                Вообще если вы берете формы, то работать напрямую с сущностью не очень хорошая идея. Да, на маленьких проектах норм, но чем сложнее проект тем больше боли они приносят. Если работать с DTO то и боли меньше с формами, и сущности красивые выходят.


                1. sqrt
                  19.05.2015 13:08

                  (ненавижу тупые сеттеры в сущностях)

                  не вы один

                  Ну а если помимо API у вас есть веб сайт, где есть такие же сущности, то тогда легче юзать форму для двух вариантов.

                  Основной мой посыл в том что формы это не панацея. Все зависит от разработчика, который должен понимать что делает, а то потом может быть очень плохо)


  1. sqrt
    15.05.2015 08:32
    +1

    По поводу entity type. Все очень просто, вы можете указать в опциях query builder и тогда он будет вытягивать с условием in. Да не очевидный кейс но он работает


  1. kovalevsky
    15.05.2015 10:25
    +1

    Я бы в контексте Symfony + API добавил бы в статью ещё и рассказ об FOSRestBundle, о какой-нибудь JWT аутентификации, обновлении и удалении токена и прочем. Ибо когда мне пришлось выполнять данную задачу, то столкнулся с кучей проблем уже на первых парах.

    Было бы интересно почитать :)


    1. sqrt
      15.05.2015 15:40
      +2

      JWT аутентификации,

      зачем он вам? лучше взять FOSOAuthServerBundle и проблем не будет.


      1. kovalevsky
        15.05.2015 16:22
        +1

        Вот видите, об этом я и говорю — информации в статье крайне мало. Мне, как минимум, было бы интересно почитать и перенять опыт :)

        За бандл отдельное спасибо ;)


        1. lowadka Автор
          15.05.2015 16:42
          +1

          Согласен с тем что информации мало, в первую очередь хотел написать статью, для того, чтобы понять насколько это интересно другим. Опыта не мало, особенно в каких-то мелочах, но со временем это все кажется очевидным и просто не понимаешь о чем писать.

          У нас на проектах, так исторически сложилось, что мы не используем бандлы для авторизации. Каждый запрос подписывается с помощью заголовка X-Access-Token, а в случае успеха юзер подставляется в security.context. Все это делается буквально в 1 listener, и мне до конца не понятно, зачем использовать что-то готовое, когда написать код дело 3 минут.

          Если интересно, могу поделиться кодом или еще лучше написать об этом в следующей статье :)


          1. kovalevsky
            15.05.2015 18:28

            Пишите :)


      1. Fesor
        15.05.2015 17:21

        Многие считают OAuth2 избыточным для REST API. Мне этот вариант лично нравится, но не могли бы вы как-то аргументировать почему не будет проблем и почему это круто? Ну мол… можно использовать старую добрую digest авторизацию которая есть в symfony из коробки.


        1. sqrt
          15.05.2015 17:34
          +1

          Я бы не сказал что OAuth2 избыточный. Все зависит от того для чего его применять. Если вы пишите апи который используете только вы, то да возможно нет смысла тянуть туда OAuth2. digest и oauth разные цели преследую. oauth в первую очередь для того чтоб авторизировать приложение.


    1. lowadka Автор
      15.05.2015 16:50
      +1

      Про FOSRestBundle, как и про многие бандлы, которые советуют использовать при создании API, я могу сказать только одно: их очень удобно использовать первое время, пока проект очень маленький и простой. Потом начинаются проблемы, из-за того, что разработчик не понимает как это работает и начинает это использовать не как это изначально, возникают проблемы и на их решение уходит большая часть времени. Но как только схема работы становится очевидной, приходит понимание, что можно сделать и лучше, не использовать кучу кода, который будет висеть мертвым грузом.

      Rest Bundle – это же по сути набор небольших скриптов и библиотек, уложенных в определенную структуру. Мне гораздо удобнее использовать их отдельно. Например: бандл для сериализации использует тот же JMS Serializer, только лишь ограничивая в его кастомизации.


      1. kovalevsky
        15.05.2015 18:24

        Да, это выбор каждого. Я предпочитаю использовать уже написанное и покрытое тестами, а не писать велосипеды. В некоторых случаях своё, конечно, оправданно, не спорю.


        1. lowadka Автор
          15.05.2015 19:59

          Если будет лишнее время, на покрытие тестами, постараюсь залить куда-нибудь свои наработки. Что-то вроде своего взгляда на то, как должен выглядеть REST Bundle, ориентированный в первую очередь на скорость разработки и гибкость.


          1. Fesor
            15.05.2015 20:51

            Да хоть так залейте, а если будет что-то интересное с покрытием кода тестами можно помочь. Гитхаб, опенсурс, все такое.


  1. witl
    06.06.2015 22:44

    Разрешите поделиться нашим опытом:
    1. JMSSerializer переусложнен и очень медленный. Даже опытные разработчики могли потерять часы, чтобы что-то подправить в API сгенерированном этим сервисом.
    Если нужно сделать что-то сложное, например поиск, это вообще ад.

    Джуниора вообще подпускать к этому бандлу нереально.
    — Выход, сделали свои трансформеры, простые.
    Код понятен, отлично дебажится и тестируется.
    Сильно ускорили разработку, поскольку просто (KISS).

    2. Symfony2 Form Component не используем.
    Хороший компонент, но когда формы обычные.
    Если речь идет о REST API, особенно если нужно сделать ~100 entity — формы начинают сильно замедлять разработку. Особенно для PATCH метода.

    p.s. Вообще сейчас мигрируем на Spring(java).
    С Symfony достаточно давно (c 2009), много кода написано.
    Да еще и афилированные партнеры SensioLabs (про это могу в привате отдельно рассказать, кому интересно).
    Но… java работает примерно от 10 до 200 раз быстрее.

    Архитектура очень похожа на SF2, работы столько же, а выхлоп в разы круче.


    1. borNfree
      08.06.2015 01:08

      JMSSerializer переусложнен и очень медленный. Даже опытные разработчики могли потерять часы, чтобы что-то подправить в API сгенерированном этим сервисом.

      Приведите, пожалуйста, конкретный пример. К слову, с такой же ситуацией я и сам недавно столкнулся, но количество времени, которое JMSSerializer сэкономил, ставит на нет некоторые недочеты и сложности.

      Если нужно сделать что-то сложное, например поиск, это вообще ад.

      А причем serializer к поиску? Если и необходимо дополнительные данные вывести (количество всех записей, текущая страница/сдвиг и т.д.) — ну добавили объект-обертку ResultSet к результирующей коллекции и полет нормальный

      Джуниора вообще подпускать к этому бандлу нереально.

      Тут сложно не согласиться.

      Но… java работает примерно от 10 до 200 раз быстрее.

      До 200 раз? Приведите, пожалуйста, пример


      1. witl
        08.06.2015 17:10

        Приведите, пожалуйста, конкретный пример. К слову, с такой же ситуацией я и сам недавно столкнулся, но количество времени, которое JMSSerializer сэкономил, ставит на нет некоторые недочеты и сложности.

        Сделайте кастомную выгрузку коллекции, чтобы там было 3-4 связи и это было скажем на хотябы на 100K записей. А также выводились данные о пагинации.

        До 200 раз? Приведите, пожалуйста, пример

        www.techempower.com/benchmarks — Искать Spring и Symfony2. На самом деле так и есть.

        p.s. Я холиварить не особенно хочу, просто выбор сделал и делюсь с вами.


        1. borNfree
          08.06.2015 18:53

          Сделайте кастомную выгрузку коллекции, чтобы там было 3-4 связи и это было скажем на хотябы на 100K записей. А также выводились данные о пагинации.


          Ну вы же не 100К записей выводите. У нас есть пример с многомиллионной таблицей — все работает. Сами связи и степень их вложенности ведь запросто контролируются аннотациями, более того можно что-нибудь сложное выбирать в POST_SERIALIZE джоином, если много запросов не устраивает.

          Про пагинацию в предыдущем комментарии ответил, нет ничего сложного и «очень медленного».

          > www.techempower.com/benchmarks — Искать Spring и Symfony2. На самом деле так и есть.

          Хотелось бы реальный пример, ну да ладно.

          PS никто не холиварит, здоровый интерес :)