Model層稱(chēng)為數(shù)據(jù)模型層,負(fù)責(zé)技術(shù)層面上對(duì)數(shù)據(jù)信息的提取、存儲(chǔ)、更新和刪除等操作,數(shù)據(jù)可來(lái)自?xún)?nèi)存,也可以來(lái)自持久化存儲(chǔ)媒介,甚至可以是來(lái)自外部第三方系統(tǒng)。雖然PhalApi的Model層是廣義上的數(shù)據(jù)層,但考慮到大部分?jǐn)?shù)據(jù)都是來(lái)自于數(shù)據(jù)庫(kù)的操作,所以這一章將重點(diǎn)講述如何進(jìn)行數(shù)據(jù)庫(kù)操作。
數(shù)據(jù)庫(kù)的配置文件為./config/dbs.php,默認(rèn)使用的是MySQL數(shù)據(jù)庫(kù),所以需要配置MySQL的連接信息。servers選項(xiàng)用于配置數(shù)據(jù)庫(kù)服務(wù)器相關(guān)信息,可以配置多組數(shù)據(jù)庫(kù)實(shí)例,每組包括數(shù)據(jù)庫(kù)的賬號(hào)、密碼、數(shù)據(jù)庫(kù)名字等信息。不同的數(shù)據(jù)庫(kù)實(shí)例,使用不同標(biāo)識(shí)作為下標(biāo)?! ?/p>
servers數(shù)據(jù)庫(kù)配置項(xiàng) | 說(shuō)明 |
---|---|
host | 數(shù)據(jù)庫(kù)域名 |
name | 數(shù)據(jù)庫(kù)名字 |
user | 數(shù)據(jù)庫(kù)用戶(hù)名 |
password | 數(shù)據(jù)庫(kù)密碼 |
port | 數(shù)據(jù)庫(kù)端口 |
charset | 數(shù)據(jù)庫(kù)字符集 |
tables選項(xiàng)用于配置數(shù)據(jù)庫(kù)表的表前綴、主鍵字段和路由映射關(guān)系,可以配置多個(gè)表,下標(biāo)為不帶表前綴的表名,其中__default__
下標(biāo)選項(xiàng)為缺省的數(shù)據(jù)庫(kù)路由,即未配置的數(shù)據(jù)庫(kù)表將使用這一份默認(rèn)配置。
表2-12 表配置項(xiàng)
tables表配置項(xiàng) | 說(shuō)明 |
---|---|
prefix | 表前綴 |
key | 表主鍵 |
map | 數(shù)據(jù)庫(kù)實(shí)例映射關(guān)系,可配置多組。每組格式為:array('db' => 服務(wù)器標(biāo)識(shí), 'start' => 開(kāi)始分表標(biāo)識(shí), 'end' => 結(jié)束分表標(biāo)識(shí)) ,start和end要么都不提供,要么都提供 |
例如默認(rèn)數(shù)據(jù)庫(kù)配置為:
return array(
/**
* DB數(shù)據(jù)庫(kù)服務(wù)器集群
*/
'servers' => array(
'db_master' => array( //服務(wù)器標(biāo)記
'host' => '127.0.0.1', //數(shù)據(jù)庫(kù)域名
'name' => 'phalapi', //數(shù)據(jù)庫(kù)名字
'user' => 'root', //數(shù)據(jù)庫(kù)用戶(hù)名
'password' => '', //數(shù)據(jù)庫(kù)密碼
'port' => 3306, //數(shù)據(jù)庫(kù)端口
'charset' => 'UTF8', //數(shù)據(jù)庫(kù)字符集
),
),
/**
* 自定義路由表
*/
'tables' => array(
//通用路由
'__default__' => array(
'prefix' => 'tbl_',
'key' => 'id',
'map' => array(
array('db' => 'db_master'),
),
),
),
);
其中,在servers中配置了名稱(chēng)為dbmaster數(shù)據(jù)庫(kù)實(shí)例,意為數(shù)據(jù)庫(kù)主庫(kù),其host為localhost,名稱(chēng)為phalapi,用戶(hù)名為root等。在tables中,只配置了通用路由,并且表前綴為tbl,主鍵均為id,并且全部使用db_demo數(shù)據(jù)庫(kù)實(shí)例。
溫馨提示:當(dāng)tables中配置的db數(shù)據(jù)庫(kù)實(shí)例不存在servers中時(shí),將會(huì)提示數(shù)據(jù)庫(kù)配置錯(cuò)誤。
普通情況下,數(shù)據(jù)庫(kù)連接失敗時(shí)會(huì)這樣提示:
{
"ret": 500,
"data": [],
"msg": "服務(wù)器運(yùn)行錯(cuò)誤: 數(shù)據(jù)庫(kù)db_demo連接失敗"
}
考慮到生產(chǎn)環(huán)境不方便爆露服務(wù)器的相關(guān)信息,故這樣簡(jiǎn)化提示。當(dāng)在開(kāi)發(fā)過(guò)程中,需要定位數(shù)據(jù)庫(kù)連接失敗的原因時(shí),可使用debug調(diào)試模式。開(kāi)啟調(diào)試后,當(dāng)再次失敗時(shí),會(huì)看到類(lèi)似這樣的提示:
{
"ret": 500,
"data": [],
"msg": "服務(wù)器運(yùn)行錯(cuò)誤: 數(shù)據(jù)庫(kù)db_demo連接失敗,異常碼:1045,錯(cuò)誤原因:SQLSTATE[28000] [1045] ... ..."
}
然后,便可根據(jù)具體的錯(cuò)誤提示進(jìn)行排查解決。
NotORM是一個(gè)優(yōu)秀的開(kāi)源PHP類(lèi)庫(kù),可用于操作數(shù)據(jù)庫(kù)。PhalApi的數(shù)據(jù)庫(kù)操作,主要是依賴(lài)此NotORM來(lái)完成。
參考:NotORM官網(wǎng):www.notorm.com。
所以,如果了解NotORM的使用,自然而然對(duì)PhalApi中的數(shù)據(jù)庫(kù)操作也就一目了然了。但為了更符合接口類(lèi)項(xiàng)目的開(kāi)發(fā),PhalApi對(duì)NotORM的底層進(jìn)行優(yōu)化和調(diào)整。以下改動(dòng)點(diǎn)包括但不限于:
NotORM_Result::fetchAll()
和NotORM_Result::fetchRows()
NotORM_Result::queryAll()
和NotORM_Result::queryRows()
在PhalApi中獲取NotORM實(shí)例,有兩種方式:全局獲取方式、局部獲取方式。
\PhalApi\DI()->notorm
$this->getORM()
第一種全局獲取的方式,可以用于任何地方,這是因?yàn)槲覀円呀?jīng)在初始化文件中注冊(cè)了\PhalApi\DI()->notorm
這一服務(wù)。
第二種局部獲取的方式,僅限用于繼承PhalApi\Model\NotORMModel的子類(lèi)中。首先需要實(shí)現(xiàn)相應(yīng)的Model子類(lèi),通常一個(gè)表對(duì)應(yīng)一個(gè)Model子類(lèi)。例如為user表創(chuàng)建相應(yīng)的Model類(lèi)。
PhalApi推薦使用封裝的第二種方式,并且下面所介紹的使用都是基于第二種快速方式。以下是獲取一個(gè)NotORM實(shí)例的示例。
class User extends NotORM {
public function doSth() {
$orm = $this->getORM();
}
}
通常情況下,一張表對(duì)應(yīng)一個(gè)Model類(lèi)。當(dāng)需要新增時(shí),可以繼承于PhalApi\Model\NotORMModel類(lèi),并放置在A(yíng)pp\Model命名空間下。例如,對(duì)于數(shù)據(jù)庫(kù)表tbl_user:
CREATE TABLE `tbl_user` (
`id` int(11) NOT NULL,
`name` varchar(45) DEFAULT NULL,
`age` int(3) DEFAULT NULL,
`note` varchar(45) DEFAULT NULL,
`create_date` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
可以新增App\Model\User.php文件,并在里面放置以下代碼。
<?php
namespace App\Model;
use PhalApi\Model\NotORMModel as NotORM;
class User extends NotORM {
}
至此,便可得到一個(gè)基本的Model子類(lèi)了。
對(duì)于基本的Model子類(lèi),可以得到基本的數(shù)據(jù)庫(kù)操作。以下示例演示了Model的基本CURD操作。
$model = new App\Model\User();
// 查詢(xún)
$row = $model->get(1);
$row = $model->get(1, 'id, name'); //取指定的字段
$row = $model->get(1, array('id', 'name')); //可以數(shù)組取指定要獲取的字段
// 更新
$data = array('name' => 'test', 'update_time' => time());
$model->update(1, $data); //基于主鍵的快速更新
// 插入
$data = array('name' => 'phalapi');
$id = $model->insert($data);
//$id = $model->insert($data, 5); //如果是分表,可以通過(guò)第二個(gè)參數(shù)指定分表的參考ID
// 刪除
$model->delete(1);
上面的App\Model\User類(lèi),自動(dòng)匹配的表名為:user,加上配置前綴“tbl_”,完整的表名是:tbl_usre。默認(rèn)表名的自動(dòng)匹配規(guī)則是:取“\Model\”后面部分的字符全部轉(zhuǎn)小寫(xiě),并且在轉(zhuǎn)化后會(huì)加上配置的表前綴。
雙如:
<?php
namespace App\Model\User;
use PhalApi\Model\NotORMModel as Model;
class Friends extends Model {
}
則類(lèi)App\Model\User\Friends自動(dòng)匹配的表名為user_friends
。以下是2.x版本的一些示例:
2.x 的Model類(lèi)名 | 對(duì)應(yīng)的文件 | 自動(dòng)匹配的表名 |
---|---|---|
App\Model\User | ./src/app/Model/User.php | user |
App\ModelUser\Friends | ./src/app/Model/User/Friends.php | user_friends |
App\User\Model\Friends | ./src/app/user/Model/Friends.php | friends |
App\User\Model\User\Friends | ./src/app/user/Model/User/Friends.php | user_friends |
但在以下場(chǎng)景或者其他需要手動(dòng)指定表名的情況,可以重寫(xiě)PhalApi\Model\NotORMModel::getTableName($id)
方法并手動(dòng)指定表名。
數(shù)據(jù)庫(kù)表使用蛇形命名法而類(lèi)名使用大寫(xiě)字母分割的方式
如,當(dāng)Model_User類(lèi)對(duì)應(yīng)的表名為:my_user表時(shí),可這樣重新指定表名:
<?php
namespace App\Model;
use PhalApi\Model\NotORMModel as NotORM;
class User extends NotORM { protected function getTableName($id) { return 'my_user'; } }
其中,$id參數(shù)用于進(jìn)行分表的參考主鍵,只有當(dāng)存在分表時(shí)才需要用到。通常傳入的$id是整數(shù),然后對(duì)分表的總數(shù)進(jìn)行求余從而得出分表標(biāo)識(shí)。
即存在分表時(shí),需要返回的格式為:表名稱(chēng) + 下劃線(xiàn) + 分表標(biāo)識(shí)。分表標(biāo)識(shí)通常從0開(kāi)始,為連續(xù)的自然數(shù)。
## 在Model內(nèi)的CURD基本操作
假設(shè)對(duì)于前面的tbl_user表,有以下數(shù)據(jù)。
```sql
INSERT INTO `tbl_user` VALUES ('1', 'dogstar', '18', 'oschina', '2015-12-01 09:42:31');
INSERT INTO `tbl_user` VALUES ('2', 'Tom', '21', 'USA', '2015-12-08 09:42:38');
INSERT INTO `tbl_user` VALUES ('3', 'King', '100', 'game', '2015-12-23 09:42:42');
下面將結(jié)合示例,分別介紹NotORM更為豐富的數(shù)據(jù)庫(kù)操作。在開(kāi)始之前,假定已有:
class User extends NotORM {
public function test() {
$user = $this->getORM();
}
}
選擇單個(gè)字段:
// SELECT id FROM `tbl_user`
$user->select('id')
選擇多個(gè)字段:
// SELECT id, name, age FROM `tbl_user`
$user->select('id, name, age')
使用字段別名:
// SELECT id, name, MAX(age) AS max_age FROM `tbl_user`
$user->select('id, name, MAX(age) AS max_age')
選擇全部表字段:
// SELECT * FROM `tbl_user`
$user->select('*')
單個(gè)條件:
// WHERE id = 1
$user->where('id', 1)
$user->where('id = ?', 1)
$user->where(array('id', 1))
多個(gè)AND條件:
// WHERE id > 1 AND age > 18
$user->where('id > ?', 1)->where('age > ?', 18)
$user->and('id > ?', 1)->and('age > ?', 18)
$user->where('id > ? AND age > ?', 1, 18)
$user->where(array('id > ?' => 1, 'age > ?' => 10))
// WHERE name = 'dogstar' AND age = 18
$user->where(array('name' => 'dogstar', 'age' => 18))
多個(gè)OR條件:
// WHERE name = 'dogstar' OR age = 18
$user->or('name', 'dogstar')->or('age', 18)
嵌套條件:
// WHERE ((name = ? OR id = ?)) AND (note = ?) -- 'dogstar', '1', 'xxx'
// 實(shí)現(xiàn)方式1:使用AND拼接
$user->where('(name = ? OR id = ?)', 'dogstar', '1')->and('note = ?', 'xxx')
// 實(shí)現(xiàn)方式2:使用WHERE,并順序傳遞多個(gè)參數(shù)
$user->where('(name = ? OR id = ?) AND note = ?', 'dogstar', '1', 'xxx')
// 實(shí)現(xiàn)方式3:使用WHERE,并使用一個(gè)索引數(shù)組順序傳遞參數(shù)
$user->where('(name = ? OR id = ?) AND note = ?', array('dogstar', '1', 'xxx'))
// 實(shí)現(xiàn)方式4:使用WHERE,并使用一個(gè)關(guān)聯(lián)數(shù)組傳遞參數(shù)
$user->where('(name = :name OR id = :id) AND note = :note',
array(':name' => 'dogstar', ':id' => '1', ':note' => 'xxx'))
IN查詢(xún):
// WHERE id IN (1, 2, 3)
$user->where('id', array(1, 2, 3))
// WHERE id NOT IN (1, 2, 3)
$user->where('NOT id', array(1, 2, 3))
// WHERE (id, age) IN ((1, 18), (2, 20))
$user->where('(id, age)', array(array(1, 18), array(2, 20)))
模糊匹配查詢(xún):
// WHERE name LIKE '%dog%'
$user->where('name LIKE ?', '%dog%')
// WHERE name NOT LIKE '%dog%'
$user->where('name NOT LIKE ?', '%dog%')
溫馨提示:需要模糊匹配時(shí),不可寫(xiě)成:where('name LIKE %?%', 'dog')。
NULL判斷查詢(xún):
// WHERE (name IS NULL)
$user->where('name', null)
非NULL判斷查詢(xún):
// WHERE (name IS NOT ?) LIMIT 1; -- NULL
$user->where('name IS NOT ?', null)
單個(gè)字段升序排序:
// ORDER BY age
$user->order('age')
$user->order('age ASC')
單個(gè)字段降序排序:
// ORDER BY age DESC
$user->order('age DESC')
多個(gè)字段排序:
// ORDER BY id, age DESC
$user->order('id')->order('age DESC')
$user->order('id, age DESC')
限制數(shù)量,如查詢(xún)前10個(gè):
// LIMIT 10
$user->limit(10)
分頁(yè)限制,如從第5個(gè)位置開(kāi)始,查詢(xún)前10個(gè):
// LIMIT 5, 10
$user->limit(5, 10)
只有GROUP BY,沒(méi)有HAVING:
// GROUP BY note
$user->group('note')
既有GROUP BY,又有HAVING:
// GROUP BY note HAVING age > 10
$user->group('note', 'age > 10')
插入操作可分為插入單條紀(jì)錄、多條紀(jì)錄,或根據(jù)條件插入。
操作 | 說(shuō)明 | 示例 | 備注 | 是否PhalApi新增 |
---|---|---|---|---|
insert() | 插入數(shù)據(jù) | $user->insert($data); |
全局方式需要再調(diào)用insert_id()獲取插入的ID | 否 |
insert_multi() | 批量插入 | $user->insert_multi($rows); |
可批量插入 | 否 |
insert_update() | 插入/更新 | 接口簽名:insert_update(array $unique, array $insert, array $update = array() |
不存時(shí)插入,存在時(shí)更新 | 否 |
插入單條紀(jì)錄數(shù)據(jù),注意,必須是保持狀態(tài)的同一個(gè)NotORM表實(shí)例,方能獲取到新插入的行ID,且表必須設(shè)置了自增主鍵ID。
// INSERT INTO tbl_user (name, age, note) VALUES ('PhalApi', 1, 'framework')
$data = array('name' => 'PhalApi', 'age' => 1, 'note' => 'framework');
$user->insert($data);
$id = $user->insert_id();
var_dump($id);
// 輸出:新增的ID
int (4)
// 或者使用Model封裝的insert()方法
$model = new Model_User();
$id = $model->insert($data);
var_dump($id);
批量插入多條紀(jì)錄數(shù)據(jù):
// INSERT INTO tbl_user (name, age, note) VALUES ('A君', 12, 'AA'), ('B君', 14, 'BB'), ('C君', 16, 'CC')
$rows = array(
array('name' => 'A君', 'age' => 12, 'note' => 'AA'),
array('name' => 'B君', 'age' => 14, 'note' => 'BB'),
array('name' => 'C君', 'age' => 16, 'note' => 'CC'),
);
$rs = $user->insert_multi($rows);
var_dump($rs);
// 輸出,成功插入的條數(shù)
int(3)
插入/更新:
// INSERT INTO tbl_user (id, name, age, note) VALUES (8, 'PhalApi', 1, 'framework')
// ON DUPLICATE KEY UPDATE age = 2
$unique = array('id' => 8);
$insert = array('id' => 8, 'name' => 'PhalApi', 'age' => 1, 'note' => 'framework');
$update = array('age' => 2);
$rs = $user->insert_update($unique, $insert, $update);
var_dump($rs);
// 輸出影響的行數(shù)
操作 | 說(shuō)明 | 示例 | 備注 | 是否PhalApi新增 |
---|---|---|---|---|
update() | 更新數(shù)據(jù) | $user->where('id', 1)->update($data); |
更新異常時(shí)返回false,數(shù)據(jù)無(wú)變化時(shí)返回0,成功更新返回1 | 否 |
根據(jù)條件更新數(shù)據(jù):
// UPDATE tbl_user SET age = 2 WHERE (name = 'PhalApi');
$data = array('age' => 2);
$rs = $user->where('name', 'PhalApi')->update($data);
var_dump($rs);
// 輸出
int(1) //正常影響的行數(shù)
int(0) //無(wú)更新,或者數(shù)據(jù)沒(méi)變化
boolean(false) //更新異常、失敗
在使用update()進(jìn)行更新操作時(shí),如果更新的數(shù)據(jù)和原來(lái)的一樣,則會(huì)返回0(表示影響0行)。這時(shí),會(huì)和更新失?。ㄍ瑯佑绊?行)混淆。但NotORM是一個(gè)優(yōu)秀的類(lèi)庫(kù),它已經(jīng)提供了優(yōu)秀的解決文案。我們?cè)谑褂胾pdate()時(shí),只須了解這兩者返回結(jié)果的微妙區(qū)別即可。因?yàn)槭‘惓r(shí),返回false;而相同數(shù)據(jù)更新會(huì)返回0。即:
2、更新失敗時(shí),如更新一個(gè)不存在的字段,返回false,即:bool(false)
用代碼表示,就是:
$rs = DI()->notorm->user->where('id', $userId)->update($data);
if ($rs >= 1) { // 成功 } else if ($rs === 0) { // 相同數(shù)據(jù),無(wú)更新 } else if ($rs === false) { // 更新失敗 }
更新數(shù)據(jù),進(jìn)行加1操作:
```php
// UPDATE tbl_user SET age = age + 1 WHERE (name = 'PhalApi')
$rs = $user->where('name', 'PhalApi')->update(array('age' => new NotORM_Literal("age + 1")));
var_dump($rs);
// 輸出影響的行數(shù)
查詢(xún)操作主要有獲取一條紀(jì)錄、獲取多條紀(jì)錄以及聚合查詢(xún)等。
操作 | 說(shuō)明 | 示例 | 備注 | 是否PhalApi新增 |
---|---|---|---|---|
fetch() | 循環(huán)獲取每一行 | while($row = $user->fetch()) { ... ... } |
否 | |
fetchOne() | 只獲取第一行 | $row = $user->where('id', 1)->fetchOne(); |
等效于fetchRow() | 是 |
fetchRow() | 只獲取第一行 | $row = $user->where('id', 1)->fetchRow(); |
等效于fetchOne() | 是 |
fetchPairs() | 獲取鍵值對(duì) | $row = $user->fetchPairs('id', 'name'); |
第二個(gè)參數(shù)為空時(shí),可取多個(gè)值,并且多條紀(jì)錄 | 否 |
fetchAll() | 獲取全部的行 | $rows = $user->where('id', array(1, 2, 3))->fetchAll(); |
等效于fetchRows() | 是 |
fetchRows() | 獲取全部的行 | $rows = $user->where('id', array(1, 2, 3))->fetchRows(); |
等效于fetchAll() | 是 |
queryAll() | 復(fù)雜查詢(xún)下獲取全部的行,默認(rèn)下以主鍵為下標(biāo) | $rows = $user->queryAll($sql, $parmas); |
等效于queryRows() | 是 |
queryRows() | 復(fù)雜查詢(xún)下獲取全部的行,默認(rèn)下以主鍵為下標(biāo) | $rows = $user->queryRows($sql, $parmas); |
等效于queryAll() | 是 |
count() | 查詢(xún)總數(shù) | $total = $user->count('id'); |
第一參數(shù)可省略 | 否 |
min() | 取最小值 | $minId = $user->min('id'); |
否 | |
max() | 取最大值 | $maxId = $user->max('id'); |
否 | |
sum() | 計(jì)算總和 | $sum = $user->sum('age'); |
否 |
循環(huán)獲取每一行,并且同時(shí)獲取多個(gè)字段:
// SELECT id, name FROM tbl_user WHERE (age > 18);
$user = $user->select('id, name')->where('age > 18');
while ($row = $user->fetch()) {
var_dump($row);
}
// 輸出
array(2) {
["id"]=>
string(1) "2"
["name"]=>
string(3) "Tom"
}
array(2) {
["id"]=>
string(1) "3"
["name"]=>
string(4) "King"
}
... ...
循環(huán)獲取每一行,并且只獲取單個(gè)字段。需要注意的是,指定獲取的字段,必須出現(xiàn)在select里,并且返回的不是數(shù)組,而是字符串。
// SELECT id, name FROM tbl_user WHERE (age > 18);
$user = $user->select('id, name')->where('age > 18');
while ($row = $user->fetch('name')) {
var_dump($row);
}
// 輸出
string(3) "Tom"
string(4) "King"
... ...
注意!以下是錯(cuò)誤的用法。還記得前面所學(xué)的NotORM狀態(tài)的保持嗎?因?yàn)檫@里每次循環(huán)都會(huì)新建一個(gè)NotORM表實(shí)例,所以沒(méi)有保持前面的查詢(xún)狀態(tài),從而死循環(huán)。
while ($row = DI()->notorm->user->select('id, name')->where('age > 18')->fetch('name')) {
var_dump($row);
}
只獲取第一行,并且獲取多個(gè)字段,等同于fetchRow()操作。
// SELECT id, name FROM tbl_user WHERE (age > 18) LIMIT 1;
$rs = $user->select('id, name')->where('age > 18')->fetchOne();
var_dump($rs);
// 輸出
array(2) {
["id"]=>
string(1) "2"
["name"]=>
string(3) "Tom"
}
只獲取第一行,并且只獲取單個(gè)字段,等同于fetchRow()操作。
var_dump($user->fetchOne('name'));
// 輸出
string(3) "Tom"
獲取鍵值對(duì),并且獲取多個(gè)字段:
// SELECT id, name, age FROM tbl_user LIMIT 2;
$rs = $user->select('name, age')->limit(2)->fetchPairs('id'); //指定以ID為KEY
var_dump($rs);
// 輸出
array(2) {
[1]=>
array(3) {
["id"]=>
string(1) "1"
["name"]=>
string(7) "dogstar"
["age"]=>
string(2) "18"
}
[2]=>
array(3) {
["id"]=>
string(1) "2"
["name"]=>
string(3) "Tom"
["age"]=>
string(2) "21"
}
}
獲取鍵值對(duì),并且只獲取單個(gè)字段。注意,這時(shí)的值不是數(shù)組,而是字符串。
// SELECT id, name FROM tbl_user LIMIT 2
var_dump($user->limit(2)->fetchPairs('id', 'name')); //通過(guò)第二個(gè)參數(shù),指定VALUE的列
// 輸出
array(2) {
[1]=>
string(7) "dogstar"
[2]=>
string(3) "Tom"
}
獲取全部的行,相當(dāng)于fetchRows()操作。
// SELECT * FROM tbl_user
var_dump($user->fetchAll());
// 輸出全部表數(shù)據(jù),結(jié)果略
使用原生SQL語(yǔ)句進(jìn)行查詢(xún),并獲取全部的行:
// SELECT name FROM tbl_user WHERE age > :age LIMIT 1
$sql = 'SELECT name FROM tbl_user WHERE age > :age LIMIT 1';
$params = array(':age' => 18);
$rs = $user->queryAll($sql, $params);
var_dump($rs);
// 輸出
array(1) {
[0]=>
array(1) {
["name"]=>
string(3) "Tom"
}
}
// 除了使用上面的關(guān)聯(lián)數(shù)組傳遞參數(shù),也可以使用索引數(shù)組傳遞參數(shù)
$sql = 'SELECT name FROM tbl_user WHERE age > ? LIMIT 1';
$params = array(18);
// 也使用queryRows()別名
$rs = $user->queryRows($sql, $params);
在使用queryAll()queryRows()
進(jìn)行原生SQL操作時(shí),需要特別注意:
下面是不好的寫(xiě)法,很有可能會(huì)導(dǎo)致SQL注入攻擊
// 存在SQL注入的寫(xiě)法
$id = 1;
$sql = "SELECT * FROM tbl_demo WHERE id = $id";
$rows = $this->getORM()->queryAll($sql);
對(duì)于外部不可信的輸入數(shù)據(jù),應(yīng)改用參數(shù)傳遞的方式。
// 使用參數(shù)綁定方式
$id = 1;
$sql = "SELECT * FROM tbl_demo WHERE id = ?";
$rows = $this->getORM()->queryAll($sql, array($id));
查詢(xún)總數(shù):
// SELECT COUNT(id) FROM tbl_user
var_dump($user->sum('id'));
// 輸出
string(3) "3"
查詢(xún)最小值:
// SELECT MIN(age) FROM tbl_user
var_dump($user->min('age'));
// 輸出
string(2) "18"
查詢(xún)最大值:
// SELECT MAX(age) FROM tbl_user
var_dump($user->max('age'));
// 輸出
string(3) "100"
計(jì)算總和:
// SELECT SUM(age) FROM tbl_user
var_dump($user->sum('age'));
// 輸出
string(3) "139"
操作 | 說(shuō)明 | 示例 | 備注 | 是否PhalApi新增 |
---|---|---|---|---|
delete() | 刪除 | $user->where('id', 1)->delete(); |
禁止無(wú)where條件的刪除操作 | 否 |
按條件進(jìn)行刪除,并返回影響的行數(shù):
// DELETE FROM tbl_user WHERE (id = 404);
$user->where('id', 404)->delete();
請(qǐng)?zhí)貏e注意,PhalApi禁止全表刪除操作。即如果是全表刪除,將會(huì)被禁止,并拋出異常。如:
// Exception: sorry, you can not delete the whole table
$user->delete();
以下是事務(wù)操作的一個(gè)示例。
// Step 1: 開(kāi)啟事務(wù)
\PhalApi\DI()->notorm->beginTransaction('db_demo');
// Step 2: 數(shù)據(jù)庫(kù)操作
\PhalApi\DI()->notorm->user->insert(array('name' => 'test1'));
\PhalApi\DI()->notorm->user->insert(array('name' => 'test2'));
// Step 3: 提交事務(wù)/回滾
\PhalApi\DI()->notorm->commit('db_demo');
//\PhalApi\DI()->notorm->rollback('db_demo');
對(duì)于關(guān)聯(lián)查詢(xún),簡(jiǎn)單的關(guān)聯(lián)可使用NotORM封裝的方式,而復(fù)雜的關(guān)聯(lián),如多個(gè)表的關(guān)聯(lián)查詢(xún),則可以使用PhalApi封裝的接口。
如果是簡(jiǎn)單的關(guān)聯(lián)查詢(xún),可以使用NotORM支持的寫(xiě)法,這樣的好處在于我們使用了一致的開(kāi)發(fā),并且能讓PhalApi框架保持分布式的操作方式。需要注意的是,關(guān)聯(lián)的表仍然需要在同一個(gè)數(shù)據(jù)庫(kù)。
以下是一個(gè)簡(jiǎn)單的示例。假設(shè)我們有這樣的數(shù)據(jù):
INSERT INTO `phalapi_user` VALUES ('1', 'wx_edebc', 'dogstar', '***', '4CHqOhe1', '1431790647', '');
INSERT INTO `phalapi_user_session_0` VALUES ('1', '1', 'ABC', '', '0', '0', '0', null);
那么對(duì)應(yīng)關(guān)聯(lián)查詢(xún)的代碼如下面:
// SELECT expires_time, user.username, user.nickname FROM phalapi_user_session_0
// LEFT JOIN phalapi_user AS user
// ON phalapi_user_session_0.user_id = user.id
// WHERE (token = 'ABC') LIMIT 1
$rs = \PhalApi\DI()->notorm->user_session_0
->select('expires_time, user.username, user.nickname')
->where('token', 'ABC')
->fetchRow();
var_dump($rs);
會(huì)得到類(lèi)似這樣的輸出:
array(3) {
["expires_time"]=>
string(1) "0"
["username"]=>
string(35) "wx_edebc"
["nickname"]=>
string(10) "dogstar"
}
這樣,我們就可以實(shí)現(xiàn)關(guān)聯(lián)查詢(xún)的操作。按照NotORM官網(wǎng)的說(shuō)法,則是:
If the dot notation is used for a column anywhere in the query ("$table.$column") then NotORM automatically creates left join to the referenced table. Even references across several tables are possible ("$table1.$table2.$column"). Referencing tables can be accessed by colon: $applications-&select("COUNT(application_tag:tag_id)").
所以->select('expires_time, user.username, user.nickname')
這一行調(diào)用將會(huì)NotORM自動(dòng)產(chǎn)生關(guān)聯(lián)操作,而ON的字段,則是這個(gè)字段關(guān)聯(lián)你配置的表結(jié)構(gòu),外鍵默認(rèn)為:表名_id 。
如果是復(fù)雜的關(guān)聯(lián)查詢(xún),則是建議使用原生的SQL語(yǔ)句,但仍然可以保持很好的寫(xiě)法,如這樣一個(gè)示例:
$sql = 'SELECT t.id, t.team_name, v.vote_num '
. 'FROM phalapi_team AS t LEFT JOIN phalapi_vote AS v '
. 'ON t.id = v.team_id '
. 'ORDER BY v.vote_num DESC';
$rows = $this->getORM()->queryAll($sql, array());
var_dump($rows);
如前面所述,這里需要手動(dòng)填寫(xiě)完整的表名,以及慎防SQL注入攻擊。
有時(shí),我們還需要進(jìn)行一些其他的數(shù)據(jù)庫(kù)操作,如創(chuàng)建表、刪除表、添加表字段等。對(duì)于需要進(jìn)行的數(shù)據(jù)庫(kù)操作,而上面所介紹的方法未能滿(mǎn)足時(shí),可以使用更底層更通用的接口,即:NotORM_Result::query($query, $parameters)
。
例如,刪除一張表。
$user->query('DROP TABLE tbl_user', array());
假設(shè)有以下多個(gè)數(shù)據(jù)庫(kù)表,它們的表結(jié)構(gòu)一樣。
數(shù)據(jù)庫(kù)表 | 數(shù)據(jù)庫(kù)實(shí)例 |
---|---|
tbl_demo | db_master |
tbl_demo_0 | db_master |
tbl_demo_1 | db_master |
tbl_demo_2 | db_master |
為了使用分表存儲(chǔ),可以修改數(shù)據(jù)庫(kù)表的配置,讓它支持分表的情況。
return array(
'tables' => array(
'demo' => array(
'prefix' => 'tbl_',
'key' => 'id',
'map' => array(
array('db' => 'db_master'),
array('start' => 0, 'end' => 2, 'db' => 'db_master'),
),
),
),
);
上面配置map選項(xiàng)中array('db' => 'master')
用于指定缺省主表使用master數(shù)據(jù)庫(kù)實(shí)例,而下一組映射關(guān)系則是用于配置連續(xù)在同一臺(tái)數(shù)據(jù)庫(kù)實(shí)例的分表區(qū)間,即tbl_demo_0、tbl_demo_1、tbl_demo_2都使用了master數(shù)據(jù)庫(kù)實(shí)例。
溫馨提示:當(dāng)分表找不到時(shí),PhalApi會(huì)自動(dòng)退化使用缺省主表。
假設(shè)分別的規(guī)則是根據(jù)ID對(duì)3進(jìn)行求余。當(dāng)需要使用分表時(shí),在使用Model基類(lèi)的情況下,可以通過(guò)重寫(xiě)PhalApi\Model\NotORMModel::getTableName($id)
實(shí)現(xiàn)相應(yīng)的分表規(guī)則。
<?php
namespace App\Model;
use PhalApi\Model\NotORMModel as NotORM;
class Demo extends NotORM {
protected function getTableName($id) {
$tableName = 'demo';
if ($id !== null) {
$tableName .= '_' . ($id % 3);
}
return $tableName;
}
}
然后,便可使用之前一樣的CURD基本操作,但框架會(huì)自動(dòng)匹配分表的映射。例如:
$model = new App\Model\Demo();
$row = $model->get('3', 'id'); // 使用分表tbl_demo_0
$row = $model->get('10', 'id'); // 使用分表tbl_demo_1
$row = $model->get('2', 'id'); // 使用分表tbl_demo_2
回到使用Model基類(lèi)的上下文,更進(jìn)一步,我們可以通過(guò)$this->getORM($id)
來(lái)獲取分表的實(shí)例從而進(jìn)行分表的操作。如:
<?php
namespace App\Model;
use PhalApi\Model\NotORMModel as NotORM;
class Demo extends NotORM {
public function getNameById($id) {
$row = $this->getORM($id)->select('name')->fetchRow();
return !empty($row) ? $row['name'] : '';
}
}
通過(guò)傳入不同的$id,即可獲取相應(yīng)的分表實(shí)例。
把數(shù)據(jù)庫(kù)表的基本建表語(yǔ)句保存到./data目錄下,文件名與數(shù)據(jù)庫(kù)表名相同,后綴統(tǒng)一為“.sql”。如這里的./data/demo.sql文件。
`name` varchar(11) DEFAULT NULL,
需要注意的是,這里說(shuō)的基本建表語(yǔ)句是指:僅是這個(gè)表所特有的字段,排除已固定公共有的自增主鍵id、擴(kuò)展字段ext_data和CREATE TABLE關(guān)鍵字等。
然后可以使用phalapi-buildsqls腳本命令,快速自動(dòng)生成demo缺省主表和全部分表的建表SQL語(yǔ)句。如下:
$ ./bin/phalapi-buildsqls ./config/dbs.php demo
正常情況下,會(huì)生成類(lèi)似以下的SQL語(yǔ)句:
CREATE TABLE `demo` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(11) DEFAULT NULL,
`ext_data` text COMMENT 'json data here',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `tpl_demo_0` ... ...;
CREATE TABLE `tpl_demo_1` ... ...;
CREATE TABLE `tpl_demo_2` ... ...;
在將上面的SQL語(yǔ)句導(dǎo)入數(shù)據(jù)庫(kù)后,或者手動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)表后,便可以像之前那樣操作數(shù)據(jù)庫(kù)了。
更多建議: