【问题标题】:Database Object Class PHP数据库对象类 PHP
【发布时间】:2012-01-19 17:46:10
【问题描述】:

我最近使用数据库对象超类完成了一些项目,我使用该超类进行快速的一次性记录查询/更新,并使用适当的类进行扩展,例如用户类。

我发现我正在编写的许多类都有完全相同的方法:query_values()、update()、delete() 等。

所以我想出了一个带有如下构造函数的类:

public function __construct($table, $db_object, $record_id = null){
    $this->db = $db_object; // Database object with query methods

    $this->table = $table; // The name of the database table

    $this->get_column_data();

    if(!is_null($record_id)){
        // This retrieves all column values, 
        // stores into private $fields array property
        $this->query_values($record_id);
    }
}

子类构造函数如下所示:

public function __construct($db_object, $record_id = null){
    parent::__construct($this->table, $db_object, $record_id);
}

$table 属性定义在顶部,因为我们应该知道这个特定对象使用哪个表。

现在,所有常见的记录管理方法都在一个地方,特定于类的方法都在它们各自的子类中定义。

我在这里看到的最大缺点是所有数据字段都被提取并封装在 $fields 属性中,因此需要定义通用 get 和 set 方法(我通常这样做),这几乎否定了封装*,或者必须为我们要公开的每个属性专门定义一个方法。

*示例: $user_id = $User->id; // 不使用我的方法 对比 $user_id = $User->_get('id'); // 访问 $User->fields['id']

您认为这是缺点还是优点?目标是易于使用、面向对象(封装),而且非常棒!

【问题讨论】:

标签: php database oop


【解决方案1】:

好吧,您可以使用 PHP 的神奇重载 __call 方法来创建通用的 getter 和 setter,让您的生活变得轻松。您可以将以下方法添加到“数据库对象超类”中:

/**
 * Create magic getter and setter methods to access private $fields array 
 */
public function __call($method, $args)
{
  $prefix = substr($method, 0, 3);
  $prop   = lcfirst(substr($method, 3));

  if (isset($this->fields[$prop])) {

    if ($prefix == 'get') {
      return $this->fields[$prop];
    } elseif ($prefix == 'set') {
      if ( ! isset($args[0])) {
        $msg = 'Missing argument: ' . get_class($this) . "::$method must specify a value";
        throw new InvalidArgumentException($msg);
      }
      $this->fields[$prop] = $args[0];
      return;
    }
  }

  $msg = 'Invalid method: ' . get_class($this) . "::$method does not exist";
  throw new BadMethodCallException($msg);
}

所以让我解释一下这里发生了什么。神奇的__call 方法将接收对与对象的具体方法之一不匹配的any 对象方法的调用。它接收被调用方法的名称及其参数数组作为参数。

上面的__call 方法对substr 进行快速检查以查看该方法是“getter”还是“setter”(使用方法名称的前三个字母)。它希望您的 $fields 数组存储小写的“属性”名称 (lcfirst),并使用 setter/getter 前缀之后的所有内容作为预期的属性名称。

如果某个属性与 getter 方法匹配,则返回该属性。如果不是,则抛出 SplException BadMethodCallException。这是自 PHP 中包含 Spl 异常以来的最佳实践。

同样,如果没有指定参数,setter 方法也会抛出 SplException InvalidArgumentException

PHP 的神奇方法将改变您的生活。您还可以使用__get__set 以类似的方式分配$fields 数组值,而无需进行虚假方法调用。兴奋起来:)

【讨论】:

    【解决方案2】:

    通过在对象上实现一些magic methods,您可以两全其美。例如,通过实现__get__set__isset,您可以使属性访问看起来“自然”(例如$user->id),同时不必在每个单独的子类中定义属性。

    例如,__get 的实现可以在运行时检查$fields 成员,以查看您尝试获取的属性是否对特定对象类有效。您可以在基类中编写此通用实现一次,然后相应地填充$fields 以使其适用于任何类型的对象。这实际上是所有现代地图工具所做的,可能包括您听说过的所有 PHP 框架。

    顺便说一句,我希望你在get_column_data 中缓存每个类的表模式。每次构造相应类的对象时,都要重新查询数据库以获取同一张表的架构,效率真的很低。

    【讨论】:

      【解决方案3】:

      主要有两种方法:

      PHP 文件创建

      我知道的所有大型 ORM / 数据库项目都不让用户自己编写所有 getter 方法。当您调用命令行脚本时,DoctrinePropel 都使用自动生成器通过 getter 方法创建真正的 PHP 文件。这些文件包含所谓的 Base 方法,这些方法会由您将自己的代码放入其中的某些类自动扩展(因此重新创建基类不会删除您自己的代码)。

      文件创建的优点是你有一个定义良好的接口(有些人不喜欢可调用的方法在源代码中不直接可见)。

      魔术方法/重载

      我在客户端应用程序中看到的一个数据库引擎使用魔术方法来模拟 getter 和 setter。您可以为此使用魔术方法__get()__set()__call()(称为“overloading” in PHP documentation)。使用哪种方法取决于您希望如何访问您的值(最常见的$obj->property$obj->getProperty())。

      重载的优点是无需编写复杂的 PHP 代码生成器,也无需在每次更改数据库设计时调用命令行脚本。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-11-13
        • 1970-01-01
        • 2011-10-16
        • 2014-07-16
        • 2017-01-13
        • 2013-06-29
        • 1970-01-01
        相关资源
        最近更新 更多