数据库操作
文章分页:

在前文代理类中提到,本框架的数据库调用方法通过代理的模式实现多接口、多数据库调用,具体声明方式详见代理类说明

扩展类

数据库类中包含两个扩展类,分别是 base_dbbase_sql

base_db

本扩展类中设置了通用的数据库方法,包括:

1 safeName 规范数据表字段名称,去除非法字符
2 safeValue 规范数据内容,去除非法字符,避免注入危险,相对于 mysql_real_escape_string 等方法,可以在未连接数据库的情况下执行
3 cache 设置外部缓存模式 0 - 关闭, 1 - 可读, 2 - 可写,3 - 可读写
4 setCache 传入缓存实例,此实例必须为框架自带的myCache类(可通过代理模式扩展)
5 getCache 获取缓存内容,参考缓存模式权限
6 writeCache 写入缓存内容,参考缓存模式权限
base _sql

本扩展类是针对标准数据库服务的扩充,预定义了以下内容:

  • 服务器配置参数,包括数据库服务器地址($host)、用户($user)、密码($pwd)、字符编码($charset)、当前查询($sql)、当前数据库($db)、定界符($delimiter )等,相关参数均为内部参数,如功能类引用了此扩展,需要遵循以上变量名称。
  • SQL构建方法,基于SQLBuilder 类,并扩充了CRUD的多关系表操作,包括 select, update, delete, record(返回单一记录), records(返回全部记录), result(返回第一行第一项), count(返回某查询记录数) 等,相关方法基于mysql,如有语法不同,可编写对应转换方法,或在功能类中重写对应方法。
调用方式

只要在对应的功能类中 use 对应的扩展类即可,如下:

class someDatabase {  
    use base_db, base_sql;  
    ......  
}

 

单表查询

SQLBuilder 为查询语句生成类,可以通过简单的链式操作,生成复杂的SQL查询语句。

构造函数

本函数包含四个参数,如下:

1 $tbl 数据表名称
2 $join 连接关系,包含 mode(left,right 或 inner),field(关联字段名),field_join(如关联字段与主表名称不一致,此项为主表对应字段的名称)
3 $idx 数据表别名(便于引用)
4 $delimiter 名称定界符,用于区分关键字,如mysql中的“`”或mssql中的“[]”
reset

将类内所有变量初始化,如参数1设定为true的话,连表名、别名和连接关系也一同复位。

field

指定当前表查询字段,可为如下几种情况:

  • 针对 select 查询,参数可为以逗号间隔的字符串,或者每个字段为一个独立项的数组,也可以为多个参数,每个参数对应一个字段。字段项目可以为如下特殊格式:
    • ['alias_name', 'col_name']‘alias_name as col_name’‘alias_name:col_name’‘alias_name=>col_name’,表示给对应字段指派别名。
    • ‘func(para1, para2)’,表示使用数据库自带函数,如 length(col_name)
    相关示例如下:
    $db->build('table_name')  
        ->field('col_a, col_b, col_c')  
        ->field([  
            'col_name',  
            'name:name_alias',  
            'count(*) as cnt',  
            'sum(amount*price) as v1',  
            ['length(col1)+length(col2)', 'v2'],  
        ]);
    以上代码将被转换为:
    select   
      `col_a`,`col_b`,`col_c`,  
      `col_name`,  
      `name` as `name_alias`,  
      count(*) as `cnt`,  
      sum(amount*price) as `v1`,  
      length(col1)+length(col2) as `v2`   
    from `table_name` as t0
    注意,如果是非函数模式格式,非标准名称字符([^/w]+)将被自动替换掉。
  • 针对 update 更新,参数为一个键、值对应的数据,其中键为字段名,值为需要更新的数据,也可以将这个数组拆开为多个参数。数据值可以为以下格式:
    • ['table_name', 'col_name'] 模式的数组,表示跨表更新
    • null 或 字符串‘null’,表示空值
    • ‘+n’‘-n’ ,其中 n 为数字,表示在现有基础上加减 n
    • ‘(表达式或数据库函数)’,直接执行对应的表达式或数据库函数
  • 针对 insert 添加,模式与 updateselect的普通模式 一样,如果插入数据包含数据表中的每一项,也可以不用field,直接通过values添加。
  • 参数为 'distinct''distinctrow' 表示为查询添加唯一限定,需要与其他 field 方法组合使用,如 $db->field('distinct')->field('name')。
  • 如果参数为 ‘[reset]’ ,则清空现有字段数据
  • 如果未提供参数,则返回解析好的目前所记录的字段信息。

本方法可连续多次使用,相关设置将被累加,可通过 ‘[reset]’ 关键字或 reset 方法复位。

values

为 insert 语句添加记录行,内容可以为数组,每项对应一行所需字段,可以为多参数,每参数对应一个指定字段。所对应的指定字段由 field方法 传入,如为update格式,则会将对应的值转换为一条value数据,如:

$db->build('table_name')->field(array(  
      'id' => 0,  
      'name' => 'name',  
      'file' => 'file',  
      'path' => '/',  
      'comment' => 'nothing'  
    ))  
    ->values(0,'name','file','/','nothing')  
    ->values(  
        [0,'name','file','/','nothing'],  
        [0,'name','file','/','nothing']  
    );

 以上代码将被解析为:

insert LOW_PRIORITY into `table_name`  
    (`id`,`name`,`file`,`path`,`comment`)  
values  
    ('0','name','file','/','nothing'),    #由 field 方法添加  
    ('0','name','file','/','nothing'),    #由第一种方式的 value 方法添加  
    ('0','name','file','/','nothing'),    #由第二种方式的 value 方法添加  
    ('0','name','file','/','nothing');    #由第二种方式的 value 方法添加

本方法可连续多次使用,相关设置将被累加,可通过 ‘[reset]’ 关键字或 reset 方法复位。

where

用于生成查询条件,包含如下模式:

  • $db->where($field, $condition, $value, $mode),本方法的通用模式,四个参数分别对应字段、条件、判断值和条件关系(and 或 or,默认为 and),其中条件参数后面将展开说明,如果 $field 为表达式或者算式等不需要解析的内容,则需要在内容外加上一个括号;如果 $value  不需要解析,则需要使用 'f' 系列的 $condition,如:
    $db->build('table_name')  
        ->where('idx', '>', 'a01')  
        ->where('left(x,3)','f>','right(y,3)')  
        ->where('(sign & 1)','n=',1);
    以上代码将被解析为:
    select * from `table_name` as t0 where   
      `idx` > 'a01' and   
      left(x,3) > right(y,3) and   
      (sign & 1) = 1
     
  • $db->where($expression),$expression可以为表达式;如果为字段名,则表示此字段不能为空;如果为函数或表达式,则需要用括号括起,如:
    $db->build('table_name')  
        ->where('image')  
        ->where('left(x,3)>right(y,3)')  
        ->where('(isnull(style))');
    以上代码将被解析为:
    select * from `table_name` as t0 where   
      `image` is not NULL and   
      left(x,3) > right(y,3) and   
      isnull(style)
    需要注意的是,如果最后一个 where('(isnull(style))') 不带括号而改为 where('isnull(style)') ,则会被解析为 `isnull(style)` is not NULL

  • $db->where($condition_array),用于符合条件的生成,由多级数组构成,每一级数组为一个条件组,最低阶格式为 [$field, $condition, $value, $mode] 的模式,每组条件最后一个值表示 条件关系(and 或 or),具体模式参考如下:
    $db->build('table_name')->where(  
      array(  
        array('no', 'n>', '1\' and no!=5 and name!=\''),  
        array(  
          array('tag', 'like', '1'),  
          array('tag', 'like', '2%', 'or'),  
          array('tag', 'like', '%3', 'or'),  
          'and'  
        ),  
        array(  
          array('name', '<>', 'xxxx'),  
          array('id', 'nin', '1, 2, a3, 5a, 5', 'or'),  
          'or'  
        ),  
        array('date', 'd>', array('now()', 'y-1'), 'or'),  
      ),  
    )
    其中,每个数组为一个组,每组第一项无需设置 条件关系,其他组如未设置的话,默认为 and ,可以通过多级子数组嵌套来生成复杂查询,其中里面用到了 n> , d>, nin 等条件,其意义将在下文讲解,以上条件将生成如下查询语句:
     select * from `table_name` as t0 where   
    (  
      `no` > 1   
      and (  
        `tag` like '%1%'   
        or `tag` like '2%'   
        or `tag` like '%3'  
      )   
      or (  
        `name` <> 'xxxx'   
        or `id` in (1,2,0,5,5)  
      )   
      or `date` > DATE_SUB(now(), INTERVAL 1 YEAR)  
    )
  • $db->where('[reset]'),将清空现有条件数据。
  • 如果未提供参数,则返回解析好的条件字符串。

本方法可连续多次使用,相关设置将被累加,可通过 ‘[reset]’ 关键字或 reset 方法复位。

order

用于指定排序依据,包含两个参数,参数一表示排序字段,参数二表示是否倒叙(布尔值),如:

$db->build('table_name')->order('id', true);  

将被翻译为:

select * from `table_name` as t0 order by `id` desc;

如果参数为 ‘[reset]’ ,则清空现有字段数据,参数也可以为 ‘rand()’ 等数据库方法。

如果未提供参数,则返回解析好的排序字符串。

本方法可连续多次使用,相关设置将被累加,可通过 ‘[reset]’ 关键字或 reset 方法复位。 

limit

用于指定结果集返回记录数量,可以有如下形式:

$db->build('table_name')->limit(5);  
$db->build('table_name')->limit(5, 10);  
$db->build('table_name')->limit('5, 10');

本方法保持数字安全,非数字字符将被忽略。 

group

用于指分组信息及相关取值条件,可以有如下形式:

$db->build('table_name')->field(['field_name', 'count(*) as cnt'])  
    ->group('field_name')  
    ->group(['field_name', 'cnt>3'])  
    ->group('field_name', ['cnt','n>',3])  
    ->group(['field'=>'field_name', 'having'=>'cnt>3']);

每次使用group方法将会自动覆盖之前所设置的参数,以上代码将被解析为:

select `field_name`,count(*) as `cnt`  
from `table_name` as t0  
group by `field_name` having `cnt` > 3;
select

依照以上各查询属性的设置,返回生成select语句,需要注意的是本方法仅针对生成器类 SQLBuilder 的单数据库。

insert

依照以上各查询属性的设置,返回生成insert语句,参数一默认为 false,如果设置为 true,将生成 MySQL 的replace语句。

update

依照以上各查询属性的设置,返回生成update语句,需要在 field 方法中按照对应的格式传入参数,本方法无参数。

delete

依照以上各查询属性的设置,返回生成delete语句,需预先设置条件(where),否则将清空数据库,无参数。

查询条件

在where方法中,基本条件判断的模式是 [比较值, 检测条件, 检测值] 的模式,规则如下:

  • 比较值:可为字段名、表达式或者数据库函数,如为比较复杂的非字段内容,建议在外层加上括号,以避免安全性检测将其中特殊字符去除。
  • 检测条件:及其对应说明如下:
    1 = , < , <= , > , >= ,
    <> , !=
    直接将比较值与检测值进行判断,检测值只做安全性处理
    2 b= 进行二进制模式的字符串比较,保证大小写一致
    3 n= , n< , n<= , n> , n>= ,
    n<> , n!=
    数字比较,检测值将被强制转换为数值
    4 d= , d< , d<= , d> , d>= ,
    d<> , d!=
    日期比较,检测值为一个两项数据,第一项为需比较的时间,第二项为时间差距格式为:时间代码+/-差距数值,其中时间代码为:y年,m月,d日,q季度,w星期,h小时,具体模式为:['now()', 'y-1'] 表示据当前时间1年以前,['date', 'd+1'] 表示据date字段的时间1天以后。
    注:本方法仅针对MySQL
    5 f= , f< , f<= , f> , f>= ,
    f<> , f!=
    本方法用于表达式或数据库函数比较,检测值将被视为表达式或数据库函数,不会进行字符串安全检测
    6 like, like binary, not like,
    not like binary
    字符串模糊查询,其中binary选项对大小写敏感,检测值如果首尾不带 % 等同于首尾均带有 %,也可以根据需要在某一边加上 %
    7 rlike, regexp, rlike binary,
    regexp binary, not rlike,
    not regexp, not rlike binary,
    not regexp binary
    基于正则表达式的字符串判断。
    注:本方法仅针对MySQL
    8 in, nin, not in, not nin 包含判断,也就是 field_name in ('V1', 'V2',....,'Vn') 的模式,带nin的条件表示相关取值范围均为数值。
    9 is, is not 判断检测值是否为 NULL 
    10 ! 表示比较值本身即为符合条件判断的表达式或者数据库函数,并将忽略检测条件和检测值
    11 空值NULL 如比较值外部有一对括号,则按照比较值本身即为符合条件判断的表达式或者数据库函数处理,否则将作为 is not null 的判断,本模式将忽略检测条件和检测值
  • 检测值:用于和比较值通过检测条件相比较的数值、字符串、表达式或数据库函数,如为表达式或数据库函数,建议在外层加上括号,以避免安全性检测将其中特殊字符去除。
    条件查询中除个别函数或表达式外,大部分值判断都会对检测值进行字符串安全性处理,去除可能造成注入的代码。

 

多表查询

与单表查询类似,多表查询通过数据库类实现,主要步骤如下:

数据库类添加base_sql 扩展,引入 build 方法,此方法的功能是,针对某一个表建立一个 SQLBuilder 类实例,如果已建立则直接返回已建立的实例,本方法包含两个参数,参数一为表名,参数二为关系表连接信息(参考 SQLBuilder 类构造函数相关说明)。本方法会自动为每个表设定别名,并传入本数据库类内定义的数据库关键字定界符($this->delimiter)。如果第一个参数为“[reset]”,则将清除所有已设置的表查询设置。

用户需要通过 $db->build('table_name') 所返回的查询构造类实例来设定相关查询参数,具体方法与单表查询一致。在多表关联查询时,扩展类多提供的方法可以直接生成连接查询语句,如对应数据库与MySQL语法差距较大,可依照扩展类的相关代码,重写对应方法,或者编写对应的转换方法(可参考 mssql.class.php 中的 convertSQLconvertLimit 两个方法)。

select 连接查询生成:

$db->build('tbl1')->field('col1, col2');  
$db->build('tbl1')  
    ->where('id', 'nin', '1, 2, a3, 5a, 5')  
    ->where('date', 'd>', array('now()', 'y-1'))  
    ->where('col1', '<>', 'xxx')  
    ->where('col2', 'f=', 'left(subject, 10)')  
    ->where('col3', 'like', 'xxx');  
$db->build('tbl2', array(  
    'mode' => 'left',  
    'field' => 'news_id'  
))  
    ->field(['field_name', 'count(*) as cnt'])  
    ->group(['field_name', 'cnt>3']);  
$db->build('tbl1')  
    ->where('col4')  
    ->where('(isnull(col5))')  
    ->where('id', 'n<', '10')  
    ->limit(5);

以上代码将被解析为:

select  
   `t0`.`col1`,`t0`.`col2`,  
   `t1`.`field_name`,count(*) as `cnt`  
from  
   `tbl1` as t0 left join `tbl2` as t1 using(`news_id`)  
where  
   `t0`.`id` in (1,2,0,5,5)  
   and `t0`.`date` > DATE_SUB(now(), INTERVAL 1 YEAR)  
   and `t0`.`col1` <> 'xxx'  
   and `t0`.`col2` = left(subject, 10)  
   and `t0`.`col3` like '%xxx%'  
   and `t0`.`col4` is not NULL  
   and isnull(t1.`col5`)  
   and `t1`.`id` < 10  
group by  
   t1.`field_name`  
having  
   t1.`cnt` > 3  
limit 5;

以上代码通过 $db->select() 直接执行,需入获取解析查询,可通过$db->select(true) 方式返回。

update 连接查询生成:

$db->build('tbl1')->field(array(  
    'views' => 5,  
    'tag' => 'tag',  
    'date'=>'(now())'  
))  
    ->where('news_id', 'n=', '1');  
$db->build('tbl2', array(  
    'mode' => 'left',  
    'field' => 'id',  
    'field_join' => 't0.news_id'  
))  
    ->field(array('date'=>['t0','date']))  
    ->where('page', 'n>=', '1');

以上代码将生成:

update  
   `tbl1` as t0  
left join  
   `tbl2` as t1 on t1.`id`=`t0`.`news_id`  
set  
   `t0`.`views`='5',  
   `t0`.`tag`='tag',  
   `t0`.`date`=now(),  
   `t1`.`date`=`t0`.`date`  
where  
   `t0`.`news_id` = 1  
   and `t1`.`page` >= 1;

以上代码通过 $db->update() 直接执行,需入获取解析查询,可通过$db->update(true) 方式返回。

insert 和 delete 的查询生成与以上示例类似,这里就不赘述了,可参考框架函数文档及示例栏目进一步了解。

由于数据库实例可能对应多个关系表,并生成组合查询语句,指定条件时必须要通过 $db->build('table_name') 的方法获取构造实例,如为第二各关联表,只需在第一次声明传入 join 相关参数即可,如无其他更改,第二次调用时只需传入表名即可。$db->build('table_name') 所返回的实例支持链式调用的模式,可以根据需要选择链式调用,或者通过 build 方法重新获取实例再传入对应参数的模式。

 

接口规范

为保证所有被代理数据库类都具有同样的基础结构,需通过自定义接口规范,如下:

<?PHP  
interface interface_db {  
    public function connect();            //数据路连接  
    public function selectDB($the_db);    //数据库选择  
    public function insert();             //数据插入  
    public function select();             //数据查询  
    public function update();             //数据更新  
    public function delete();             //数据删除  
    public function query($sql);          //执行语句  
    public function record($sql);         //返回单行  
    public function records($sql);        //返回所有行  
    public function result($sql);         //返回单值  
    public function getFields($the_tbl);  //获取字段列表  
    public function close();              //关闭数据库  
}  
   
interface interface_sql {  
    public function getRS();                     //取得下一条记录  
    public function getDBs();                    //获取数据库列表  
    public function getTbls();                   //获取数据表列表  
    public function getPri($the_tbl);            //获取主键  
    public function getInsertId();               //获取最新插入自增ID  
    public function getStat();                   //获取数据库基本信息  
    public function getCreateScript($the_tbl);   //获取数据表建立语句  
    public function getDataScript($the_tbl);     //获取数据插入语句  
    public function getIdxScript($the_tbl);      //获取索引建立语句  
    public function batchExe($SQLs);             //批量执行语句  
    public function handleSQL($strSQL);          //将多语句字符串分割为单语句数组  
    public function file($file);                 //执行多语句文件  
    public function check($obj);                 //检测数据库连接、结果集等对象是否存在  
}

注:扩展类中引用的方法适用于接口要求。

功能类声明方式如下:

class someDatabase extends myBase implements interface_db, interface_sql {  
    use base_db, base_sql;  
    ......  
}

 

上一篇:预定义变量
下一篇:错误处理