PHP PDO Fetch MySQL DateTime



我有一个PHP类表示MySQL表。其中一个列表类型是DateTime。以前我使用string,一切都很好,因为我不需要处理日期类型。我只是使用fetchAll函数和列表自动映射到适当的字段。

$stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_CLASS, MyPHPClass::class);

现在我想在我的PHP脚本中使用DateTime类型。是否可以自动当使用PDOfetchAll时,将MySQL DateTime转换为PHP DateTime?如果是,怎么做?

注意:我知道如何将DateTime字符串从MySQL转换为PHP DateTime,我只是想知道这是否有可能添加@Annotation或转换器。

出于这个目的,所谓的水合剂的概念是非常常见的。特别是对于DateTime类型的数据字段(很可能在其他模型中重复),使用水合物是有意义的。这使逻辑远离模型,并与可重用代码一起工作。

为什么加湿器?

如果您正在考虑使用另一个数据库系统来进行整个开发,或者如果您只是想保持数据模型的最大可能的灵活性,那么hydrators是非常有意义的。如前所述,水合器可以确保模型不受任何逻辑的影响。此外,水合器可用于表示灵活的场景。此外,仅仅基于PHP PDO类提供的可能性来合并数据是非常弱的。只需将数据库中的原始数据作为数组处理,然后让水合器发挥神奇的作用。

水合器背后的逻辑

每个水合剂可以对被水合物的性质采用不同的策略。这些水合剂策略可用于在实际水合作用之前改变模型中的值或执行其他功能。

<?php
declare(strict_types=1);
namespace MarcelHydrator;
interface HydratorInterface
{
public function hydrate(array $data, object $model): object;
public function extract(object $model): array;
}

上面显示的接口应该在每个水合器类中实现。每个水合物都应该有一个水合物方法,它将给定的数据数组推入给定的模型。此外,还必须有一个转换,即提取方法,它将数据从模型中提取到数组中。

<?php
declare(strict_types=1);
namespace MarcelHydratorStrategy;
interface StrategyInterface 
{
public function hydrate($value);
}

两个接口都定义了水合物和水合物策略必须带来的方法。这些接口主要用于实现对象识别的安全类型提示。

水合器策略

<?php
declare(strict_types=1);
namespace MarcelHydratorStrategy;
use DateTime;
class DateTimeStrategy implements StrategyInterface
{
public function hydrate($value) 
{
$value = new DateTime($value);
return $value;
}
}

这个hydrator策略的简单示例只不过是获取原始值并用它初始化一个新的DateTime对象。为了简单说明,我在这里省略了错误处理。在生产环境中,您应该总是在此时检查DateTime对象是否真的被创建并且没有生成任何错误。

水合器

<?php
declare(strict_types=1);
namespace MarcelHydrator;
use MarcelHydratorStrategyStrategyInterface;
use ReflectionClass;
class ClassMethodsHydrator implements HydratorInterface
{
protected ?ReflectionClass $reflector = null;
protected array $strategies = [];
public function hydrate(array $data, object $model): object
{
if ($this->reflector === null) {
$this->reflector = new ReflectionClass($model);
}
foreach ($data as $key => $value) {
if ($this->hasStrategy($key)) {
$strategy = $this->strategies[$key];
$value = $strategy->hydrate($value);
}
$methodName = 'set' . ucfirst($key);
if ($this->reflector->hasMethod($methodName)) {
$model->{$methodName}($value);
}
}
return $model;
}
public function extract(object $model): array
{
return get_object_vars($model);
}
public function addStrategy(string $name, StrategyInterface $strategy): void
{
$this->strategies[$name] = $strategy; 
}

public function hasStrategy(string $name): bool
{
return array_key_exists($name, $this->strategies);
}
}

这个hydrator要求你的模型有getter和setter方法。在这个例子中,它至少要求每个属性都有一个对应的setter方法。为了避免错误并正确命名方法,应该从数据库中过滤列名的名称。通常,数据库中的名称用下划线标注,模型的属性遵循驼峰大小写约定。(例子:"fancy_string"=比;"setFancyString"

的例子
class User 
{
protected int $id;
protected DateTime $birthday;
public function getId(): int
{
return $this->id;
}
public function setId(int $id): void
{
$this->id = $id;
}
public function getBirtday(): DateTime
{
return $this->birthday;
}
public function setBirthday(DateTime $birthday): void
{
$this->birthday = $birthday;
}
}
$data = [
'id' => 1,
'birthday' => '1979-12-19',
];
$hydrator = new ClassMethodsHydrator();
$hydrator->addStrategy('birthday', new DateTimeStrategy());
$user = $hydrator->hydrate($data, new User());   

这段代码的结果将是一个精细的用户模型。

object(MarcelModelUser)#3 (2) {
["id":protected] => int(1)
["birthday":protected] => object(DateTime)#5 (3) {
["date"] => string(26) "1979-12-19 00:00:00.000000"
["timezone_type"] => int(3)
["timezone"] => string(13) "Europe/Berlin"
}

我们可以利用PHP将为所有未定义的属性调用__set魔术方法的事实,因此我们可以在那里初始化DateTime对象。

class User {
public string $name;
public DateTime $dateObject;

public function __set($property, $value) {
if ($property === 'date') {
$this->dateObject = new DateTime($value);
} else {
$this->$property = $value;
}
}
}
$stmt->fetchAll(PDO::FETCH_CLASS, User::class);

注意:数据库中的列名必须与User对象中的属性名不同,否则__set方法将不会被调用。

MySQL将datetime保存为unix时间戳,并将所有日期作为时间戳返回,如果我们将其作为字符串,那么我们将转换为时间戳

例子:日期("m/d/Y H:我:s ', 1541843467);

最新更新