使用PHP PDO获取MySQL DateTime

4

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

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

现在我想在我的PHP脚本中使用DateTime类型。在使用PDO fetchAll时,是否有可能将MySQL DateTime自动转换为PHP DateTime?如果可以,该如何实现?

注意: 我知道如何将来自MySQL的DateTime字符串转换为PHP DateTime,我只是想知道是否有可能添加类似于@Annotation或转换器这样的东西。

没有内置的现成方法可以实现这一点。您需要使用像Doctrine这样的DBAL来为您完成此操作。 - Daniel W.
冷静点,你不必为这么简单的任务使用Doctrine。考虑使用hydrators和hydrator strategies。这是一种简单的模型填充方法。 - Marcel
3个回答

2
我们可以利用 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方法。


2
为此,所谓的hydrate概念是非常普遍的,特别是对于类型为DateTime的数据字段,在其他模型中可能会重复出现,使用hydrate是有意义的。这将使逻辑与模型分离,并与可重用代码一起工作。
为什么需要Hydrators?
如果您考虑使用另一个数据库系统进行整个开发,或者如果您只想保持数据模型的最大灵活性,hydrate就是绝佳选择。如前所述,hydrate可以确保模型不带任何逻辑。此外,hydrate可用于表示灵活的场景。此外,仅基于PHP PDO类提供的可能性来hydration数据是很弱的。只需处理数组形式的数据库中的原始数据,然后让hydrate完成魔法即可。
Hydrators背后的逻辑
每个hydrate都可以对要hydrate的对象的属性应用不同的策略。这些hydrate策略可用于在实际hydration之前更改值或执行其他函数。
<?php
declare(strict_types=1);
namespace Marcel\Hydrator;

interface HydratorInterface
{
    public function hydrate(array $data, object $model): object;

    public function extract(object $model): array;
}

每个水合器类都应该实现上面显示的接口。每个水合器都应该有一个hydrate方法,将给定的数据数组推送到给定的模型中。此外还必须有一个反向操作,即extract方法,它将从模型中提取数据并转换为数组。

<?php
declare(strict_types=1);
namespace Marcel\Hydrator\Strategy;

interface StrategyInterface 
{
    public function hydrate($value);
}

这两个接口定义了水合器和水合器策略必须具备的方法。这些接口主要用于安全类型提示,以便识别对象。

水合器策略

<?php
declare(strict_types=1);
namespace Marcel\Hydrator\Strategy;

use DateTime;

class DateTimeStrategy implements StrategyInterface
{
    public function hydrate($value) 
    {
        $value = new DateTime($value);
        return $value;
    }
}

这个简单的数据注入策略只是将原始值取出并用它初始化一个新的DateTime对象。为了简单起见,我在此省略了错误处理。在生产环境中,您应该始终检查此时是否真正创建了DateTime对象并且没有生成任何错误。

数据注入器

<?php
declare(strict_types=1);
namespace Marcel\Hydrator;

use Marcel\Hydrator\Strategy\StrategyInterface;
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);
    }
}

这个水合器要求你的模型有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(Marcel\Model\User)#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"
}

-3

MySQL将日期时间保存为Unix时间戳,并且如果我们以字符串形式获取所有日期,则返回时间戳,然后我们将其转换为时间戳

例如:- date('m/d/Y H:i:s', 1541843467);


4
MySQL不会返回datetime数据类型的时间戳,而是以字符串形式返回日期/时间,格式为Y-m-d H:i:s。因此,您无需自己进行转换。OP还询问如何自动将MySQL响应从datetime(日期字符串Y-m-d H:i:s)转换为DateTime实例(PHP类),而不是如何将时间戳转换为日期字符串。 - M. Eriksson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接