Observação
Este tutorial explica a implementação de tipos de dados personalizados usando a interface MongoDB\BSON\Persistable encontrada na extensão MongoDB. Considere usar um codec para separar a lógica de persistência do MongoDB de sua lógica de negócios. Consulte o tutorial do codec para obter um exemplo.
A extensão e biblioteca MongoDB PHP oferecem suporte a classes personalizadas durante a serialização e desserialização. Um exemplo de onde isso pode ser útil é se você deseja armazenar informações de data/hora retendo as informações de zona horário que a classe DateTimeImmutable do PHP armazena com um ponto no tempo.
A extensão serializa variáveis PHP, incluindo objetos, em BSON quando se comunica com o servidor e desserializa BSON de volta para variáveis PHP quando recebe dados do servidor.
É possível influenciar o comportamento implementando a interface do MongoDB\BSON\Persistable. Se uma classe implementar essa interface, após a serialização, o método bsonSerialize será chamado. Este método é responsável por retornar uma array ou objeto stdClass para converter em BSON e armazenar no banco de dados. Esses dados serão posteriormente utilizados para reconstruir o objeto após a leitura do banco de dados.
Como exemplo apresentamos a classe LocalDateTime . Esta classe envolve o tipo de dados MongoDB\BSON\UTCDateTime e um zona horário.
/* 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; }
Ao implementar a interface MongoDB\BSON\Persistable, a classe é necessária para implementar os métodos bsonSerialize e bsonUnserialize. No método bsonSerialize, retornamos uma array com os dois valores que precisamos persistir: o ponto no tempo em milissegundos desde a época, representado por um objeto MongoDB\BSON\UTCDateTime e uma string contendo o identificador de zona horário Olson:
public function bsonSerialize() { return [ 'utc' => $this->utc, 'tz' => $this->tz->getName(), ]; }
A extensão também adicionará um campo __pclass ao documento e o armazenará no banco de dados de dados também. Este campo contém o nome da classe PHP para que, após a desserialização, a extensão saiba qual classe usar para recriar o objeto armazenado.
Quando o documento é lido a partir do banco de dados, a extensão detecta se um __pclass campo está presente e, em seguida, executa o método MongoDB\BSON\Persistable::bsonUnserialize, responsável por restaurar o estado original do objeto.
No código abaixo, verificamos se os dados nos campos utc e tz são da hora certa e, em seguida, atribuímos seus valores às duas propriedades 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']); }
Você deve ter notado que a classe também implementa a interface MongoDB\BSON\UTCDateTimeInterface. Essa interface define os dois métodos não construtores da classe MongoDB\BSON\UTCDateTime.
É recomendável que os wrappers em torno das classes BSON existentes implementem sua respectiva interface (ou seja, MongoDB\BSON\UTCDateTimeInterface) para que os objetos wrapper possam ser usados no mesmo contexto que sua versão original não envolvida. Também é recomendável que você sempre digite a dica na interface (ou seja, MongoDB\BSON\UTCDateTimeInterface) e nunca contra a classe concreta (ou seja, MongoDB\BSON\UTCDateTime), pois isso impediria que objetos envoltos fossem aceitos em métodos.
Em nosso novo método toDateTime, retornamos um objeto DateTime com o zona horário local definido, em vez do zona horário UTC que MongoDB\BSON\UTCDateTime normalmente usa em seu valor de retorno.
public function toDateTime() { return $this->utc->toDateTime()->setTimezone($this->tz); } public function __toString() { return (string) $this->utc; } }
Com a classe definida, agora podemos usá-la em nossos documentos. O trecho abaixo demonstra a viagem de ida e volta do objeto LocalDateTime para BSON e de volta para LocalDateTime.
$bson = MongoDB\BSON\Document::fromPHP(['date' => new LocalDateTime]); $document = $bson->toPHP(); var_dump($document); var_dump($document->date->toDateTime());
Quais saídas:
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" }
Armazenar o identificador de fuso horário Olson em um campo separado também funciona bem com a Estrutura de agregação do MongoDB, que permite manipulação de data, formatação e consulta, dependendo de um fuso horário específico.