【问题标题】:PHP PDO Fetch MySQL DateTimePHP PDO 获取 MySQL 日期时间
【发布时间】:2021-12-06 22:13:36
【问题描述】:

我有一个代表 MySQL 表的 PHP 类。该列表类型之一是DateTime。以前我使用字符串并且一切正常,因为我不必处理日期类型。我只是使用fetchAll 函数和列表自动映射到一个适当的字段。

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

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

注意: 我知道如何将 DateTime 字符串从 MySQL 转换为 PHP DateTime,我只是想知道是否可以添加类似 @Annotation 或转换器的内容。

【问题讨论】:

  • 没有内置的现成方法来实现这一点。你必须使用像 Doctrine 这样的 DBAL 来为你做这件事。
  • 冷静下来。您不必为如此简单的任务使用 Doctrine。想想保湿剂和保湿剂策略。水合模型的简单方法。

标签: php datetime pdo


【解决方案1】:

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

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

【讨论】:

  • MySQL 不返回数据类型 datetime 的时间戳。它以Y-m-d H:i:s 格式返回一个带有日期/时间的字符串,因此无需自行转换。 OP 还询问是否将 MySQL 响应从 datetime(日期字符串 Y-m-d H:i:s)自动转换为 DateTime-instance(PHP 类),而不是如何将时间戳转换为日期字符串。
【解决方案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方法将不会被调用。

【讨论】:

    【解决方案3】:

    为此,所谓的水合器的概念非常普遍。特别是对于 DateTime 类型的数据字段,很可能会在其他模型中重复,使用 hydrator 是有意义的。这使逻辑远离模型并使用可重用的代码。

    为什么选择保湿剂?

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

    水合器背后的逻辑

    每个水化器可以对要水化的对象的属性应用不同的策略。这些水化器策略可用于在实际水化之前更改模型中的值或执行其他功能。

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

    上面显示的接口应该在每个 hydrator 类中实现。每个 hydrator 都应该有一个 hydrate 方法,它将给定的数据数组推送到给定的模型中。此外,必须有一个转折点,即 extract 方法,它将模型中的数据提取到一个数组中。

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

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

    补水策略

    <?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;
        }
    }
    

    这个简单的 hydrator 策略示例只不过是获取原始值并用它初始化一个新的 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);
        }
    }
    

    此 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(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"
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-07
      • 1970-01-01
      • 1970-01-01
      • 2015-06-21
      • 2011-06-12
      • 2013-06-16
      相关资源
      最近更新 更多