Сериализация документов с помощью Doctrine MongoDB ODM

Я пытаюсь кодирует класс, обрабатывающий сериализацию документов путем чтения их метаданных. Меня вдохновила эта реализация для сущности с Doctrine ORM и модифицировали его, чтобы он соответствовал тому, как Doctrine ODM обрабатывает документы. К сожалению, что-то работает неправильно, поскольку один документ никогда не сериализуется более одного раза, даже если на него ссылаются во второй раз, что приводит к неполной сериализации.

Например, он выводит это (в json) для user1 (см. Document/User.php#L26" rel="nofollow">Документ пользователя), принадлежащий какому-то месту1 (см. Поместить документ). Затем он выводит место и пользователей, принадлежащих ему, где мы должны снова увидеть user1, но мы этого не делаем:

{
  id: "505cac0d6803fa1e15000004",
  login: "user1",
  places: [
    {
      id: "505cac0d6803fa1e15000005",
      code: "place1",
      users: [
        {
          id: "505c862c6803fa6812000000",
          login: "user2"
        }
      ]
    }
  ]
}

Я предполагаю, что это может быть связано с чем-то, что препятствует циклическим ссылкам, но есть ли способ обойти это?

Кроме того, я использую это в приложении ZF2, есть ли лучший способ реализовать это с помощью сериализатора ZF2?

Спасибо за вашу помощь.


person jhuet    schedule 05.12.2012    source источник
comment
Чтобы ответить на мой вопрос, @superdweebie создал отличную библиотеку Doctrine, которую он описал ниже и которая содержит сериализатор. Если вы используете нетерпеливую выборку, вам будут возвращены все документы. На данный момент вы можете столкнуться с проблемой циклических ссылок, но мы работаем над функцией для реализации опции максимальной глубины вложенности.   -  person jhuet    schedule 17.12.2012


Ответы (2)


У меня уже есть сериализатор, написанный для DoctrineODM. Вы можете найти его в http://github.com/superdweebie/DoctrineExtensions — посмотрите в lib/Sds/DoctrineExtensions/Serializer.

Если вы используете zf2, вам также может понравиться http://github.com/superdweebie/DoctrineExtensionsModule. , который настраивает DoctrineExtensions для использования в zf2.

Чтобы использовать модуль, установите его с помощью composer, как и любой другой модуль. Затем добавьте следующее в конфигурацию zf2:

'sds' => [
    'doctrineExtensions' => [
        'extensionConfigs' => [
            'Sds\DoctrineExtensions\Serializer' => null,
        ),
    ),
),

Чтобы получить сериализатор, используйте:

$serializer = $serivceLocator->get('Sds\DoctrineExtensions\Serializer');

Чтобы использовать сериализатор:

$array = $serializer->toArray($document)
$json = $serializer->toJson($document)

$document = $serializer->fromArray($array)
$document = $serializer->fromJson($json)

Есть также несколько дополнительных аннотаций для управления сериализацией, если вы хотите их использовать:

@Sds\Setter - specify a non standard setter for a property
@Sds\Getter - specify a non standard getter fora  property
@Sds\Serializer(@Sds\Ignore) - ignore a property when serializing

Это все еще в стадии разработки, поэтому любые комментарии/улучшения будут высоко оценены. Если у вас возникнут проблемы с этими библиотеками, просто зарегистрируйте их на github, и они будут оперативно решены.

Наконец, примечание о сериализации встроенных документов и документов, на которые ссылаются, — встроенные документы должны быть сериализованы вместе с их родителем, а документы, на которые есть ссылки, — нет. Это отражает способ сохранения данных в БД. Это также означает, что циклические ссылки не являются проблемой.

Обновлять

Я отправил обновления в Sds/DoctrineExtensions/Serializer, чтобы теперь он мог правильно обрабатывать ссылки. Обновлены следующие три (пять) методов:

toArray/toJson
fromArray/fromJson
applySerializeMetadataToArray

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

По умолчанию ссылки будут сериализованы в такой массив:

[$ref: 'CollectionName/DocumentId']

Стиль ссылок $ref — это то, что Mongo использует внутри, поэтому он кажется подходящим. Формат ссылки дан с расчетом на то, что ее можно будет использовать в качестве URL-адреса REST API.

Поведение по умолчанию можно переопределить, определив альтернативу ReferenceSerializer следующим образом:

/**
 * @ODM\ReferenceMany(targetDocument="MyTargetDocument")
 * @Sds\Serializer(@Sds\ReferenceSerializer('MyAlternativeSerializer'))
 */
protected $myDocumentProperty;

Один альтернативный ReferenceSerializer уже включен в библиотеку. Это энергичный сериализатор — он будет сериализовать ссылки, как если бы они были встроенными документами. Его можно использовать следующим образом:

/**
 * @ODM\ReferenceMany(targetDocument="MyTargetDocument")
 * @Sds\Serializer(@Sds\ReferenceSerializer('Sds\DoctrineExtensions\Serializer\Reference\Eager'))
 */
protected $myDocumentProperty;

Или предоставляется альтернативная сокращенная аннотация:

/**
 * @ODM\ReferenceMany(targetDocument="MyTargetDocument")
 * @Sds\Serializer(@Sds\Eager))
 */
protected $myDocumentProperty;

Альтернативный вариант ReferenceSerializers должен реализовать Sds\DoctrineExtensions\Serializer\Reference\ReferenceSerializerInterface

Кроме того, я убрал аннотацию игнорирования, поэтому к свойствам можно добавить следующие аннотации, чтобы обеспечить более точное управление сериализацией:

@Sds\Serializer(@Sds\Ignore('ignore_when_serializing'))
@Sds\Serializer(@Sds\Ignore('ignore_when_unserializing'))
@Sds\Serializer(@Sds\Ignore('ignore_always'))
@Sds\Serializer(@Sds\Ignore('ignore_never'))

Например, поместите @Sds\Serializer(@Sds\Ignore('ignore_when_serializing')) в свойство электронной почты — это означает, что электронная почта может быть отправлена ​​​​на сервер для обновления, но никогда не может быть сериализована для клиента в целях безопасности.

И, наконец, если вы еще не заметили, аннотации sds поддерживают наследование и переопределение, поэтому они хорошо работают со сложными структурами документов.

person superdweebie    schedule 06.12.2012
comment
Привет @superdweebie, спасибо за помощь. Я попробовал ваш сериализатор, но столкнулся с проблемой, что ссылочные документы по-прежнему сериализуются и извлекаются как объекты, потому что здесь похоже, нет проверки $mapping['reference'], чтобы не добавлять их. Также как насчет геттеров, которые не возвращают строки, такие как объекты даты? - person jhuet; 07.12.2012
comment
Кстати, как бы вы предоставили какой-то API для доступа к данным вашего приложения со ссылками на документы, если вы не извлекаете их в самом сериализаторе? - person jhuet; 07.12.2012
comment
Привет @jhuet, спасибо, что нашли время, чтобы попробовать. Я добавлю тестовый пример для справочных документов сериализатора (думаю, у меня его не было). Что касается API, я думал использовать что-то вроде этого: dojotoolkit.org /reference-guide/1.8/dojox/json/ref.html. На самом деле мне не нужно было это делать (пока), поэтому код не на все 100%. Но это хорошее изменение, чтобы еще больше укрепить библиотеку. - person superdweebie; 09.12.2012
comment
Нет проблем :) dojox.json.ref выглядит как интересный подход, знаете ли вы, насколько широко он уже принят? До сих пор в любом API, с которым я сталкивался, упомянутые документы просто выводились как обычный документ. И, кстати, это, похоже, не решает, как вы будете получать эти упомянутые документы в первую очередь. Как вы узнаете на стороне сервера, должен ли документ служить ссылкой или обычным документом, если он отправляется в первый раз? Я сомневаюсь, что клиент должен отправлять все документы, о которых он знает, по каждому запросу? - person jhuet; 09.12.2012
comment
@jhuet Я только что внес несколько изменений, как указано выше. Они могут помочь. - person superdweebie; 10.12.2012
comment
Вау, это то, что вы называете несколько изменениями! Отличная работа там, спасибо! Там есть почти все, что мне нужно ;) Сейчас я чувствую себя более комфортно, используя способ Eager для сериализации ссылок, и, конечно же, я столкнулся с проблемой циклических ссылок. Как вы думаете, можно ли добавить какой-нибудь детектор максимальной глубины рекурсии? - person jhuet; 10.12.2012

Еще один очень простой, независимый от фреймворка способ преобразования документа ODM Doctrine в массив или JSON — http://ajaxray.com/blog/converting-doctrine-mongodb-document-tojson-or-toarray

Это решение дает вам черту, которая предоставляет функции toArray() и toJSON() для ваших документов ODM. После useинга трейта в вашем документе вы можете сделать -

<?php
// Assuming in a Symfony2 Controller
// If you're not, then make your DocmentManager as you want
$dm = $this->get('doctrine_mongodb')->getManager();
$report = $dm->getRepository('YourCoreBundle:Report')->find($id);

// Will return simple PHP array
$docArray = $report->toArray();

// Will return JSON string
$docJSON = $report->toJSON();

Кстати, это будет работать только на PHP 5.4 и выше.

person Anis    schedule 13.05.2014
comment
@laalto, я добавил пример кода использования этого решения. Спасибо. - person Anis; 13.05.2014