Десериализация из BSON

Внимание

Документы BSON технически могут содержать повторяющиеся ключи, поскольку документы хранятся в виде списка пар ключ-значение; однако приложениям следует воздерживаться от создания документов с дубликатами ключей, поскольку поведение сервера и драйвера может быть неопределённым. Поскольку объекты и массивы PHP не могут иметь повторяющихся ключей, данные также могут быть потеряны при декодировании документа BSON с повторяющимися ключами.

Устаревший модуль mongo десериализовывал BSON-документы и BSON-массивы в PHP-массивы. Хотя с PHP-массивами удобно работать, такое поведение было проблематичным, поскольку разные BSON-типы могли десериализоваться до одного и того же PHP-значения (например, {"0": "foo"} и ["foo"]), что делало невозможным определение оригинального BSON-типа. По умолчанию модуль mongodb решает эту проблему и гарантирует, что BSON-массивы преобразуются в PHP-массивы, а BSON-документы в PHP-объекты.

Для составных типов существует три типа данных:

root

относится только к BSON-документу верхнего уровня

document

относится только к встроенным BSON-документам

array

относится к BSON-массивам

Помимо трёх групповых типов, также можно настроить определённые поля в документе для сопоставления с типами данных, как указывает следующий фрагмент кода. В качестве примера, следующая карта типов позволяет сопоставить каждый встроенный документ в массиве «addresses» с классом Address, а каждое поле «city» в этих документах с встроенным адресом с классом City:

[
    'fieldPaths' => [
        'addresses.$' => 'MyProject\Address',
        'addresses.$.city' => 'MyProject\City',
    ],
]

Каждый из этих трёх типов данных, а также сопоставления конкретных полей можно сопоставить с различными типами PHP. Возможные значения сопоставления:

не указано или NULL (по умолчанию)

  • BSON-массив будет десериализован как PHP-массив (array).

  • BSON-документ (корневой или встроенный) без свойства __pclass [1] становится PHP-объектом stdClass, причём каждый ключ BSON-документа устанавливается как открытое свойство объекта stdClass.

  • BSON-документ (корневой или встроенный) со свойством __pclass [1] становится PHP-объектом имени класса, как это определяет свойство __pclass.

    Если именованный класс реализует интерфейс MMongoDB\BSON\Persistable, то свойства BSON-документа, включая свойство __pclass, отправляются как ассоциативный массив в метод MongoDB\BSON\Unserializable::bsonUnserialize(), чтобы инициализировать свойства объекта.

    Если именованный класс не существует или не реализует интерфейс MongoDB\BSON\Persistable, будет использоваться объект stdClass, и каждый ключ BSON-документа (включая свойство __pclass) будет установлен как открытое свойство объекта stdClass.

    Функциональность свойства __pclass зависит от того, представляет ли собой свойство часть извлечённого документа БД MongoDB. Если при запросе документов используется проекция, необходимо включить в проекцию поле __pclass, чтобы эта функциональность работала.

«array»

Превращает BSON-массив или BSON-документ в PHP-массив. Специальной обработки свойства __pclass [1] не будет, но его можно установить как элемент в возвращаемом массиве, если он содержался в BSON-документе.

«object» или «stdClass»

Превращает BSON-массив или BSON-документ в объект stdClass. Специальной обработки свойства __pclass [1] не будет, но его можно установить как открытое свойство в возвращаемом объекте, если оно присутствовало в BSON-документе.

«bson»

Превращает BSON-массив в объект MongoDB\BSON\PackedArray, а BSON-документ в объект MongoDB\BSON\Document, независимо от того, есть ли у BSON-документа свойство __pclass [1].

Замечание: Значение bson доступно только для трёх корневых типов, но не в отображениях для конкретных полей.

любая другая строка

Определяет имя класса, который должен десериализовать BSON-массив или BSON-объект. Для BSON-объектов, которые содержат свойства __pclass, этот класс будет приоритетным.

Если именованный класс не существует, не представляет собой конкретный класс (то есть это абстрактный класс или интерфейс) или не реализует интерфейс MongoDB\BSON\Unserializable, выбрасывается исключение MongoDB\Driver\Exception\InvalidArgumentException.

Если BSON-объект содержит свойство __pclass и этот класс существует и реализует интерфейс MongoDB\BSON\Persistable, он заменит класс, представленный в карте типов.

Свойства BSON-документа, включая свойство __pclass, если оно существует, отправляются как ассоциативный массив в метод MongoDB\BSON\Unserializable::bsonUnserialize(), чтобы инициализировать свойства объекта.

TypeMaps

Функции настройки TypeMaps можно установить методом MongoDB\Driver\Cursor::setTypeMap() для объекта MongoDB\Driver\Cursor или аргумента $typeMap в методах MongoDB\BSON\toPHP(), MongoDB\BSON\Document::toPHP() и MongoDB\BSON\PackedArray::toPHP(). Каждый из трёх классов (root, document, и array) можно задать индивидуально, в дополнение к типам полей.

Если значение на карте равно NULL, это означает то же, что и значение по умолчанию для этого элемента.

Примеры

В этих примерах используются следующие классы:

MyClass

не реализует интерфейсы

YourClass

реализует интерфейс MongoDB\BSON\Unserializable

OurClass

реализует интерфейс MongoDB\BSON\Persistable

TheirClass

расширяет класс OurClass

Метод MongoDB\BSON\Unserializable::bsonUnserialize() классов YourClass, OurClass, OurClass выполняет итерацию по массиву и устанавливает свойства без изменений. Метод также устанавливает для свойства $unserialized значение true:

<?php

function bsonUnserialize(array $map)
{
foreach (
$map as $k => $value) {
$this->$k = $value;
}
$this->unserialized = true;
}

?>

/* typemap: [] (все значения по умолчанию) */
{"foo": "yes", "bar" : false}
  -> stdClass {$foo => 'yes', $bar => false}

{"foo": "no", "array" : [5, 6]}
  -> stdClass {$foo => 'no', $array => [5, 6]}

{"foo": "no", "obj" : {"embedded" : 3.14}}
  -> stdClass {$foo => 'no', $obj => stdClass {$embedded => 3.14}}

{"foo": "yes", "__pclass": "MyClass"}
  -> stdClass {$foo => 'yes', $__pclass => 'MyClass'}

{"foo": "yes", "__pclass": {"$type" : "80", "$binary" : "MyClass"}}
  -> stdClass {$foo => 'yes', $__pclass => Binary(0x80, 'MyClass')}

{"foo": "yes", "__pclass": {"$type" : "80", "$binary" : "YourClass")}
  -> stdClass {$foo => 'yes', $__pclass => Binary(0x80, 'YourClass')}

{"foo": "yes", "__pclass": {"$type" : "80", "$binary" : "OurClass")}
  -> OurClass {$foo => 'yes', $__pclass => Binary(0x80, 'OurClass'), $unserialized => true}

{"foo": "yes", "__pclass": {"$type" : "44", "$binary" : "YourClass")}
  -> stdClass {$foo => 'yes', $__pclass => Binary(0x44, 'YourClass')}

/* typemap: ["root" => "MissingClass"] */
{"foo": "yes"}
  -> MongoDB\Driver\Exception\InvalidArgumentException("MissingClass does not exist")

/* typemap: ["root" => "MyClass"] */
{"foo": "yes", "__pclass" : {"$type": "80", "$binary": "MyClass"}}
  -> MongoDB\Driver\Exception\InvalidArgumentException("MyClass does not implement Unserializable interface")

/* typemap: ["root" => "MongoDB\BSON\Unserializable"] */
{"foo": "yes"}
  -> MongoDB\Driver\Exception\InvalidArgumentException("Unserializable is not a concrete class")

/* typemap: ["root" => "YourClass"] */
{"foo": "yes", "__pclass" : {"$type": "80", "$binary": "MongoDB\BSON\Unserializable"}}
  -> YourClass {$foo => "yes", $__pclass => Binary(0x80, "MongoDB\BSON\Unserializable"), $unserialized => true}

/* typemap: ["root" => "YourClass"] */
{"foo": "yes", "__pclass" : {"$type": "80", "$binary": "MyClass"}}
  -> YourClass {$foo => "yes", $__pclass => Binary(0x80, "MyClass"), $unserialized => true}

/* typemap: ["root" => "YourClass"] */
{"foo": "yes", "__pclass" : {"$type": "80", "$binary": "OurClass"}}
  -> OurClass {$foo => "yes", $__pclass => Binary(0x80, "OurClass"), $unserialized => true}

/* typemap: ["root" => "YourClass"] */
{"foo": "yes", "__pclass" : {"$type": "80", "$binary": "TheirClass"}}
  -> TheirClass {$foo => "yes", $__pclass => Binary(0x80, "TheirClass"), $unserialized => true}

/* typemap: ["root" => "OurClass"] */
{ foo: "yes", "__pclass" : {"$type": "80", "$binary": "TheirClass"}}
  -> TheirClass {$foo => "yes", $__pclass => Binary(0x80, "TheirClass"), $unserialized => true}

/* typemap: ['root' => 'YourClass'] */
{foo: "yes", "__pclass": {"$type": "80", "$binary": "YourClass"}}
  -> YourClass {$foo => 'yes', $__pclass => Binary(0x80, 'YourClass'), $unserialized => true}

/* typemap: ['root' => 'array', 'document' => 'array'] */
{"foo": "yes", "bar": false}
  -> ["foo" => "yes", "bar" => false]

{"foo": "no", "array": [5, 6]}
  -> ["foo" => "no", "array" => [5, 6]]

{"foo": "no", "obj": {"embedded": 3.14}}
  -> ["foo" => "no", "obj" => ["embedded => 3.14]]

{"foo": "yes", "__pclass": "MyClass"}
  -> ["foo" => "yes", "__pclass" => "MyClass"]

{"foo": "yes", "__pclass": {"$type": "80", "$binary": "MyClass"}}
  -> ["foo" => "yes", "__pclass" => Binary(0x80, "MyClass")]

{"foo": "yes", "__pclass": {"$type": "80", "$binary": "OurClass"}}
  -> ["foo" => "yes", "__pclass" => Binary(0x80, "OurClass")]

/* typemap: ['root' => 'object', 'document' => 'object'] */
{"foo": "yes", "__pclass": {"$type": "80", "$binary": "MyClass"}}
  -> stdClass {$foo => "yes", "__pclass" => Binary(0x80, "MyClass")}

add a note

User Contributed Notes 3 notes

up
2
Miguel
6 years ago
Make sure you include the field "__pclass" to the projection if you want the ODM to automatically call the bsonUnserialize of the class.

If you don't get that field in the query, the ODM will never know which class to call, so you'll have to specify it with the "typemap" variable.
up
1
hello at stefandjokic dot com
4 years ago
Minor correction to my previous comment:

Use 'fieldPaths' for specifying type of nested property.

Eg.

'typeMap' => [
'array' => 'array',
'fieldPaths' => [
'notifications.email.to' => 'array',
'notifications.data.params' => 'array',
],
],
up
0
hello at stefandjokic dot com
4 years ago
You can also specify the type map for nested document item, e.g.

<?php
.
.
.
// everything else is by default an object
'typeMap' => [
'customer.phones' => 'array',
],
To Top