Nota
Este tutorial explica la implementación de tipos de datos personalizados utilizando el MongoDB\BSON\Persistable Interfaz que se encuentra en la extensión MongoDB. Considere usar un códec para desacoplar la lógica de persistencia de MongoDB de su lógica de negocio. Consulte Tutorial de códec para un ejemplo.
La extensión y biblioteca PHP de MongoDB admiten clases personalizadas durante la serialización y deserialización. Un ejemplo de su utilidad es almacenar información de fecha y hora conservando la información de zona horaria que la clase DateTimeImmutable de PHP almacena con un punto en el tiempo.
La extensión serializa variables PHP, incluidos objetos, en BSON cuando se comunica con el servidor y deserializa BSON nuevamente en variables PHP cuando recibe datos del servidor.
Es posible influir en el comportamiento implementando la interfaz MongoDB\BSON\Persistable. Si una clase implementa esta interfaz, al serializar, se llama al método bsonSerialize. Este método devuelve una matriz o un objeto stdClass para convertirlo a BSON y almacenarlo en la base de datos. Estos datos se utilizarán posteriormente para reconstruir el objeto al leerlo de la base de datos.
Como ejemplo presentamos el LocalDateTime clase. Esta clase envuelve el tipo de datos MongoDB\BSON\UTCDateTime y una zona horaria.
/* Custom document class that stores a UTCDateTime and time zone and also * implements the UTCDateTime interface for portability. */ class LocalDateTime implements \MongoDB\BSON\Persistable, \MongoDB\BSON\UTCDateTimeInterface { private $utc; private $tz; public function __construct($milliseconds = null, \DateTimeZone $timezone = null) { $this->utc = new \MongoDB\BSON\UTCDateTime($milliseconds); if ($timezone === null) { $timezone = new \DateTimeZone(date_default_timezone_get()); } $this->tz = $timezone; }
Dado que implementa la interfaz MongoDB\BSON\Persistable, la clase debe implementar los métodos bsonSerialize y bsonUnserialize. En el método bsonSerialize, devolvemos una matriz con los dos valores que necesitamos conservar: el punto temporal en milisegundos desde la época, representado por un objeto MongoDB\BSON\UTCDateTime, y una cadena que contiene el identificador de la zona horaria de Olson:
public function bsonSerialize() { return [ 'utc' => $this->utc, 'tz' => $this->tz->getName(), ]; }
La extensión añadirá además un campo __pclass al documento y lo almacenará en la base de datos. Este campo contiene el nombre de la clase PHP para que, al deserializar, la extensión sepa qué clase usar para recrear el objeto almacenado.
Cuando se lee el documento desde la base de datos, la extensión detecta si __pclass hay un campo y luego ejecuta el método MongoDB\BSON\Persistable::bsonUnserialize, que es responsable de restaurar el estado original del objeto.
En el código siguiente, nos aseguramos de que los datos de los campos utc y tz sean del momento correcto y luego asignamos sus valores a las dos propiedades privadas.
public function bsonUnserialize(array $data) { if ( ! isset($data['utc']) || ! $data['utc'] instanceof \MongoDB\BSON\UTCDateTime) { throw new Exception('Expected "utc" field to be a UTCDateTime'); } if ( ! isset($data['tz']) || ! is_string($data['tz'])) { throw new Exception('Expected "tz" field to be a string'); } $this->utc = $data['utc']; $this->tz = new \DateTimeZone($data['tz']); }
Quizás hayas notado que la clase también implementa la interfaz MongoDB\BSON\UTCDateTimeInterface. Esta interfaz define los dos métodos no constructores de la clase MongoDB\BSON\UTCDateTime.
Se recomienda que los contenedores de las clases BSON existentes implementen la interfaz correspondiente (es decir, MongoDB\BSON\UTCDateTimeInterface) para que los objetos contenedor puedan usarse en el mismo contexto que sus versiones originales sin envolver. También se recomienda que siempre se utilicen sugerencias de tipo para la interfaz (es decir, MongoDB\BSON\UTCDateTimeInterface) y nunca para la clase concreta (es decir, MongoDB\BSON\UTCDateTime), ya que esto impediría que los objetos envueltos sean aceptados en los métodos.
En nuestro nuevo toDateTime método devolvemos un objeto DateTime con la zona horaria local establecida, en lugar de la zona horaria UTC que MongoDB\BSON\UTCDateTime normalmente usa en su valor de retorno.
public function toDateTime() { return $this->utc->toDateTime()->setTimezone($this->tz); } public function __toString() { return (string) $this->utc; } }
Con la clase definida, ya podemos usarla en nuestros documentos. El siguiente fragmento muestra el recorrido de ida y vuelta del objeto LocalDateTime a BSON y de vuelta a LocalDateTime.
$bson = MongoDB\BSON\Document::fromPHP(['date' => new LocalDateTime]); $document = $bson->toPHP(); var_dump($document); var_dump($document->date->toDateTime());
¿Qué resultados:
object(stdClass)#1 (1) { ["date"]=> object(LocalDateTime)#2 (2) { ["utc":"LocalDateTime":private]=> object(MongoDB\BSON\UTCDateTime)#3 (1) { ["milliseconds"]=> string(13) "1533042443716" } ["tz":"LocalDateTime":private]=> object(DateTimeZone)#4 (2) { ["timezone_type"]=> int(3) ["timezone"]=> string(13) "Europe/London" } } } object(DateTime)#5 (3) { ["date"]=> string(26) "2018-07-31 14:07:23.716000" ["timezone_type"]=> int(3) ["timezone"]=> string(13) "Europe/London" }
Almacenar el identificador de zona horaria de Olson en un campo separado también funciona bien con el marco de agregación de MongoDB, que permite la manipulación, el formato y la consulta de fechas según una zona horaria específica.