ORM对象
ORM概念介绍
ORM(Object Relational Mapping对象关系映射)技术能把PHP对象和数据库记录一一对应起来,这样可以把数据记录对象化,或者把运行时对象持久化。有了ORM的帮助,数据库的增、删、改、查操作就不再是使用SQL语句,而更像平时OOP的各种对象操作了。
用xts\Orange
直接完成ORM
xts\Orange
类是xts框架中负责ORM的类,提供ORM的各项基本功能。这个类依赖xts\Query
和xts\SqlBuilder
完成数据库操作。它的配置如下:
'orange' => array(
'class' => '\\xts\\Orange',
'singleton' => false,
'conf' => array(
'tablePrefix' => '',
'queryId' => 'db',
'enableCacheByDefault' => false,
'moldyConf' => array(
'cacheId' => 'cache',
'duration' => 3600,
),
'schemaConf' => array(
'schemaCacheId' => 'cc',
'useSchemaCache' => true,
'schemaCacheDuration' => 0,
),
),
),
配置中的queryId是xts\Query
类的组件ID,默认是db。如果项目的数据表有统一的prefix,可以定义tablePrefix字段。moldyConf是交给xts\MoldyOrange
使用的配置选项,后者是提供Orange缓存支持的类。schemaConf定义了数据库结构信息缓存的机制,默认使用编译缓存。
下面我们使用用户表作为例子说明:
字段名 | 数据类型 | 说明 |
---|---|---|
id | int | 用户ID |
varchar | 电子邮件 | |
screen_name | varchar | 用户名 |
first_name | varchar | 名字 |
last_name | varchar | 姓氏 |
新建
$user = X::orange('user');
xts推荐的方法是用X::orange
方法创建ORM对象实例。这个函数会检查是否存在名为User的Orange的派生类,如果有就返回它的实例,如果没有,会返回一个Orange类的实例。X::orange
方法在XComponentFactory
类中定义,没有使用标准的组件工厂实现。
xts会在内部建立一个数据表user的结构对象,用于记录表的字段和关系。因为表结构不会经常改变,所以默认情况下,xts会打开schemaCache并长期缓存这个信息。如果修改了表结构,可能需要手动删除protected/runtime/compile_cache
目录里的缓存文件。
注意:如果定义了tablePrefix配置项,在新建ORM对象的时候只需要写表名称的有效部分,不需要加上前缀。
Orange会自动维护对象属性和数据表字段间的关系,此外还提供驼峰命名法和下划线命名法的转换,用户可以直接以字段名作为属性名,也可以以对应的驼峰命名作为属性名称。
$user = X::orange('user');
$user->name = "James Bond";
$user->screen_name = "007";
$user->firstName = "James";
$user->lastName = "Bond";
$user->save();
这个例子中,同时使用了直接字段名和驼峰命名的规则来访问对象的属性,这都是可以的。最后的save方法调用会把数据插入到MySQL中。这里没有给$user->id
赋值,因为在数据库中定了了ID为主键自增的字段。当然也可以根据需要指定id的值,不过这样有可能和自增长的值发生冲突。如果没有启用自增主键,则问题不大。
加载
我们会时常需要从数据库中按id读取一条记录,这时可以使用Orange的load方法。
$user = X::orange('user')->load($_SESSION['user_id']);
if ($user instanceof xts\Orange) {
X::view()->assign('currentUser', $user);
} else {
X::apple()->redirect('/login')->end();
}
上面是一个登录检查的例子,load方法在找不到记录的时候会返回null,所以可以根据返回值类型来确定是否成功加载出数据库记录。
修改
使用ORM来修改数据库记录,遵循加载、赋值、保存的步骤。前面已经提供了加载的范例代码,修改操作的代码和新建时相似,也是直接对ORM对象赋值。不同点在于修改操作的对象不是新建出来的,而是加载出来的。xts的ORM机制支持按需生成UPDATE语句,这意味着实际操作数据库的时候只有被真正修改的字段会被更新。
保存
在新建的例子代码当中示范了最常见的保存方法——直接调save方法即可。
xts的ORM对象还支持一些更复杂的保存操作。比如有的时候我们不确定是否已经存在重复的记录,可能需要INSERT IGNORE
语句,或者INSERT ... ON DUPLICATE KEY UPDATE ...
语句,当然还有REPLACE
语句。可以利用save方法的第一个参数来通知xts生成不同的查询语句,此参数默认是xts\Orange::INSERT_NORMAL
,生成一条普通的INSERT
语句。
$user->save(xts\Orange::INSERT_IGNORE); // 生成 INSERT IGNORE 语句
$user->save(xts\Orange::INSERT_UPDATE); // 生成 INSERT ... ON DUPLICATE KEY UPDATE ... 语句
$user->save(xts\Orange::INSERT_REPLACE); // 生成 REPLACE 语句
查询
xts的查询生成SELECT语句。和load总成用主键查询不同,这里的查询可以自定义条件。xts提供的查询方法包括单条查询和多条查询。分别使用one
和many
方法。
$user = X::orange('user')->one('email=:eml AND password=:pwd', array(
':eml' => $_POST['email'],
':pmd' => sha1($_POST['password'] . X::$conf['salt']),
));
上面是one
方法的使用范例,此方法接收两个参数,条件和数据列表。如果有多个复合条件,需要直接写成字符串。相比one
方法,many
方法要多两个参数offset
和limit
。
$users = X::orange('user')->many('first_name LIKE :fn ORDER BY id ASC', array(
':fn' => $_GET['q'] . '%',
), 0, 20);
many
方法返回一个以主键为Key,xts\Orange
对像为Value组成的关联数组。
删除
remove
方法提供删除功能,这个方法有两种调用方式,一种接收要删除数据的主键为参数,删除这个主键所对应的一行数据;另一种不用传入参数,删除当前对象在数据库中的映射。
X::orange('user')->remove(47);
上面的例子代码能直接删除ID为47的用户记录,不论他是否存在;下面的例子能把所有名字以Th开头的用户删除。
$users = X::orange('user')->many("first_name LIKE 'Td%'");
foreach ($users as $user) {
$user->remove();
}
组装和拆包
有的时候我们会用X::db()->query
进行复杂的SQL查询,但又想把查询结果以ORM对象的方式使用,这时候需要用到组装功能。
$userArray = X::db()->query("SELECT * FROM `user` WHERE `first_name`='Thomas'");
$users = array();
foreach ($userArray as $userArr) {
$users[$userArr['id']] = X::orange('user')->setup($userArr);
}
前面是组装的例子,setup
方法能把数组组装成ORM对象。
另一种相反的情况,我们可能需要把ORM对象转换为关联数组,这时候可以调用getProperties
方法或者直接调properties
属性。
$user = X::orange('user')->load(17);
$userArr = $user->properties;
自定义ORM对象
xts提供的ORM功能只有数据库核心的操作功能。如果需要把一些逻辑代码写到Model层里,也可以编写自己的ORM对象。只需要继承xts\Orange
类就可以了。
class User extends xts\Orange {
public function __construct() {
parent::__construct('user');
}
// Your code here
}
特殊的表名称
自定义ORM对象中,一种常见的情况是需要定义特殊的表名称,比如带有特殊的前缀,这时候可以通过重载getTableName
方法来达成目的。
public function getTableName() {
return 'special_user';
}
上面的例子是直接简单地返回表名称的字符串,但其实可以进行一定的状态判断,并返回适当的表名称。
属性Getter
xts\Orange
对象支持Getter和Setter方法,继承它的子类也会自动取得相应支持,Getter和Setter在属性取用时拥有最高的优先权。在xts框架中,Getter方法的名称使用get+属性名的驼峰命名格式,Setter方法名称使用set+属性名的驼峰命名格式。比如以下是一个Getter的实例
public function getAge() {
list($bYear, $_) = explode('-', $this->birthday, 2);
return date('Y') - $bYear;
}
有了这个Getter,我们就可以直接用$user->age
的方式访问这个属性了。
JSON迭代器
xts\Orange
类实现了JsonSerializable
接口,所以可以直接被json_encode函数序列化。默认情况下,json_encode会输出所有ORM映射的字段。不过我们也可以根据需要将Getter或者Relation字段也输出出来。只需通过重载getExportProperties
就可以。
protected function getExportProperties() {
return array_merge(array_keys($this->_properties), array('age'));
}
上面的例子在基本的数据库字段外,加入了age字段一并进行json_encode序列化。