Сериализатор, использующий нормализатор, ничего не возвращает при использовании setCircularReferenceHandler

Вопрос:

Почему мой ответ возвращает «пустой», когда я устанавливаю обратный вызов setCircularReferenceHandler?

ИЗМЕНИТЬ:

Может показаться, что он ничего не возвращает, но устанавливает для заголовка значение 500 Internal Server Error. Это сбивает с толку, поскольку Symfony должен отправить какой-то ответ об ошибке, касающийся ошибки?

Я обернул $json = $serializer->serialize($data, 'json'); в try/catch, но явная ошибка не выдается, поэтому ничего не перехватывается. Любые идеи будут действительно полезны.

Контекст:

При запросе Entity Media я получаю пустой ответ. Entity Media сопоставляется (с Doctrine) с Entity Author. Поскольку они связаны, при попытке сериализации могут возникать неопределенные циклы.

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

Ошибка:

Это ошибка, которую я получаю, когда я НЕ устанавливаю обработчик циклических ссылок:

Обнаружена циклическая ссылка при сериализации объекта класса Proxies__CG__\AppBundle\Entity\Author\Author (настроенное ограничение: 1) (внутренняя ошибка сервера 500)

Теперь эта ошибка полностью ожидаема, так как мой Entity Author указывает на Entity Media при первоначальном запросе Media ( Media -> Author -> Media )

   class Author implements JsonSerializable {

     //Properties, Getters and setters here

     /**
     * Specify data which should be serialized to JSON
     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
     * @return mixed data which can be serialized by <b>json_encode</b>,
     * which is a value of any type other than a resource.
     * @since 5.4.0
     */
    function jsonSerialize()
    {
        return [
            "title"     => $this->getTitle(),
            "id"        => $this->getId(),
            "firstname" => $this->getFirstname(),
            "lastname"  => $this->getLastname(),

            //This is the problem right here. Circular reference.
            "medias"    => $this->getAuthorsMedia()->map(function($object){
                return $object->getMedia();
            })
        ];
    }
  }

Что я пробовал:

Мои сущности реализуют интерфейс JsonSerializable, поэтому я определяю, какие атрибуты возвращаются (что и требуется для JsonSerializeNormalizer). Это работает полностью, когда я удаляю свойство "medias" в классе автора, все работает.

Вот как я использую свой серлиазер с нормализатором.

/**
 * @Route("/media")
 * Class MediaController
 * @package BackBundle\Controller\Media
 */
class MediaController extends Controller
{

    /**
     * @Route("")
     * @Method({"GET"})
     */
    public function listAction(){
        /** @var MediaService $mediaS */
        $mediaS= $this->get("app.media");

        /** @var array $data */
        $data = $mediaS->getAll();
        $normalizer = new JsonSerializableNormalizer();
        $normalizer->setCircularReferenceLimit(1);
        $normalizer->setCircularReferenceHandler(function($object){
            return $object->getId();
        });
        $serializer = new Serializer([$normalizer], [new JsonEncoder()]);

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

        return new Response($json);
    }

}

Открыта проблема Github


person kemicofa ghost    schedule 05.09.2017    source источник
comment
Пробовали ли вы добавить метаданные и включить параметр enable_max_depth, чтобы обойти это? См. раздел обработка глубины сериализации.   -  person Nicolai Fröhlich    schedule 05.09.2017
comment
Сколько уровней глубины вы хотите сериализовать?   -  person Nicolai Fröhlich    schedule 05.09.2017
comment
enable_max_depth используется всем, что расширяет AbstractObjectNormalizer. JsonSerializableNormalizer наследуется напрямую от AbstractNormalizer и вообще не использует эту опцию.   -  person Kal Zekdor    schedule 08.09.2017


Ответы (1)


Я попытался воспроизвести вашу ошибку, и у меня все сработало, как и ожидалось (см. примеры кода ниже).

Итак, ваш setCircularReferenceHandler() работает нормально.

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

Тест (вместо контроллера):

class SerializerTest extends \PHPUnit\Framework\TestCase
{
    public function testIndex()
    {
        $media = new Media();
        $author = new Author();

        $media->setAuthor($author);
        $author->addMedia($media);
        $data = [$media];

        $normalizer = new JsonSerializableNormalizer();
        $normalizer->setCircularReferenceLimit(1);
        $normalizer->setCircularReferenceHandler(function($object){
            /** @var Media $object */
            return $object->getId();
        });
        $serializer = new Serializer([$normalizer], [new JsonEncoder()]);

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

        $this->assertJson($json);
        $this->assertCount(1, json_decode($json));
    }
}

Медиаобъект

class Media implements \JsonSerializable
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var Author
     *
     * @ORM\ManyToOne(targetEntity="Author", inversedBy="medias")
     * @ORM\JoinColumn(name="author_id", referencedColumnName="id")
     */
    private $author;

    /**
     * {@inheritdoc}
     */
    function jsonSerialize()
    {
        return [
            "id"        => $this->getId(),
            "author"    => $this->getAuthor(),
        ];
    }
    //todo: here getter and setters, generated by doctrine
}

Автор

class Author implements \JsonSerializable
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var Media[]
     *
     * @ORM\OneToMany(targetEntity="Media", mappedBy="author")
     */
    private $medias;

    /**
     * {@inheritdoc}
     */
    function jsonSerialize()
    {
        return [
            "id"        => $this->getId(),
            "medias"    => $this->getMedias(),
        ];
    }
    //todo: here getter and setters, generated by doctrine
}
person Maksym Moskvychev    schedule 09.09.2017