====== Db设计文档 ======
Typecho中抛弃了繁琐的MVC构建方式,将所有的功能单纯地封装为一个模块(也就是上述的Widget),使其更加适合开源地并发开发模式.由于没有了Model层的存在,Widget必须直接与数据库联系.Db类的设计就是为了帮助你更快速的获取自己所需要的数据。Typecho的Db对象使用方法与[[http://framework.zend.com|Zend Framework]]中的Db对象很相似,我们参考了Zend_Db中的Select对象组织方法,但不保证完全兼容。它们之间有一些差别,造成这些差别的原因之一就是Zend为不同的操作提供了不同的对象,而Typecho的所有操作都由Typecho_Db_Query来封装成SQL语句的,它的好处是你能在不同的操作中使用统一的API。
===== 初始化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**,它表示可以读也可以写。
===== 构建SQL语句 =====
Typecho_Db内建的SQL语法构建器能够帮助你更容易地构建标准查询语句。
==== 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
==== 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')
==== 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'
==== UPDATE语句 ====