PHP数据库操作面向对象的优点


Posted in PHP onOctober 09, 2006

我们都知道如何从Mysql获取我们需要的行(记录),读取数据,然后存取一些改动。很明显也很直接,在这个过程背后也没有什么拐弯抹角的。然而对于我们使用面对对象的程序设计(OOP)来管理我们数据库中的数据时,这个过程就需要大大改进一下了。这篇文章将对如何设计一个面对对象的方式来管理数据库的记录做一个简单的描述。你的数据当中的所有内部逻辑关系将被封装到一个非常条理的记录对象,这个对象能够提供专门(专一)的确认代码系统,转化以及数据处理。随着Zend Engine2 和PHP5的发布,PHP开发者将会拥有更强大的面对对象的工具来辅助工作,这将使这个过程(面对对象地管理数据库)更有吸引力。

以下列出了一些使用对象来描叙你的数据库的有利方面:

存取方法(Accessor methods)将会使你对属性的读取和写入过程做到完全的控制
每一级的每个记录和属性(的操作)都有确认过程
从关系表中智能的获取对象
重复使用的逻辑方法意味着所有的数据交互都要通过相同的基础代码(codebase),这将使维护变得更加简单
代码简单,因为不同的记录的内部逻辑都已经包含在各自所处的类(class)当中,而不是繁琐的库(lib)文件
在手工编写代码和SQL查询语句时,出错的机会将更少

存取方法(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类。

变量$username将不复存在,所有的东西都被整合的放在数组$_data当中
如果username是空的话,username()函数将提供一个缺省(默认)的值给它
setUsername()过程将在接受值之前确认username是否合乎标准格式(如字长等)

<?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()方法之外的。从验证到存储到数据库的过程轻而易举。而且,这是个非常好的单凭经验的方法,一个方法或一个类需要做的越少,它的重复使用的机会将会越大。这在你开始写一个子类时更加明显,假如你需要一个子类,并且又要跳过(忽略)父类方法(行为)中的一些特殊的细节,如果(针对这个细节的)方法很小而又精细,(修改它)只是一瞬间的过程,而如果这个方法非常臃肿,针对多种目的,你可能将在复制子类中大量代码中郁闷而终。

比方说,假如Admin是User类的一个子类。我们对adamin的用户可能会有不同的,相对苛刻一些的密码验证方法。最好是跨过父类的验证方法和整个setUsername()方法(在子类中重写)。

更多关于存取器(Accessor)
下面是一些其他的例子来说明如何使存取器用的更有效果。很多时候我们可能要计算结果,而不是简单的返回数组中的静态数据。存取方法还能做的一个有用的事情就是更新(updating)缓存中的值。当所有的变动(对数据的所有操作)都要通过setX()方法的时候,这正是我们根据X来重置缓存中的值的时刻。

于是我们的这个类层次变得更加明了:

内部变量$_data的处理被替换成受保护的私有方法(private methods)_getData()和_setData()
这类方法被转移到被称作记录(Record)的抽象的超级类(super class),当然它是User类下的子类
这个记录类(Record class)掌握所有存取数组$_data的细节,在内容被修改之前调用验证的方法,以及将变更的通知发给记录(Records),就像发给中心对象存储(ObjectStore)实例。

<?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类更加瘦小而又有效率,而这对于评价我们处理大量对象的效率的时候是个重要因素。

PHP 相关文章推荐
php模板之Phpbean的目录结构
Jan 10 PHP
php小技巧 把数组的键和值交换形成了新的数组,查找值取得键
Jun 02 PHP
php更新mysql后获取影响的行数发生异常解决方法
Mar 28 PHP
php+js iframe实现上传头像界面无跳转
Apr 29 PHP
微信API接口大全
Apr 15 PHP
详解PHP匿名函数与注意事项
Mar 29 PHP
php smtp实现发送邮件功能
Jun 22 PHP
PHP将数据导出Excel表中的实例(投机型)
Jul 31 PHP
PHP实现基于栈的后缀表达式求值功能
Nov 10 PHP
PHP分享图片的生成方法
Apr 25 PHP
php服务器的系统详解
Oct 12 PHP
laravel ORM关联关系中的 with和whereHas用法
Oct 16 PHP
PHP5中MVC结构学习
Oct 09 #PHP
PHP5/ZendEngine2的改进
Oct 09 #PHP
PHP模板引擎SMARTY
Oct 09 #PHP
PHP入门速成(2)
Oct 09 #PHP
用PHP制作静态网站的模板框架
Oct 09 #PHP
PHP5在Apache下的两种模式的安装
Sep 05 #PHP
WINDOWS 2000下使用ISAPI方式安装PHP
Sep 05 #PHP
You might like
PHP 冒泡排序 二分查找 顺序查找 二维数组排序算法函数的详解
2013/06/25 PHP
ThinkPHP模板引擎之导入资源文件方法详解
2014/06/18 PHP
PHP中Http协议post请求参数
2015/11/02 PHP
Yii中CGridView实现批量删除的方法
2015/12/28 PHP
工作需要写的一个js拖拽组件
2011/07/28 Javascript
jQuery插件原来如此简单 jQuery插件的机制及实战
2012/02/07 Javascript
JavaScript动态插入script的基本思路及实现函数
2013/11/11 Javascript
JavaScript列表框listbox全选和反选的实现方法
2015/03/18 Javascript
JavaScript实现获取远程的html到当前页面中
2017/03/26 Javascript
CentOS 安装NodeJS V8.0.0的方法
2017/06/15 NodeJs
详解如何构建Angular项目目录结构
2017/07/13 Javascript
Vue-Router进阶之滚动行为详解
2017/09/13 Javascript
利用nvm管理多个版本的node.js与npm详解
2017/11/02 Javascript
react-redux中connect的装饰器用法@connect详解
2018/01/13 Javascript
vue中简单弹框dialog的实现方法
2018/02/26 Javascript
js中document.write和document.writeln的区别
2018/03/11 Javascript
微信小程序input框中加入小图标的实现方法
2018/06/19 Javascript
bootstrap 弹出框modal添加垂直方向滚轴效果
2018/07/09 Javascript
layui中table表头样式修改方法
2018/08/15 Javascript
uni-app 支持多端第三方地图定位的方法
2020/01/03 Javascript
Vue实现手机号、验证码登录(60s禁用倒计时)
2020/12/19 Vue.js
Python实现将n个点均匀地分布在球面上的方法
2015/03/12 Python
Python读取一个目录下所有目录和文件的方法
2016/07/15 Python
Python3 翻转二叉树的实现
2019/09/30 Python
关于TensorFlow新旧版本函数接口变化详解
2020/02/10 Python
Tirendo比利时:在线购买轮胎
2018/10/22 全球购物
奥地利度假券的专家:we-are.travel
2019/04/10 全球购物
写出二分查找算法的两种实现
2013/05/13 面试题
公立医院改革实施方案
2014/03/14 职场文书
农村葬礼主持词
2014/03/31 职场文书
小学教师师德承诺书
2014/05/23 职场文书
汉语言文学毕业求职信
2014/07/17 职场文书
计划生育工作总结2015
2015/04/03 职场文书
电影圆明园观后感
2015/06/03 职场文书
运动会通讯稿100字
2015/07/20 职场文书
windows10 家庭版下FTP服务器搭建教程
2022/08/05 Servers