一花一世界,一沙一天国,君掌盛无边,刹那含永劫。

— 《天真的预兆》

1.20.1 前提

为了让大家更为明确数据库NotORM的使用,我们假设有以下数据库表:

  1. -- ----------------------------
  2. -- Table structure for `tbl_user`
  3. -- ----------------------------
  4. DROP TABLE IF EXISTS `tbl_user`;
  5. CREATE TABLE `tbl_user` (
  6. `id` int(11) NOT NULL,
  7. `name` varchar(45) DEFAULT NULL,
  8. `age` int(3) DEFAULT NULL,
  9. `note` varchar(45) DEFAULT NULL,
  10. `create_date` datetime DEFAULT NULL,
  11. PRIMARY KEY (`id`)
  12. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  13. -- ----------------------------
  14. -- Records of tbl_user
  15. -- ----------------------------
  16. INSERT INTO `tbl_user` VALUES ('1', 'dogstar', '18', 'oschina', '2015-12-01 09:42:31');
  17. INSERT INTO `tbl_user` VALUES ('2', 'Tom', '21', 'USA', '2015-12-08 09:42:38');
  18. INSERT INTO `tbl_user` VALUES ('3', 'King', '100', 'game', '2015-12-23 09:42:42');

并且,假设我们已获得了tbl_user表对应的notorm实例$user,此NotORM表实例可从两种方式获得:

  • 1、使用原生态的notorm,即:$user = DI()->notorm->user
  • 2、使用PhalApi_Model_NotORM基类的方式,即:$user = $this->getORM(),如:

    1. <?php
    2. class Model_User extends PhalApi_Model_NotORM {
    3. protected function getTableName($id) {
    4. return 'user';
    5. }
    6. public function doSth() {
    7. $user = $this->getORM(); //获取NotORM表实例
    8. }
    9. }

写在前面的话

NotORM的实例是有内部状态的,因为在开发过程中,需要特别注意何时需要保留状态(使用同一个实例)、何时不需要保留状态。即:

保留状态的写法:

  1. $user = $notorm->user; //获取一个新的实例
  2. $user->where('age > ?', 18);
  3. $user->where('name LIKE ?', '%dog%'); //相当于age > 18 AND name LIKE '%dog%'

不保留状态的写法:

  1. $user = $notorm->user; //获取一个新的实例
  2. $user->where('age > ?', 18);
  3. $user = $notorm->user; //重新获取新的实例
  4. $user->where('name LIKE ?', '%dog%'); //此时只有 name LIKE '%dog%'

关于这两者的使用场景,下面在进行说明时会特别提及。下面,就让我们结合实例来尝试一下数据库的操作吧!

调试模式

(1)打印SQL语句

在请求接口时,当需要查看当前接口所执行的SQL语句时,可以添加参数&sql=1。如请求:

  1. http://demo.phalapi.net/?service=User.getBaseInfo&user_id=1&__sql__=1

会返回类似这样:

  1. [1 - 0.00035s]SELECT * FROM tbl_user WHERE (id = ?); -- 1<br>
  2. {"ret":200,"data":{"code":0,"msg":"","info":{"id":"1","name":"dogstar","note":"oschina"}},"msg":""}

(2)显示数据库连接错误的详细信息

普通情况下,数据库连接失败时会这样提示:

  1. {
  2. "ret": 500,
  3. "data": [],
  4. "msg": "服务器运行错误: 数据库db_demo连接失败"
  5. }

考虑到生产环境不方便爆露服务器的相关信息,故这样简化提示。当在开发过程中,需要定位数据库连接失败的原因时,可使用debug调试模式。开启调试模式的方法有两种:

  • 1、在请求接口时添加 &debug=1,开启本次调试
  • 2、将配置文件 ./Config/sys.php中的debug设置为true,开启全局调试
    开启后,当再次失败时,会看到类似这样的提示:
  1. {
  2. "ret": 500,
  3. "data": [],
  4. "msg": "服务器运行错误: 数据库db_demo连接失败,异常码:1045,错误原因:SQLSTATE[28000] [1045] Access denied for user 'root'@'localhost' (using password: NO)"
  5. }

然后,便可根据具体的错误提示进行排查解决。

默认表名与指定表名

请注意:PhalApi v1.3.4 版本以上有效。

当继承PhalApi_Model_NotORM时,如编写Model_User类:

  1. <?php
  2. class Model_User extends PhalApi_Model_NotORM {
  3. }

则默认的表名为:user。默认表名的自动匹配规则是:取Model_后面部分的字符全部转小写。转化后框架会另外加上表前缀。

在以下场景下,需要重写此方法以指定表名:

  • 自动匹配的表名与实际表名不符
  • 存在分表
  • Model类名不含有Model_
    如,当Model_User类对应的表名为:my_user表时,可使用getTableName($id)重新指定表名:
  1. <?php
  2. class Model_User extends PhalApi_Model_NotORM {
  3. protected function getTableName($id) {
  4. return 'my_user';
  5. }
  6. }

1.20.2 基本操作

(1)SELECT

单个字段:

  1. // SELECT id FROM `tbl_user`
  2. $user->select('id')

多个字段获取:

  1. // SELECT id, name, age FROM `tbl_user`
  2. $user->select('id, name, age')

字段别名获取:

  1. // SELECT id, name, MAX(age) AS max_age FROM `tbl_user`
  2. $user->select('id, name, MAX(age) AS max_age')

全部字段(缺省)获取:

  1. // SELECT * FROM `tbl_user`
  2. $user->select('*')

(2)WHERE

单个字段查询:

  1. // WHERE id = 1
  2. $user->where('id', 1)
  3. $user->where('id = ?', 1)
  4. $user->where(array('id', 1))

多个字段查询:

  1. // WHERE id > 1 AND age > 18
  2. $user->where('id > ?', 1)->where('age > ?', 18)
  3. $user->and('id > ?', 1)->and('age > ?', 18)
  4. $user->where('id > ? AND age > ?', 1, 18)
  5. $user->where(array('id > ?' => 1, 'age > ?' => 10))
  6. // WHERE name = 'dogstar' AND age = 18
  7. $user->where(array('name' => 'dogstar', 'age' => 18))
  8. // WHERE name = 'dogstar' OR age = 18
  9. $user->or('name', 'dogstar')->or('age', 18)
  10. // WHERE ((name = ? OR id = ?)) AND (note = ?) -- 'dogstar', '1', 'xxx'
  11. #实现方式1:使用and拼接
  12. $user->where('(name = ? OR id = ?)', 'dogstar', '1')->and('note = ?', 'xxx')
  13. #实现方式2:只使用where,不用数组传参
  14. $user->where('(name = ? OR id = ?) AND note = ?', 'dogstar', '1', 'xxx')
  15. #实现方式3:只使用where,用下标为顺序数字的数组传参
  16. $user->where('(name = ? OR id = ?) AND note = ?', array('dogstar', '1', 'xxx'))
  17. #实现方式4:只使用where,用下标为标识符的数组传参
  18. $user->where('(name = :name OR id = :id) AND note = :note', array(':name' => 'dogstar', ':id' => '1', ':note' => 'xxx'))

IN查询:

  1. // WHERE id IN (1, 2, 3)
  2. $user->where('id', array(1, 2, 3))
  3. // WHERE id NOT IN (1, 2, 3)
  4. $user->where('NOT id', array(1, 2, 3))
  5. // WHERE (id, age) IN ((1, 18), (2, 20))
  6. $user->where('(id, age)', array(array(1, 18), array(2, 20)))

模糊匹配查询:

  1. // WHERE name LIKE '%dog%'
  2. $user->where('name LIKE ?', '%dog%')
  3. // WHERE name NOT LIKE '%dog%'
  4. $user->where('name NOT LIKE ?', '%dog%')

NULL判断查询:

  1. // WHERE (name IS NULL)
  2. $user->where('name', null)

对于非空的判断,则可以:

  1. // WHERE (name IS NOT ?) LIMIT 1; -- NULL
  2. $user->where('name IS NOT ?', null)

(3)ORDER BY

单个字段排序:

  1. // ORDER BY age
  2. $user->order('age')
  3. // ORDER BY age DESC
  4. $user->order('age DESC')

多个字段排序:

  1. // ORDER BY id, age DESC
  2. $user->order('id')->order('age DESC')
  3. $user->order('id, age DESC')

(4)LIMIT

按数量限制:

  1. // LIMIT 10
  2. $user->limit(10)

按数量和偏移量限制(请注意:先偏移量、再数量):

  1. // LIMIT 2,10
  2. $user->limit(2, 10)

(5)GROUP BY和HAVING

不带HAVING:

  1. // GROUP BY note
  2. $user->group('note')

带HAVING:

  1. // GROUP BY note HAVING age > 10
  2. $user->group('note', 'age > 10')

1.20.3 CURD之查询类(Retrieve)

操作 说明 示例 备注 是否PhalApi新增
fetch() 循环获取每一行 while($row = $user->fetch()) { //… }
fetchOne() 只获取第一行 $row = $user->where('id', 1)->fetchOne(); 等效于fetchRow()
fetchRow() 只获取第一行 $row = $user->where('id', 1)->fetchRow(); 等效于fetchOne()
fetchPairs() 获取键值对 $row = $user->fetchPairs('id', 'name'); 第二个参数为空时,可取多个值,并且多条纪录
fetchAll() 获取全部的行 $rows = $user->where('id', array(1, 2, 3))->fetchAll(); 等效于fetchRows()
fetchRows() 获取全部的行 $rows = $user->where('id', array(1, 2, 3))->fetchRows(); 等效于fetchAll()
queryAll() 复杂查询下获取全部的行,默认下以主键为下标 $rows = $user->queryAll($sql, $parmas); 等效于queryRows()
queryRows() 复杂查询下获取全部的行,默认下以主键为下标 $rows = $user->queryRows($sql, $parmas); 等效于queryAll()
count() 查询总数 $total = $user->count('id'); 第一参数可省略
min() 取最小值 $minId = $user->min('id');
max() 取最大值 $maxId = $user->max('id');
sum() 计算总和 $sum = $user->sum('age');

循环获取每一行(多个字段):

  1. // SELECT id, name FROM tbl_user WHERE (age > 18);
  2. $user = $notorm->user->select('id, name')->where('age > 18');
  3. while($row = $user->fetch()) {
  4. var_dump($row);
  5. }
  6. // 输出
  7. array(2) {
  8. ["id"]=>
  9. string(1) "2"
  10. ["name"]=>
  11. string(3) "Tom"
  12. }
  13. array(2) {
  14. ["id"]=>
  15. string(1) "3"
  16. ["name"]=>
  17. string(4) "King"
  18. }

循环获取每一行(单个字段):

  1. // SELECT id, name FROM tbl_user WHERE (age > 18);
  2. $user = $notorm->user->select('id, name')->where('age > 18');
  3. while($row = $user->fetch('name')) { // 指定获取name这列,但此字段须在select里
  4. var_dump($row);
  5. }
  6. // 输出
  7. string(3) "Tom"
  8. string(4) "King"

循环获取每一行(错误的用法,注意!会死循环):

  1. while($row = $notorm->user->select('id, name')->where('age > 18')->fetch('name')) {
  2. var_dump($row);
  3. }

只获取第一行(多个字段):

  1. // SELECT id, name FROM tbl_user WHERE (age > 18) LIMIT 1;
  2. $rs = $user->select('id, name')->where('age > 18')->fetchOne(); //等同fetchRow()
  3. var_dump($rs);
  4. // 输出
  5. array(2) {
  6. ["id"]=>
  7. string(1) "2"
  8. ["name"]=>
  9. string(3) "Tom"
  10. }

只获取第一行(单个字段):

  1. var_dump($user->fetchOne('name')); // 输出 string(3) "Tom"
  2. var_dump($user->fetchRow('name')); // 等同输出 string(3) "Tom"

获取键值对(多个字段):

  1. // SELECT id, name, age FROM tbl_user LIMIT 2;
  2. $rs = $user->select('name, age')->limit(2)->fetchPairs('id'); //指定以ID为KEY
  3. var_dump($rs);
  4. // 输出
  5. array(2) {
  6. [1]=>
  7. array(3) {
  8. ["id"]=>
  9. string(1) "1"
  10. ["name"]=>
  11. string(7) "dogstar"
  12. ["age"]=>
  13. string(2) "18"
  14. }
  15. [2]=>
  16. array(3) {
  17. ["id"]=>
  18. string(1) "2"
  19. ["name"]=>
  20. string(3) "Tom"
  21. ["age"]=>
  22. string(2) "21"
  23. }
  24. }

获取键值对(单个字段):

  1. // SELECT id, name FROM tbl_user LIMIT 2
  2. var_dump($user->limit(2)->fetchPairs('id', 'name')); //通过第二个参数,指定VALUE的列
  3. // 输出
  4. array(2) {
  5. [1]=>
  6. string(7) "dogstar"
  7. [2]=>
  8. string(3) "Tom"
  9. }

获取全部的行:

  1. // SELECT * FROM tbl_user
  2. var_dump($user->fetchAll()); //全部表数据输出,输出结果略,相当于$user->fetchRows()

复杂查询下获取全部的行(1.3.1及以前版本默认下以主键为下标,1.3.1以后的版本则默认采用数组方式):

  1. // SELECT name FROM tbl_user WHERE age > :age LIMIT 1
  2. $sql = 'SELECT name FROM tbl_user WHERE age > :age LIMIT 1';
  3. $params = array(':age' => 18);
  4. $rs = $user->queryAll($sql, $params);
  5. var_dump($rs);
  6. // 输出
  7. array(1) {
  8. [0]=>
  9. array(1) {
  10. ["name"]=>
  11. string(3) "Tom"
  12. }
  13. }
  14. // 也可以这样。。。
  15. $sql = 'SELECT name FROM tbl_user WHERE age > ? LIMIT 1'; //使用问号表示变量
  16. $params = array(18);
  17. $rs = $user->queryRows($sql, $params); //使用queryRows()别名
  18. var_dump($rs);

请注意:使用上面这种方式进行查询,需要手动填写完整的表名字,并且需要通过某个表的实例来运行。

取最小值:

  1. // SELECT MIN(age) FROM tbl_user
  2. var_dump($user->min('age'));
  3. // 输出
  4. string(2) "18"

取最大值:

  1. // SELECT MAX(age) FROM tbl_user
  2. var_dump($user->max('age'));
  3. // 输出
  4. string(3) "100"

计算总和:

  1. // SELECT SUM(age) FROM tbl_user
  2. var_dump($user->sum('age'));
  3. // 输出
  4. string(3) "139"

1.20.4 CURD之插入类(Create)

操作 说明 示例 备注 是否PhalApi新增
insert() 插入数据 $user->insert($data); 原生态操作需要再调用insert_id()获取插入的ID
insert_multi() 批量插入 $user->insert_multi($rows); 可批量插入
insert_update() 插入/更新 接口签名:insert_update(array $unique, array $insert, array $update = array() 不存时插入,存在时更新

插入数据

  1. // INSERT INTO tbl_user (id, name, age, note) VALUES (4, 'PhalApi', 1, 'framework')
  2. $data = array('id' => 4, 'name' => 'PhalApi', 'age' => 1, 'note' => 'framework');
  3. $user->insert($data);
  4. $id = $user->insert_id(); //必须是同一个实例,方能获取到新插入的行ID,且表必须设置了自增
  5. var_dump($id); //新增的ID
  6. //使用Model的写法
  7. $model = new Model_User();
  8. var_dump($model->insert($data)); //输出新增的ID

批量插入:

  1. // INSERT INTO tbl_user (name, age, note) VALUES ('A君', 12, 'AA'), ('B君', 14, 'BB'), ('C君', 16, 'CC')
  2. $rows = array(
  3. array('name' => 'A君', 'age' => 12, 'note' => 'AA'),
  4. array('name' => 'B君', 'age' => 14, 'note' => 'BB'),
  5. array('name' => 'C君', 'age' => 16, 'note' => 'CC'),
  6. );
  7. $rs = $user->insert_multi($rows);
  8. var_dump($rs);
  9. // 输出
  10. int(3) //成功插入的条数

插入/更新:

  1. // INSERT INTO tbl_user (id, name, age, note) VALUES (8, 'PhalApi', 1, 'framework') ON DUPLICATE KEY UPDATE age = 2
  2. $unique = array('id' => 8);
  3. $insert = array('id' => 8, 'name' => 'PhalApi', 'age' => 1, 'note' => 'framework');
  4. $update = array('age' => 2);
  5. $rs = $user->insert_update($unique, $insert, $update);
  6. var_dump($rs); //输出影响的行数

1.20.5 CURD之更新类(Update)

操作 说明 示例 备注 是否PhalApi新增
update() 更新数据 $user->where('id', 1)->update($data); 更新异常时返回false,数据无变化时返回0,成功更新返回1

更新数据:

  1. // UPDATE tbl_user SET age = 2 WHERE (name = 'PhalApi');
  2. $data = array('age' => 2);
  3. $rs = $user->where('name', 'PhalApi')->update($data);
  4. var_dump($rs);
  5. // 输出
  6. int(1) //正常影响的行数
  7. int(0) //无更新,或者数据没变化
  8. boolean(false) //更新异常、失败

更新数据(+1):

  1. // UPDATE tbl_user SET age = age + 1 WHERE (name = 'PhalApi')
  2. $rs = $user->where('name', 'PhalApi')->update(array('age' => new NotORM_Literal("age + 1")));
  3. var_dump($rs); //输出影响的行数

1.20.6 CURD之删除类(Delete)

操作 说明 示例 备注 是否PhalApi新增
delete() 删除 $user->where('id', 1)->delete(); 禁止无where条件的删除操作

按条件删除,返回影响的行数:

  1. // DELETE FROM tbl_user WHERE (id = 404);
  2. $user->where('id', 404)->delete();

注意: 如果是全表删除,框架将会禁止,并抛出异常。如:

  1. // Exception: sorry, you can not delete the whole table
  2. $user->delete();

参考

更多请参考 NotORM官网接口说明

原文: https://www.phalapi.net/wikis/1-20.html