Join us Sept 17 at .local NYC! Use code WEB50 to save 50% on tickets. Learn more >
MongoDB Event
Docs 菜单
Docs 主页
/ / /
PHP 库手册
/

自定义数据类型

注意

本教程介绍如何使用MongoDB扩展中的 MongoDB\ BSON \Persistable 接口实施自定义数据类型。请考虑改用编解码器,将MongoDB持久性逻辑与业务逻辑分离开来。有关示例,请参阅 编解码器教程。

MongoDB PHP扩展和库在序列化和反序列化时支持自定义类。这可能有用的一个示例是,如果您想要存储日期/时间信息,并保留 PHP 的 DateTimeImmutable 类随时间点存储的时区域信息。

该扩展在与服务器通信时将PHP变量(包括对象)序列化为BSON ,并在从服务器接收数据时将BSON反序列化回PHP变量。

可以通过实现MongoDB\ BSON\Persistable 接口来影响行为。如果一个类实现了此接口,则在序列化时将调用 bsonSerialize 方法。此方法负责返回一个大量或 stdClass对象,以转换为BSON并存储在数据库中。稍后,该数据将用于在从数据库读取时重建对象。

我们以LocalDateTime 类示例。此类包装MongoDB\ BSON\UTCDateTime数据类型和时区域。

<?php
/* 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对象表示,以及一个包含 Olson 时区域标识符的字符串:

<?php
public function bsonSerialize()
{
return [
'utc' => $this->utc,
'tz' => $this->tz->getName(),
];
}
?>

该扩展还会向文档添加一个__pclass字段,并将其存储在数据库中。 该字段包含PHP类名,以便在反序列化时扩展知道使用哪个类来重新创建存储的对象。

从数据库读取文档时,扩展会检测是否存在__pclass 字段,然后执行MongoDB\ BSON\Persistable::bsonUnserialize 方法,该方法负责恢复对象的原始状态。

在下面的代码中,我们确保utctz字段中的数据是正确的时间,然后将它们的值分配给这两个私有属性。

<?php
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 通常在其返回值中使用的 UTC 时区域。

<?php
public function toDateTime()
{
return $this->utc->toDateTime()->setTimezone($this->tz);
}
public function __toString()
{
return (string) $this->utc;
}
}
?>

定义类后,现在就可以在文档中使用它了。 下面的代码段演示了从LocalDateTime对象到 BSON,再返回到LocalDateTime的往返。

<?php
$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 的聚合框架配合使用,该框架允许根据特定时区进行日期操作、格式化和查询。

后退

Data Formats