我最近使用数据库对象超类做了一些项目,我用它来快速进行一次性记录查询/更新,并使用适当的类进行扩展,例如User类。
我发现我写的很多类都有完全相同的方法: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);
}
在$ top属性的顶部定义,因为我们应该知道这个特定对象使用哪个表。
现在,所有常见的记录管理方法都在一个地方,并且特定于该类的方法都是在各自的子类中定义的。
我在这里看到的最大缺点是所有数据字段都被拉出并封装在$ fields属性中,因此需要定义泛型get和set方法(我通常这样做)几乎否定封装*,或者必须为我们想要公开的每个属性专门定义一个方法。
*实施例: $ user_id = $ User-> id; //不使用我的方法 与 $ user_id = $ User-> _get('id'); //访问$ User-> fields ['id']
你认为这是一个缺点,还是一个加分?目标是易于使用,面向对象(封装),并且只是非常棒!
答案 0 :(得分:2)
好吧,你可以让你的生活更轻松,并使用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
方法将接收对任何对象方法的调用,该方法与对象的具体方法之一不匹配。它接收作为参数的被调用方法的名称及其参数数组。
上面的__call
方法会快速substr
检查方法是否为“getter”或“setter”(使用方法名称的前三个字母)。它期望您的$fields
数组存储小写“属性”名称(lcfirst
),并使用setter / getter前缀之后的所有内容作为预期的属性名称。
如果属性与getter方法匹配,则返回该属性。如果不是,则抛出SplException BadMethodCallException
。这是最佳实践,因为在PHP中包含了Spl异常。
同样,如果没有指定参数,setter方法也会抛出SplException InvalidArgumentException
。
PHP的神奇方法将改变你的生活。您还可以使用__get
和__set
以类似的方式分配$fields
数组值,而无需进行虚假方法调用。兴奋:)
答案 1 :(得分:1)
有两种主要方法:
我知道的所有大型ORM /数据库项目都不允许用户自己编写所有getter方法。当您调用命令行脚本时,Doctrine和Propel都使用自动生成器来使用getter方法创建真正的PHP文件。这些文件包含所谓的Base方法,这些方法会由您自己编写代码的某些类自动扩展(因此基类的重新创建不会删除您自己的代码)。
文件创建的优势是你有一个定义良好的界面(当可调用的方法在源代码中不可见时,有些人不喜欢它。)
我在客户端的应用程序中看到的数据库引擎使用了神奇的方法模拟 getter和setter。您可以使用魔术方法__get()
和__set()
或__call()
(称为“overloading” in PHP documentation)。使用哪种方法取决于您希望如何访问您的值(最常见的$obj->property
或$obj->getProperty()
)。
优点是您不需要编写复杂的PHP代码生成器,也不需要在每次更改数据库设计时调用命令行脚本。
答案 2 :(得分:0)
通过在对象上实现一些magic methods,您可以充分利用这两个方面。例如,通过实施__get
,__set
和__isset
,您可以使属性访问看起来“自然”(例如$user->id
),同时无需定义属性在每个单独的子类中。
例如,__get
的实现可以在运行时检查$fields
成员,以查看您尝试获取的属性是否对特定对象类有效。您可以在基类中编写此通用实现一次,并相应地填充$fields
以使其适用于任何类型的对象。这实际上是所有现代映射工具的功能,包括您所听说过的所有PHP框架。
顺便说一句,我希望你在get_column_data
内缓存每个类的表模式。每次构造相应类的对象时,为同一个表的模式重新查询数据库是非常低效的。