PDO:获取类选项以将字段作为数组发送到构造函数

Posted

技术标签:

【中文标题】PDO:获取类选项以将字段作为数组发送到构造函数【英文标题】:PDO: Fetch class option to send fields to constructor as array 【发布时间】:2012-03-17 05:43:45 【问题描述】:

我想知道是否可以优雅地将 PDO 查询的结果映射到类中的数组成员,而不是让它们作为该对象的公共属性四处浮动。

假设我有(浓缩的)以下内容:

class DBObject 

    protected
        $record = array();

    function __construct(array $record) 
        if(!empty($record)) 
            $this->loadRecord($record);
        
    

理想情况下,我想使用从数据库传递的值数组调用构造函数,而不是使用 __set 或任何其他奇怪的方法。所以使用 PDO 现有的 API 会很棒。

我目前粗略的get_all 函数已经到了这一步:

static function get_all() 
    $class = get_called_class();
    $results = DB::factory()->query('SELECT * FROM ' . $class . ' ORDER BY ID');
    $results->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, $class);
    return $results;

注意:我正在通过 PDO 运行 PHP 5.3 和 MySQL,并且已经知道使用 __set 可以解决此问题,但我明确希望避免使用它来支持更高性能的东西。

【问题讨论】:

您是否知道这段代码与DB class 的名称紧密耦合,并且违反了“得墨忒耳法则”? 是的,它违反了 LoD。不,您的评论不会解决我的问题。此外,将模型与数据库紧密耦合比使用全局更好(IMO 更糟糕。) 如果它是一个解决方案,它会在“答案”部分。不,我的事情全局变量与静态调用没有什么不同。 事实上,当您需要一个基类等到运行时根据开发人员/测试人员参数实例化某些东西时,您怎么能说工厂模式违反了得墨忒耳定律呢?也许您将它与单例模式混淆了? 不,我没有混淆它,因为您使用的是“工厂方法”(实际上至少有一些额外的问题)。这并不是导致它违反 LoD 的原因。您正在通过工厂获取对象以获取方法。那就是问题所在。当然,从未为该对象提供工厂的事实也是违规行为。 【参考方案1】:

您不需要将参数传递给构造函数来使用 PDO::FETCH_CLASS 创建具有私有成员的类。你可以这样做:

<?php
class Songs
  

    private $artist;
    private $title;

    public function __construct()
      
    

    public function get_artist()
    
    return $this->artist;
    

    public function get_title() 
    
    return $this->title;
    

    private function set_artist($artist) 
    
    $this->artist = $artist;
    

    private function set_title($title) 
    
    $this->title = $title;
    

我实际上是在我建立的演示网站上这样做的。它适用于 PDO::FETCH_CLASS。默认情况下,FETCH_CLASS 通过在构造函数之前填充字段来创建对象。将其视为绕过构造函数。它会对私有成员执行此操作。

如果您希望将参数传递给构造函数,您可以像这样进行查询:

$obj = $statement->fetchALL(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, 'Songs', $params);

在这种情况下,您的构造函数将如下所示:

public function __construct($params)

    $this->artist = $params[0]['artist'];
    $this->title= $params[0]['title'];

【讨论】:

【参考方案2】:

删除了之前的代码


对,你不能做这样的事情:
class DBObject 

    protected $record = array();

    function __construct($record = null) 
        if(null === $record)
            $obj_vars = get_object_vars($this);
            $cls_vars = get_class_vars(get_class($this));
            $this->$record = array_diff_key($obj_vars, $cls_vars);
        else
            $this->record = $record;
        
    

然而,问题在于这些值仍然可以作为公共成员使用。 但它将做的是将“预定义”(类)成员与实际(对象)成员进行比较。

由于 PDO 将在对象中创建新成员,因此您可以使用 array_diff_key 来获取“新”成员。

是的,这仍然不会将它们传递给您的构造函数。

【讨论】:

我看不出这是如何将任何内容加载到我的 $record 数组中的。 不会。你说你试图隐藏由 fetchObject 创建的“公共”属性。 嗯,似乎有点脆弱。变量是否公开与我无关;我想以这样一种方式组织数据,以便在数据库记录数据和对象的状态/其他杂项变量之间有明确的分离。 这并不是很脆弱,除非您在同一个对象上多次调用构造函数(可能)。 PDO 将首先创建成员,然后才会调用构造函数。因此,在构造函数中,“类成员”、“普通对象成员”、“pdo 生成的成员”和“运行时生成的成员”之间存在分离。后者在对象创建期间可能根本不存在(除非在父构造函数中创建)。所以对我来说似乎很安全 好吧,我明白了。我已经针对 __set 对您的解决方案进行了基准测试,并且速度更快。如果您可以更新代码以包含构造函数参数(仅出于安全考虑),那么我会接受您的回答。干杯 :) PS:基准来源:pastebin.com/2MemcrFh【参考方案3】:

使用魔术__set()方法怎么样:

<?php
class MyClass

    protected $record = array();

    function __set($name, $value) 
        $this->record[$name] = $value;
    


$pdo = new PDO("mysql:host=localhost;dbname=db", 'user', 'password');
$results = $pdo->query('SELECT * FROM table');
$results->setFetchMode(PDO::FETCH_CLASS, 'MyClass');

PHP 将为传入其名称和值的每个不存在的属性调用此魔术方法。

【讨论】:

嗨。如果您已完整阅读 OP,您将会看到: 注意:我正在通过 PDO 运行 PHP 5.3 和 MySQL,并且我知道如何使用 __set 解决此问题,但宁愿避免这样做会降低性能。跨度> 对不起,威尔,我显然错过了那张纸条。但是,我不会太担心性能损失,因为它可能很小,尤其是。与 DB 操作添加的任何惩罚相比。我已经运行了一些测试,看起来直接分配给公共或不存在的变量比使用魔术设置器时快大约一个数量级,但我们说的是千分之一与百分之一 MILLISECOND(~0.002ms v . ~0.02ms 在我的系统中)。单独的数据库查询通常在 10-100 毫秒左右,因此慢 4-5 个数量级。 另外,请记住,调用构造函数的成本与调用任何其他方法(包括魔术方法)的成本相同。 公平地说,我不想过早地优化(太多),但是为了将东西加载到经常发生的对象中,我希望事情尽可能轻。 @Janci 你肯定有道理。但是,如果您的查询运行 0.1 秒会返回 1000 行。那将是 1000 个新对象,突然之间 0.002 毫秒和 0.02 毫秒之间存在巨大差异。我想这一切都取决于范围。如果你只返回 10 行,在 10 次查询中,那么它将是可以忽略的。

以上是关于PDO:获取类选项以将字段作为数组发送到构造函数的主要内容,如果未能解决你的问题,请参考以下文章

C#面向对象

PHP构造函数和析构函数

带有向量的类构造函数中的析构函数调用

在 PDO 中的执行函数中将 NULL 值绑定到具有关联数组的命名占位符的问题

具有私有构造函数和析构函数的类对象的向量?

php构造函数的PHP 5 构造函数和析构函数