用于包装数据库行的子类的OOP方法

时间:2011-08-22 11:11:42

标签: php oop orm subclass

假设我想将狗存储在数据库表中,每只狗在PHP中都有自己的子类。

基本上我想避免在代码中的不同位置存储/列出子类名。对此有什么好的OOP方法?

abstract class Dog {
    protected $data;
    public function __construct($data) {
        $this->data = $data;
    }
    public function name() {
        return $this->data["name"];
    }
    abstract public function breed();
}

class GermanShepherd extends Dog {
    public function breed() {
        return _("German Shepherd");
    }
}

class BullDog extends Dog {
    public function breed() {
        return _("Bulldog");
    }
}

现在我有这个类来处理对象组(即狗):

class Dogs {
    public static function getDogs() {
        // ...
        $ret = array();
        while ($row = mysql_fetch_assoc()) {
            switch ($row["type"]) { // I could do this using a lookup array
                 case "shepherd": $dog = "GermanShepherd"; break;
                 case "bulldog": $dog = "Bulldog"; break;
            }
            $ret[] = new $dog($row);
        }
        return $ret;
    }
}

我想使用这个类在我的视图中获取狗类型(特别是对于添加狗形式),而不是列出类名:

?><form><select name="type"><?php
foreach (array("GermanShepherd", "Bulldog") as $dog) { // here I would like to do avoid listing the class names again
    ?><option value="<?=$dog ?>"><?php
    $d = new $dog; // actually I can't instantiate the class here because I don't have any data at this point
    echo $d->name();
    ?></option><?php
}
?></select></form><?php

我想将此结合到Dogs类中,类似于以下内容:

class Dogs {
    private static $dogs = array(
        "shepherd" => "GermanShepherd",
        "bulldog" => "Bulldog",
    );
    public static function getDogs() {
        // ...
        $ret = array();
        while ($row = mysql_fetch_assoc()) {
            $dog = self::$dogs[$row["type"]];
            $ret[] = new $dog($row);
        }
        return $ret;
    }

    public static function getDogTypes() {
        return array_values(self::$dogs);
    }
}

?><form><select name="type"><?php
foreach (Dogs::getDogTypes() as $dog) {
    ?><option value="<?=$dog ?>"><?php
    $d = new $dog; // here I still need to instantiate the class and I don't have any data to provide it with
    echo $d->name();
    ?></option><?php
}
?></select></form><?php

到目前为止,这有点工作,但如果我需要更多类特定信息,例如当我有更多特定于狗类型的字段时,该怎么办?

foreach (Dogs::getDogTypes() as $dog) {
    $d = new $dog; // instantiate again?
    foreach ($d->formFields() as $f) { // I wouldn't do it like this, just putting this here for demonstrative purposes
        echo $f;
    }
}

我认为部分问题在于我需要能够使用带有和不带数据库数据的类:当我从数据库表中获取数据时,一切看起来都很合理,但我还需要数据时我在创建新狗时会生成表单。

感谢您的想法!

3 个答案:

答案 0 :(得分:2)

首先使用Interfaces。这将向您展示具有更多特定接口(每个子类的不同类方法和属性)将使您需要以不同方式处理它们。因此,他们将向您展示缺陷的位置,并使您能够将对象简化为更易于使用的对象。

只要您的对象只存储一些数据,请使用data transfer object代替 - 这是任何“类型”。所以你不需要处理类型。但是,如果您想将其保持基本状态,也可以使用StdClassArray。正面是:你不需要实际编写那么多代码。

如果它不够(原样),只需在需要时添加代码。从长远来看,应该让事情变得更简单。所以从一个基本的数据传输对象类开始并构建它。

因此,使用您编写的类来分离关注点,而不是交织关注点。封装各种不同的内容,因此您的代码实际上可以从您的设计中受益。

答案 1 :(得分:1)

我认为Dogs类中的静态数组是一个非常可接受的解决方案。它不包括实例化问题,但您可以使用(静态)工厂方法来解决此问题。为了使实例化更容易和更具可伸缩性,您可以确保存储的字符串以某种方式映射到对象名称:

$dog = 'Dog' . ucfirst( $row['type'] );
$ret[] = new $dog;

我认为->getFormFields()方法根本不是一个坏主意;当每个Dog类型的字段不同时,将它合并到对象中是完全有效的OO!

答案 2 :(得分:0)

如何将您的狗存放在2D阵列中?

while ($row = mysql_fetch_assoc()) {
  switch ($row["type"]) { // I could do this using a lookup array
    case "shepherd": 
      $dog = "GermanShepherd"; 
      break;
    case "bulldog": 
      $dog = "Bulldog"; 
      break;
  } // switch

  $ret[$row["type"]][] = new $dog($row);
} // while