#phalapi-入門篇5(數據庫操作和Model層)#

##前言##
***先在這里感謝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")會得到以下結果

###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")會得到以下結果

為什么會有這樣的區別,通過下面的一些小提示大家就能看到區別在哪里:
####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語句就能很快的確定問題出現在哪里
大家可以試試在請求參數中加上 就可以打印出來生成的sql語句方便調試

包括執行時間和先后順序也一同打印出來了,也可以幫助大家找到慢查詢在哪里
##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);
}
可以獲得以下結果

在這里phalapi自帶的model和傳統的model對比起來區別在于,phalapi統一制定表名不會應為方法果斷導致的表名寫錯的失誤
另一方面phalapi自帶的model提供了很多字基礎操作,利用自動提示功能可以看到

我們來再次改造一下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)
- 空白目錄
- [7.1]-phalapi-入門篇1(簡單介紹以及環境搭建)
- [7.2]-phalapi-入門篇2(把它玩起來)
- [7.3]-phalapi-入門篇3(請求和返回)
- [7.4]-phalapi-入門篇4(國際化高可用和自動生成文檔)
- [7.5]-phalapi-入門篇5(數據庫操作和Model層)
- [7.6]-phalapi-入門篇6(小技巧和淺談API適用范圍以及入門篇總結)
- [7.7]-phalapi-進階篇1(三層結構Api,Domain,和Model)
- [7.8]-phalapi-進階篇2(DI依賴注入和單例模式)
- [7.9]-phalapi-進階篇3(自動加載和攔截器)
- [7.10]-phalapi-進階篇4(notorm進階以及事務操作)
- [7.11]-phalapi-進階篇5(數據庫讀寫分離)
- [7.12]-phalapi-進階篇6(解決大量數據存儲數據庫分表分庫拓展)
- [7.13]-phalapi-進階篇7(使用緩存以及用redis拓展解決實際問題)
- [7.14]-phalapi-進階篇8(PhalApi能帶來什么和進階篇總結)