从单个PDO阵列实例化多个PHP对象(无需样板)

时间:2014-04-21 17:06:47

标签: php arrays object pdo eval

如果您想从数据库中获取单个对象,PDO非常有用:

$obj = $handle->fetchAll(PDO::FETCH_CLASS, $obj_name);

如果您希望在一行中返回多个类类型(例如:在进行JOIN时),PDO的帮助不大:

$handle->fetchAll(PDO::FETCH_ASSOC);

...followed by some data mapping on the resulting array。很好 - 除了我想出来的每一种方法看起来都很糟糕。

我的问题是:如果该数组的内容可能反映所有,没有,某些或多于所需的内容,那么如何以优雅,健壮的方式实例化来自同一数组的多个对象类?

实施例

给定PDO从数据库$vars = ('a' => 1, 'b' => 2, 'c' => 3, 'x' => 7, 'y' => 8, 'z' => 9)返回的数组,构造对象ABC(需要$a$b$c)以及{{1} (XYZ$x$y)没有太多的复制和粘贴。

选项1

$z

(许多样板代码/复制粘贴/要求实例化代码已按正确顺序准备好所有变量)

选项2

class ABC {
    private $a, $b, $c;
    function __construct ($a = null, $b = null, $c = null) {
        $this->$a = $a ? $a : $this->a;
        $this->$b = $b ? $b : $this->b;
        ...

(更好,但如果$ vars中不存在数组键,则会抱怨。当然,我们可以使用isset()但这会使样板问题更严重)

选项3

function ABCFactory($vars = array()) {
    return new ABC($vars['a'], $vars['b']....

(它有效,它很整洁,它很整洁,它很容易重复使用,它会反映我添加的任何新的类变量......但是它已经... eval(),这给了我噩梦!:])

解决方案

dockeryZ 暗示了它,但 mleko 提供了答案。我之前没有听说过变量变量,但它们正是我想要的。我终于去了:

foreach ($vars AS $key => $value) {
    eval("if (isset(\$key) && \$value) \$this->$key = '$value';");
}

然而,未来发现这一点的任何人都应该牢记 Johan Perez 的答案,不要将此片段用作构造函数中的快捷方式...特别是如果输入数组来了来自用户的GET或POST参数!

2 个答案:

答案 0 :(得分:1)

你去吧

<?php
class Name {
    public function __construct($data) {
        foreach($data as $key=>$val){
            $this->{$key}=$val;
        }

    }
}

答案 1 :(得分:1)

就个人而言,我永远不会使用构造函数来设置对象的属性。原因是封装远不止Getters和Setters。像这样做封装:

class Person {
    private $birthDate;
    private $parents;

    public function getBirthDate(){
        return $this->birthDate;
    }

    public function setBirthDate($birthDate){
        $this->birthDate = $birthDate;
    }

    public function getParents(){
        return $this->parents;
    }

    public function setParents($parents){
        $this->parents = $parents;
    }
}

甚至比根本不进行封装更糟糕,因为您已经避免了封装本身的主要目的:数据的完整性。在上面的例子中,生日可能很容易,不是日期,也不是未来的某个日期。而你的父母甚至不可能是一群母亲和父亲(或父亲和父亲或母亲和母亲......)。你应该注意一些事情。

在您的示例中,在构造函数中,您只是检查不是null,但是您没有检查任何其他内容。这可能会使您的对象中的某些数据无效,并且在您打算使用它之前就已经注意到了。在调试时,这可能很容易让您花费数小时才能找到问题。

对我来说,正确的方法是创建一个静态方法,该方法返回使用正确的getter和setter映射的对象的实例。像这样:

class Person {
    private $birthDate;
    private $parents;

    public function getBirthDate(){
        if (isset($this->birthDate) {
            return $this->birthDate;
        else {
            return new DateTime('NOW');
        }
    }

    public function setBirthDate($birthDate){
        if ($birthDate instanceof DateTime){
            $this->birthDate = $birthDate;
        } else {
            throw new InvalidArgumentException("Person::birthDate should be an instance of DateTime");
        }
    }

    public function getParents(){
        if (isset($this->parents) {
            return $this->parents;
        else {
            return new array("father" => null, "mother" => null);
        }
    }

    public function setParents($parents){
        if (is_array($parents) && array_keys($parents) == array("father", "mother")){
           if ($parents["father"] instanceof Person && $parents["mother"] instanceof Person){
               $this->parents = $parents;
           } else {
               throw new InvalidArgumentException("Father and Mother should be instances of Person class.");
           }
        } else {
            throw new Invalid ArgumentException("Person::parents should be an array and in must contain only ['father'] and ['mother'] keys.");
        }
    }

   public static function createPersonFromPDO($pdoArray){
       // Check here if $pdoArray is an array and it is a valid array.

       $person = new Person();

       foreach($pdoArray as $key => $value){
            switch($key){
                case "birthDate":
                    $person->setBirthDate($value);
                break;
                case "parents":
                    $person->setParents($value);
                break;
            }
       }

       return $person;
   } 
}