1. DB设计概述

Typecho中抛弃了繁琐的MVC构建方式,将所有的功能单纯地封装为一个模块(也就是上述的Widget),使其更加适合开源地并发开发模式.由于没有了Model层的存在,Widget必须直接与数据库联系.Db类的设计就是为了帮助你更快速的获取自己所需要的数据。Typecho的Db对象使用方法与Zend Framework中的Db对象很相似,我们参考了Zend_Db中的Select对象组织方法,但不保证完全兼容。它们之间有一些差别,造成这些差别的原因之一就是Zend为不同的操作提供了不同的对象,而Typecho的所有操作都由Typecho_Db_Query来封装成SQL语句的,它的好处是你能在不同的操作中使用统一的API。

1.1 初始化Db

在任何时候我们都可以通过如下方法获取一个实例化的Db对象。

//get方法是Typecho_Db的静态方法
$db = Typecho_Db::get();

但是在此之前,你必须利用Typecho_Db::set方法设置一个默认的且已经实例化好的db对象,db对象的实例化方法如下。

/** 定义数据库参数 */
$db = new Typecho_Db('Mysql', 'typecho_');
$db->addServer(array(
    'host'          =>  'localhost',
    'port'          =>  '3306',
    'user'          =>  'root',
    'password'      =>  '',
    'database'      =>  'typecho',
    'charset'       =>  'utf8'
), Typecho_Db::READ | Typecho_Db::WRITE);
Typecho_Db::set($db);

它的构造函数第一个参数指定所用的适配器名称,第二个参数指定数据库表前缀,其中第二个参数可以不设置。当构造好一个db类以后,你可以将其看作一个数据库池,后面要做的就是往这个池中放入数据库连接。在上例中,我们为数据库对象加入了一个服务器,并为其指定行为为Typecho_Db::READ | Typecho_Db::WRITE,它表示可以读也可以写。

2 构建SQL语句

Typecho_Db内建的SQL语法构建器能够帮助你更容易地构建标准查询语句。

2.1 SELECT查询语句

select是我们最常用的查询方式,以下代码向您展示了如何使用Db类从posts表中取出所有数据.

//获取实例化对象
$db = Typecho_Db::get();

//获取查询语句对象
$select = $db->select()->from('posts');

//提交查询并获取结果
$result = $db->fetchAll($select);

查询指定字段

在以上查询代码中,我们从posts表中取出了所有数据,但同时也取出了所有字段,如果你只需要取出指定的几个字段,只需要对上述代码做出如下改进.

//重新构建select语句,在from方法中传递需要选择字段,并用半角逗号隔开
$select = $db->select('title', 'text', 'type')->from('posts');

增加查询条件

大多数查询都需要增加查询条件,我们通过如下代码给我们的查询增加一个type = 'post'的条件.

$select = $db->select('title', 'text', 'type')->from('posts')
->where('type = ?', 'post');

如果需要增加多条条件限制,可以在后面继续调用where方法

$select = $db->select('title', 'text', 'type')->from('posts')
->where('type = ?', 'post')
->where('title = ?', 'hello');

它等效于type = 'post' AND title = 'hello'.你也可以用以下语句代替上述代码

$select = $db->select('title', 'text', 'type')->from('posts')
->where('type = ? AND title = ?', 'post', 'hello');

OR条件查询

使用where方法只能生成AND的并列条件查询,如果你需要OR条件查询,需要使用orWhere方法.我们通过下面的方法,给我们的查询增加type = 'post' OR type = 'page'的条件.

$select = $db->select('title', 'text', 'type')->from('posts')
->where('type = ?', 'post')
->orWhere('type = ?', 'page');

查询排序

在Typecho_Db中仅提供两种排序方法,非别为升序(Typecho_Db::SORT_ASC)和降序(Typecho_Db::SORT_DESC).使用order方法即可完成查询排序.

$select = $db->select()
->from('posts')
->where('type = ?', 'post')
->order('created', Typecho_Db::SORT_DESC);    //降序排序

如果你不指定order方法的第二个参数,它将按照此数据库的默认方式排序,一般为升序.

指定查询范围

在Mysql中我们通常使用LIMIT语法来限制查询范围并实现翻页操作,但在其它数据库中则有所不同.不过使用Typecho_Db时你不需要关心这些语法,只需要关注你需要查询的范围即可.下面的代码展示了如何使用limit方法限制查询范围

$select = $db->select()
->from('posts')
->where('type = ?', 'post')
->limit(10);    //仅取出10条记录

我们亦可以使用offset函数来指定查询范围的偏置值,也就是从第几条记录开始查询

$select = $db->select()
->from('posts')
->where('type = ?', 'post')
->offset(20)    //从第20条记录开始取
->limit(10);

在大多数情况下,我们使用范围限制是为了实现分页操作,我们在Typecho_Db中已经封装了分页操作,你只需要调用page方法即可实现分页,它的第一个参数是页数,第二个参数是每页的记录数.

$select = $db->select()
->from('posts')
->where('type = ?', 'post')
->page(10, 20);    //按每页20条记录的方法取出第10页的记录

对引号的过滤

引号过滤是防范SQL注入的关键,你可以在上述代码中看到,Typecho的查询条件并没有直接写入语句,而是使用占位符的方式,使用参数传递来写入查询值.因此,使用占位符方式来构建查询语句,Typecho会在底层帮你处理引号,你自己不需要对此作出处理,而且也不要在字符型查询的外侧加上单引号,这种声明系统会自动处理.

联合查询

联合查询是SQL的常用语法,在Typecho中,您可以很方便的使用此类语句。下列代码展示了如何对两个表实现左连接

$select = $db->select()
->from('posts')
->join('category', 'posts.category_id = category.id', Typecho_Db::LEFT_JOIN)
->where('`type` = ?', 'post');

我们可以看到join方法一共有三个参数,第一个参数标明需要连接的表名,第二个参数是连接条件,第三个参数表示连接方法为左连接。其余的连接方式还有Typecho_Db::INNER_JOIN(内连接,如果第三个参数不填,则默认为内连接方式),Typecho_Db::OUTER_JOIN(外连接),Typecho_Db::LEFT_JOIN(左连接),Typecho_Db::RIGHT_JOIN(右连接)。

查看查询语句

有时候我们并不能保证自己所构建的查询语句是否正确,因此我们需要将查询语句输出。在typecho中,你可以很容易地查看sql语句。对于php 5.2以上版本,你可以直接用echo语句输出select对象,对于php 5.2以下版本,你需要调用select对象的toString方法输出。

$select = $db->select('posts')
->from('posts');

//如果版本大于php5.2
echo $select;

//如果小于php5.2
echo $select->__toString();

//output: SELECT * FROM posts

2.2 INSERT语句

使用insert方法可以用于构建新增记录的查询语句。

插入一条记录

在typecho中,你不需要对插入的字段进行防注入过滤,我们已经在后端帮你完成了这个功能。以下代码揭示了如何构建一条标准的插入语句

$db = Typecho_Db::get();

$insert = $db->insert('post')
->rows(array('title' => 'example', 'content' => 'hello world'));

//将构建好的sql执行, 如果你的主键id是自增型的还会返回insert id
$insertId = $db->query($insert);

以上代码等价于

INSERT INTO post (title, content) VALUES ('example', 'hello world')

2.3 DELETE语句

delete方法用于删除数据库中的制定方法。

根据指定条件删除记录

我们通过在delete方法中使用我们在前面已经熟知的where语句来方便得进行条件删除。请看如下代码

$db = Typecho_Db::get();

$delete = $db->delete('post')
->where('title = ?', example);

//将构建好的sql执行, 会自动返回已经删除的记录数
$deletedRows = $db->query($delete);

以上代码等价于

DELETE FROM post WHERE title = 'example'

2.4 UPDATE语句