💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
#phalapi-入门篇5(数据库操作和Model层)# ![](http://webtools.qiniudn.com/master-LOGO-20150410_50.jpg) ##前言## ***先在这里感谢phalapi框架创始人@dogstar,为我们提供了这样一个优秀的开源框架.*** 本小节主要讲解基于notorm的数据库操作以及使用Model层进行快速的数据层的开发,请确保装有PDO拓展. 附上: 喵了个咪的博客:[w-blog.cn](w-blog.cn) 官网地址:[http://www.phalapi.net/](http://www.phalapi.net/ "PhalApi官网") 开源中国Git地址:[http://git.oschina.net/dogstar/PhalApi/tree/release](http://git.oschina.net/dogstar/PhalApi/tree/release "开源中国Git地址") ##1. 基于PDO的notorm进行的数据库操作## phalapi的数据库操作是使用的开源的notorm进行的,notorm是基于PDO链接数据库,在框架内部默认链接的是mysql数据库,如需修改**链接其他数据库**请修改 **/PhalApi/PhalApi/DB/NotORM.php**中的**getPdo**方法: $dsn = sprintf('mysql:dbname=%s;host=%s;port=%d', $dbCfg['name'], isset($dbCfg['host']) ? $dbCfg['host'] : 'localhost', isset($dbCfg['port']) ? $dbCfg['port'] : 3306 ); 再讲之前其实这里是有一个坑的,机智的童鞋应该发现了**框架自带的user数据库里面有一个以from命名字段,**应为在notorm**生成sql语句的时候不会给自动自动加上引号** ,所以在修改添加删除有涉及这个字段的时候会报错,所以我们在这里把它**改成phone**(所以大家要注意字段名不能为关键字) 下面我们正式来讲解如何使用,我们先在Demo/Api下面创建一个DB.php文件作为我们的DB模块, <?php /** * 数据库接口服务类 */ class Api_DB extends PhalApi_Api{ public function getRules(){ return array( 'insert' => array( 'id' => array('name' => 'id', 'require' => true, 'desc' => '用户Id'), 'name' => array('name' => 'name', 'require' => true, 'desc' => '用户名称'), 'phone' => array('name' => 'phone', 'require' => true, 'desc' => '用户手机号码'), ), 'select' => array( 'id' => array('name' => 'id', 'require' => true, 'desc' => '用户Id'), ), 'update' => array( 'id' => array('name' => 'id', 'require' => true, 'desc' => '用户Id'), 'name' => array('name' => 'name', 'require' => true, 'desc' => '用户名称'), 'phone' => array('name' => 'phone', 'require' => true, 'desc' => '用户手机号码'), ), 'delete' => array( 'id' => array('name' => 'id', 'require' => true, 'desc' => '用户Id'), ), ); } 一共是增删改查四个接口代表四种操作(这里一定要配置好数据库,以及运行框架自带的sql文件**phalapi_test.sql**) ###1.1 insert接口### 我们先写增加接口如下: /** * 新增表服务 * @return int id 新增列的Id */ public function insert(){ $data = array( //用数组构成需要插入键值一一对应 'id' => $this->id, 'name' => $this->name, 'phone' => $this->phone, ); $rs = DI()->notorm->user->insert($data); //执行数据库操作user代表的是表,返回结果是插入成功的值 return $rs['id']; //返回插入的id } 重要的是 $rs = DI()->notorm->user->insert($data); 这段代码执行了sql语句,**user是表名**(这里的表名会加下在dbs中配置的表前缀组成一个完整的表名)我们试着运行一下[http://localhost/Public/?service=DB.insert&name=miaomi&phone=13010001000&id=2](http://localhost/Public/?service=DB.insert&name=miaomi&phone=13010001000&id=2 "http://localhost/Public/?service=DB.insert&name=miaomi&phone=13010001000&id=2")会得到以下结果 ![](http://i.imgur.com/V2L6Q3u.png) ###1.2 select接口### 查询接口如下: /** * 查询 * @return array data 结果集 */ public function select(){ $data = array(); $data[] = DI()->notorm->user->select('name,phone')->where('id', $this->id)->fetch(); $data[] = DI()->notorm->user->select('name,phone')->where('id = ?', $this->id)->fetchAll(); $data[] = DI()->notorm->user->select('name,phone')->where('id != ?', $this->id)->fetchRows(); return $data; } 执行[http://localhost/Public/?service=DB.select&id=2](http://localhost/Public/?service=DB.select&id=2 "http://localhost/Public/?service=DB.select&id=2")会得到以下结果 ![](http://i.imgur.com/cI8kBXr.png) 为什么会有这样的区别,通过下面的一些小提示大家就能看到区别在哪里: ####1.2.1 select方法#### select方法主要是用来指定返回值,接受的是一个string他的作用于真正查询语句select和from之间填充,大家如果把**select('name,phone')** 改为 select('*') 就会得到包括id的所有字段的返回 ####1.2.2 where方法和排序#### where方法是查询中的重要的一个环节 **where('id', $this->id)**等同于**where('id = ?', $this->id)** **where('id != ?', $this->id)**这种方式只要是为了指定条件**大于,等于,小于,不等于**等 当然如果是需要有多个条件就使用连续的where就可以**->where('id != ?',1)->where('phone','1301000100')**这种形式 关于排序的使用其实和where差不多使用**->order('字段名')**如果要反排序在字段名后面加上DESC ####1.2.3 fetch,fetchAll和fetchRows#### 大家有看到上面执行的三条查询语句后面的结束放到都不同这里讲解一下他们的区别和怎么用他们使用单独去执行sql语句 fetch方法是获取单独的一条数据**返回结果是不带下标的数组** ,fetchAll和fetchRows不同在于他们**返回的是包含多条数据一个带下标的数组**,可以看到在条件一样的情况下第一条和第二条查询出来的结果区别是第二条多了一个0的下标,从此可得到如果是确定返回结果只有一条优先使用fetch,如果是多条结果优先使用fetchAll和fetchRows. fetchAll和fetchRows还提供了一个功能就是**单独执行sql语句** $sql = 'select * from tbl_user where id = :id'; $params = array(':id' => $this->id); //替换:id为请求参数的id DI()->notorm->user->queryAll($sql, $params); //或fetchRows($sql, $params) 这样就可以执行sql语句,包括一些复杂的查询sql可以使用此内方法执行(**关联查询应当优先使用这种形式**) ###1.3 update接口### 修改接口如下: /** * 修改 */ public function update(){ $data = array( 'name' => $this->name, 'phone' => $this->phone, ); $rs = DI()->notorm->user->where('id', $this->id)->update($data); if($rs === false){ throw new PhalApi_Exception_BadRequest('修改数据失败'); } } 大家可以试一试执行之后是否有修改数据库[http://localhost/Public/?service=DB.update&id=2&phone=13011112222&name=wenwenwen](http://localhost/Public/?service=DB.update&id=2&phone=13011112222&name=wenwenwen "http://localhost/Public/?service=DB.update&id=2&phone=13011112222&name=wenwenwen") 使用其实和添加接口差不多只是一个是吧id作为值,一个是作为条件 比较值得讲一下的是为什么使用**if($rs === false)** 原因是这样的,这里执行update方法之后获取得是影响行数,如果原本值就是一样的那就回返回0,只有在真正语句失败的时候会返回false所以这里使用全等于false作为判断是否执行成功的条件 ###1.4 delete接口### 删除接口如下: /** * 删除 */ public function delete(){ $rs = DI()->notorm->user->where('id', $this->id)->delete(); if($rs === false){ throw new PhalApi_Exception_BadRequest('删除数据失败'); } } [http://localhost/Public/?service=DB.delete&id=2](http://localhost/Public/?service=DB.delete&id=2 "http://localhost/Public/?service=DB.delete&id=2") 删除的操作也很简单,不过**if($rs === false)**就算没有删除到数据也会返回成功,只有当语句失败会反悔false,如果需要未删除到数据提示出错的同学可以把等号减少一个 ###1.5 打印sql语句### 有的时候光靠自己去看代码很难确定是不是哪里写的有问题,但是如果查看生成出来的sql语句就能很快的确定问题出现在哪里 大家可以试试在请求参数中加上![](http://i.imgur.com/o4ah1NW.png) 就可以打印出来生成的sql语句方便调试 ![](http://i.imgur.com/jEtQJWb.png) 包括执行时间和先后顺序也一同打印出来了,也可以帮助大家找到慢查询在哪里 ##2. 使用Model进行数据库操作## 使用Model操作是为了提高开发效率,让同样数据库操作可以进行高度的复用,也便于修改起来改一处则全改这种效果 ###2.1 传统的Model操作### 所谓传统的Model操作也就是把数据操作封装起来,方便调用比如**/Model/User.php**下面的getByUserId方法 public function getByUserId($userId) { return DI()->notorm->user->select('*')->where('id = ?', $userId)->fetch(); } 在内部直接封装数据库操作使用如下代码调用 $model = new Model_User(); $rs = $model->getByUserId($userId); ###2.2 框架自带的Model操作### 当然这里介绍model的目的当然是解读一下phalapi内部提供的model操作 使用自带model操作只需要继承**PhalApi_Model_NotORM **在实现如下方法 protected function getTableName($id) { return 'user'; } 这个方法主要作用是为了添加这个model 的表名,其实这两个操作在**/Model/User.php**中已经实现了,我们来重构一下getByUserId方法如下 public function getByUserId($userId) { return $this->getORM()->select('*')->where('id = ?', $userId)->fetch(); } **$this->getORM()**相当于**DI()->notorm->**(getTableName中设置的表名) 然后我们重写select接口如下: public function select(){ $model = new Model_User(); return $model->getByUserId($this->id); } 可以获得以下结果 ![](http://i.imgur.com/O8CH0P0.png) 在这里phalapi自带的model和传统的model对比起来区别在于,phalapi统一制定表名不会应为方法果断导致的表名写错的失误 另一方面phalapi自带的model提供了很多字基础操作,利用自动提示功能可以看到 ![](http://i.imgur.com/7gc9Val.png) 我们来再次改造一下selete接口使用model自带的方法 public function select(){ $model = new Model_User(); return $model->get($this->id); } 执行结果和上面是一样的,这里注意一点这里Id的名字是dbs中配置的**'key' => 'id',**要和数据库中的ID字段名对应,但是这样会有一些问题会在后面进阶篇提及到 ##3. 总结## 在本小节着重讲了CURD操作,以及其中的一些操作的使用和怎么使用phalapi的model层,希望大家看完本小节之后进行一些练习来熟练的掌握使用phalapi对数据库的操作,关于数据库操作的一些小技巧会单独在进阶篇中抽出一小节来讲讲在实际项目开发中遇到的问题以及如何解决,希望大家进一步关注! 注:笔者能力有限有说的不对的地方希望大家能够指出,也希望多多交流! **官网QQ交流群:421032344 欢迎大家的加入!** ####[上一章](/wikis/%5b7.4%5d-phalapi-%e5%85%a5%e9%97%a8%e7%af%874(%e5%9b%bd%e9%99%85%e5%8c%96%e9%ab%98%e5%8f%af%e7%94%a8%e5%92%8c%e8%87%aa%e5%8a%a8%e7%94%9f%e6%88%90%e6%96%87%e6%a1%a3).html) [文档首页](/wikis/) [下一章](/wikis/%5b7.6%5d-phalapi-%e5%85%a5%e9%97%a8%e7%af%876(%e5%b0%8f%e6%8a%80%e5%b7%a7%e5%92%8c%e6%b5%85%e8%b0%88API%e9%80%82%e7%94%a8%e8%8c%83%e5%9b%b4%e4%bb%a5%e5%8f%8a%e5%85%a5%e9%97%a8%e7%af%87%e6%80%bb%e7%bb%93).html)