自定义数据类型
注意
本教程介绍如何使用 MongoDB\ BSON\Persistable 实施自定义数据类型 MongoDB扩展中的接口。请考虑改用编解码器,将MongoDB持久性逻辑与业务逻辑分离开来。 有关示例,请参阅编解码器教程。
MongoDB PHP 扩展和库在序列化和反序列化时支持自定义类。 这可能有用的一个示例是,如果您想要存储日期/时间信息,并保留 PHP 的 DateTimeImmutable class 存储有一个时间点。
该扩展在与服务器通信时将PHP变量(包括对象)序列化为BSON ,并在从服务器接收数据时将BSON反序列化回PHP变量。
可以通过实施 MongoDB\BSON\Persistable 接口。如果一个类实现了此接口,则在序列化时, bsonSerialize 方法被调用。此方法负责返回一个数组或 stdClass 对象,以转换为 BSON 并存储在数据库中。 该数据稍后将用于在从数据库读取时重建对象。
我们以 LocalDateTime
类为例。 该类封装了 MongoDB\BSON\UTCDateTime 数据类型和时区。
/* 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; }
由于它实现了 MongoDB\BSON\Persistable 接口,该类需要实现 bsonSerialize 和 bsonUnserialize 方法。在 bsonSerialize 方法,我们返回一个数组,其中包含需要保留的两个值: 自纪元以来的时间点(以毫秒为单位),用MongoDB \BSON \UTCDateTime string表示 对象,以及一个包含 Olson 时区标识符的 :
public function bsonSerialize() { return [ 'utc' => $this->utc, 'tz' => $this->tz->getName(), ]; }
该扩展还会向文档添加一个__pclass
字段,并将其存储在数据库中。 该字段包含PHP类名,以便在反序列化时扩展知道使用哪个类来重新创建存储的对象。
从数据库读取文档时,扩展会检测是否存在__pclass
字段,然后执行 MongoDB\ BSON\Persistable::bsonUnserialize 方法,负责恢复对象的原始状态。
在下面的代码中,我们确保utc
和tz
字段中的数据是正确的时间,然后将它们的值分配给这两个私有属性。
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']); }
您可能已经注意到,该类还实现了 MongoDB\BSON\UTCDateTimeInterface 接口。此接口定义了 MongoDB\BSON\UTCDateTime 类。
建议现有 BSON 类的包装器实现各自的接口(即 MongoDB\BSON\UTCDateTimeInterface ),这样包装器对象就可以在与其原始未包装版本相同的上下文中使用。还建议您始终对接口进行类型提示(即 MongoDB\BSON\UTCDateTimeInterface ),并且从不针对具体类(即MongoDB\BSON\UTCDateTime ),因为这会阻止包装对象被方法接受。
在新的toDateTime
方法中,我们返回一个 DateTime 对象设置了本地时区,而不是 MongoDB\BSON\UTCDateTime 通常在其返回值中使用。
public function toDateTime() { return $this->utc->toDateTime()->setTimezone($this->tz); } public function __toString() { return (string) $this->utc; } }
定义类后,现在就可以在文档中使用它了。 下面的代码段演示了从LocalDateTime
对象到 BSON,再返回到LocalDateTime
的往返。
$bson = MongoDB\BSON\Document::fromPHP(['date' => new LocalDateTime]); $document = $bson->toPHP(); var_dump($document); var_dump($document->date->toDateTime());
其输出:
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" }
将 Olson 时区标识符存储在单独的字段中也可以很好地与 MongoDB 的聚合框架配合使用,该框架允许根据特定时区进行日期操作、格式化和查询。