我们都知道如何从Mysql获取我们需要的行(记录),读取数据,然后存取一些改动。很明显也很直接,在这个过程背后也没有什么拐弯抹角的。然而对于我们使用面对对象的程序设计(OOP)来管理我们数据库中的数据时,这个过程就需要大大改进一下了。这篇文章将对如何设计一个面对对象的方式来管理数据库的记录做一个简单的描述。你的数据当中的所有内部逻辑关系将被封装到一个非常条理的记录对象,这个对象能够提供专门(专一)的确认代码系统,转化以及数据处理。随着Zend Engine2 和PHP5的发布,PHP开发者将会拥有更强大的面对对象的工具来辅助工作,这将使这个过程(面对对象地管理数据库)更有吸引力。
以下列出了一些使用对象来描叙你的数据库的有利方面:
存取方法(Accessor methods)
存取方式是通过类给实例(instance)的变量赋值。一个例子,我有一个叫User的类,并且有一个实例$username,我会写这样的存取方法(函数),User->username()和User->setUsername()用来返回和给实例赋值。
<?php class User { var $username; function username() { return $this->username; } function setUsername($newUsername) { $this->username = $newUsername; } } ?> |
这里有很好的理由让我们编写这样的“特别的代码”。它将使开发者更灵活的改变类的繁琐的工作,因为这一过程将不需要其他的使用类的php代码。让我们来看看下面这个更加完善的可信赖的User类。
<?php class User { var $_data = array(); // associative array containing all the attributes for the User function username() { return !empty($this->_data['username']) ? $this->_data['username'] : '(no name!)'; } function setUsername($newUsername) { if ($this->validateUsername($newUsername)) { $this->_data['username'] = $newUsername; } } function validateUsername(&$someName) { if (strlen($someName) > 12) { throw new Exception('Your username is too long'); // PHP5 only } return true; } } ?> |
显而易见,这对我们控制存取对象的数据有很大帮助。如果一个程序员已经直接地存取username的信息,以上代码的变化将会破坏他的代码。然而我们可以使用(类的)存取方法,就像上面代码中注释的那样,添加一个验证的功能而不需要改变任何其他的东西。注意username的验证(例子当中是不能超过12字节)代码是独立在setUsername()方法之外的。从验证到存储到数据库的过程轻而易举。而且,这是个非常好的单凭经验的方法,
<?php class User extends Record { // --- OMITTED CODE --- // /** * Do not show the actual password for the user, only some asterixes with the same strlen as the password value. */ function password() { $passLength = strlen($this->_getData('password')); return str_repeat('*', $passLength); } /** * Setting the user password is not affected. */ function setPassword($newPassword) { $this->_setData('password', $newPassword); } /** * fullName is a derived attribute from firstName and lastName * and does not need to be stored as a variable. * It is therefore read-only, and has no 'setFullname()' accessor method. */ function fullName() { return $this->firstName() . " " . $this->lastName(); } /** * Spending limit returns the currency value of the user's spending limit. * This value is stored as an INT in the database, eliminating the need * for more expensive DECIMAL or DOUBLE column types. */ function spendingLimit() { return $this->_getData('spendingLimit') / 100; } /** * The set accessor multiplies the currency value by 100, so it can be stored in the database again * as an INT value. */ function setSpendingLimit($newSpendLimit) { $this->_setData('spendingLimit', $newSpendLimit * 100); } /** * The validateSpendingLimit is not called in this class, but is called automatically by the _setData() method * in the Record superclass, which in turn is called by the setSpendingLimit() method. */ function validateSpendingLimit(&$someLimit) { if (is_numeric($someLimit) AND $someLimit >= 0) { return true; } else { throw new Exception("Spending limit must be a non-negative integer"); //PHP5 only } } } /** * Record is the superclass for all database objects. */ abstract class Record { var $_data = array(); var $_modifiedKeys = array(); // keeps track of which fields have changed since record was created/fetched /** * Returns an element from the $_data associative array. */ function _getData($attributeName) { return $this->_data[$attributeName]; } /** * If the supplied value passes validation, this * sets the value in the $_data associative array. */ function _setData($attributeName, $value) { if ($this->validateAttribute($attributeName, $value)) { if ($value != $this->_data[$attributeName]) { $this->_data[$attributeName] = $value; $this->_modifiedKeys[] = $attributeName; $this->didChange(); } else { // the new value is identical to the current one // no change necessary } } } /** * For an attribute named "foo", this looks for a method named "validateFoo()" * and calls it if it exists. Otherwise this returns true (meaning validation passed). */ function validateAttribute($attributeName, &$value) { $methodName = 'validate' . $attributeName; if (method_exists($this, $methodName)) { return $this->$methodName($value); } else { return true; } } function didChange() { // notify the objectStore that this record changed } } ?> |
现在我们拥有了一个抽象的超级类(Record),我们可以将User类里面大量的代码转移出来,而让这个User的子类来关注User的特殊项目如存取和验证方法。你可能已经注意到在我们的这个纪录类(Record class)没有任何的SQL代码。这并不是疏忽或者遗漏!对象存储类(ObjectStore class)(隐藏在第二部分)将负责所有和数据库的交互,还有我们的超级类Record的实例化。这样使我们的Record类更加瘦小而又有效率,而这对于评价我们处理大量对象的效率的时候是个重要因素。
如果你有兴趣看看这篇文章基于的完整的代码(不会出现如文中出现的所有的语法错误),可以给我的邮箱发信[email protected]
原文地址: http://www.phpbuilder.com/columns/barnum20030509.php3?page=1