連接與查詢構造器
數據庫操作運行流程圖.png
1.數據庫操作運行流程圖
* ThinkPHP5的數據庫操作對底層進行優化設計,對各種操作進行高級封裝,
既可以直接使用連接器進行高效的原生查詢,也可以使用封裝好的查詢構造器進行
只管便捷的查詢。
數據庫連接配置
1.配置方法:
* 靜態連接:應用/模塊中的數據庫配置文件database.php
* 動態連接:入口類Db.php中的connect(參數[數組或字符串])方法
* 靜態配置重要參數
-> debug True 數據庫調試模式
* 動態配置連接字符串
-> mysql://root:1234@localhost:3306/thinkphp#utf8
-> 數據庫://用戶名:密碼@數據庫地址:數據庫端口/數據庫名#字符集
* 動態配置數組方式
eg: public function demo()
{
$config = [
'type'=>'mysql',
'hostname'=>'localhost',
'username'=>'root',
'password'=>'root',
'database'=>'tp5',
];
//1.獲取數據庫的連接實例/對象
$link = Db::connect($config);
//2.用連接實例調用查詢類的查詢方法
$res = $link->table('staff')->select();
//3.輸出查詢結果
dump($res);
//Db::table('staff')->select();
}
2.數據庫的第一步就是數據庫的鏈接,TP5提供了強大靈活的連接方式,
特別是惰性連接支持,極大的提高了連接效率(db()助手函數不支持),
使用戶的關注重點放在業務邏輯上,不必擔心連接問題。
數據庫原生查詢
1.原生查詢的實現
* Connection類
->query(sql語句字符串,[參數綁定]):讀操作 select
->execute(sql語句字符串,[參數綁定]):寫操作 insert、update、delete
2.thinkphp > library > think > db > Connection.php
* 查詢操作:
->普通操作
eg: $sql = "select * from staff salary > 4000";
$result = Db::query($sql);
dump($result);
->參數綁定查詢,可以防止sql注入
eg: $sql = "select * from staff where salary > ?";
$result = Db::query($sql,[4000]);
dump($result);
->命名占位符綁定(推薦使用)
eg: $sql = "select * from staff where salary > :salary";
$result = Db::query($sql,['salary'=>4000]);
dump($result);
* 其他操作
-> 更新操作
eg: $sql = "update staff set salary = salary+1000 where id=:id";
$res = Db::execute($sql,['id'=>1004]);
dump($res);
->插入操作
eg: $sql = "insert into staff (name,sex,age) values(:name,:sex,:age)";
$res = Db::execute($sql,['name'=>'thinkphp5','name'=>'1','age'=>10]);
dump($res);
->刪除操作
eg: $sql = "delete from staff where id=:id";
$res = Db::execute($sq,['id'=>10]);
dump($res);
* Connection類實例通過入口類Db靜態自動調用,不用顯示寫出
* 利用查詢構造器進行增刪改查操作,最終仍是調用連接類Connection對應方法完成。
查詢構造器
1.查詢構造器的原理:
查詢構造器工作原理圖 .png
thinkphp > library > think > db > Query.php Builder.php 查詢類和生成類
2.什么是鏈式操作?為什么要用鏈式操作?
鏈式操作的工作原理圖.png
3.數據表的查詢條件是如何生成的?
查詢條件生成原理圖.png
* 兩種方法
where():AND條件
whereOr():OR條件
* 三種格式
where('字段名','表達式','查詢條件') //默認省略表達式就是相等
where(['字段名'=>['表達式','查詢條件'], ....])
where(function($query){ //鏈式查詢語句;閉包查詢 })
eg:三種格式的例子
Db::table("staff")
->field('name')
->where('salary','>',3000)
->select();
Db::table("staff")
->field('name')
->where(['id'=>['>',1003],'salary'=>['>',3000],])
->select();
$salary = 3000;
Db::table("staff")
->field('name')
->where(function($query) use ($salary){
$query->where('id','>',1003)
->where('salary','>',$salary)
})
->select();
Db::select(
function($query){
$query->table('staff')
->field(['name'=>'姓名','salary'=>'工資'])
->where(['id'=>['>',1003],'salary'=>['>',4000],]);
})
);
4.如何用查詢構造器實現數據表的增刪改查(CURD)操作?
* 新增
-> insert(['字段'=>'值'])
-> insertAll(['二維數組'])
* 更新
-> update(['字段'=>'值'])
-> setInc/setDec('字段',步長,[秒數])
* 讀取
-> find(主鍵)
-> select(主鍵)
* 刪除
-> delete(主鍵)
-> delete(true)
5.查詢
eg:
//value('字段','默認值') column('字段','字段')
dump(Db::table('staff')->where('id','1020)->value('name'));
dump(Db::table('staff')->where('id','>','1020)->column('name'));
6.推薦使用閉包,來生成查詢條件,不僅功能強大,而且便于擴展
模型
1.模型是對實體的抽象模式,快速直觀的展示出實體的特征
2.tp5中的模型是指什么
<?php
namespace app\index\model;
use Think\Model;
class Staff extends Model
{
//
}
3.模型類中的屬性和方法
* 模型類的屬性和方法需要在基類Model中查看
* Model.php類位于thinkphp/library/think/Model.php
* 該類是一個抽象類,不能實例化,必須有子類繼承并實現內部的全部抽象方法
模型與數據表的對應關系
模型與數據表的對應關系.png
2.模型與數據表的區別與聯系
* 區別:
-> 分工不同 Db類負責數據表的訪問,模型專注于業務邏輯處理
-> 返回值不同 Db訪問返回數組,模型操作返回對象
* 聯系
-> 模型最終仍須調用Db類完成數據表的查詢操作
3.如何創建模型
* 手工創建:在應用或模塊下創建目錄model,并在該目錄下創建與數據表同名的類文件
如:User.php對應user.dbf表
* 命令創建:在當前項目目錄下,用命令:php think make:model 模塊名/模型名,會
自動創建指定位置和命名空間的空模型,并自動與數據表綁定
eg: php think make:model test/Blog
* 模型創建完成后,會自動獲取當前數據表名稱$table,表中所有字段信息$field,主鍵$pk
和數據庫配置信息$connection . 同時會自動繼承基類Model中所有屬性和方法,protected
類型在本模型中使用,public類型還可以在控制器使用,靜態方法大多直接在控制器,進行
CURD操作
4.在控制器中調用模型
* 實例化調用
-> 用new生成模型對象
-> 用模型對象處理相關業務
* 靜態調用
-> 通過靜態查詢直接將一個空模型轉為數據模型
-> 在調用相關方法完成增刪改查操作
* 不推薦使用助手函數model()和添加模型類后綴
eg:
<?php
namespace app\index\controller;
use app\index\model\Staff;
class Index
{
public function index()
{
//實例化創建模型對象
$staff = new Staff();
$res = $staff->where('id',1004)->find();
dump($res);
dump($res->getData());
//靜態創建模型對象
dump(Staff::get(1004)->getData('name'));
}
}
6.模型數據訪問方式
* 控制器訪問(外部) 用模型對象:$model
* 模型訪問(內部) 用偽對象變量:$this
模型CURD操作
1.通過模型,create創建數據
* save($data[]) 單條添加 調用方式:實例化 返回值:影響記錄數
* saveAll($data[]) 批量添加 調用方式:實例化 返回值:模型對象數組
* create($data[]) 單條添加 調用方式:靜態 返回值:模型對象
* 數據創建過程可以出發很多操作,非Db類操作可比
* 靜態調用的實質其實仍是實例化調用,只是將CURD方法進行靜態封裝
* saveAll()方法實際上是通過多次執行insert語句完成,很少用到
* 理論上講,通過模型向表中添加數據,盡可能都采用靜態方式
eg:
引入 use app\index\model\Staff;
//方法中,實例化模型
$staff = new Staff();
$staff->name = 'test';
$staff->sex = 1;
$staff->save();
//批量添加
$data = [
['name'=>'test','sex'=>1],
['name'=>'test1','sex'=>0],
['name'=>'test2','sex'=>1],
];
$res = $staff->saveAll($data); //返回的是對象數組
//靜態添加
$res = Staff::create([['name'=>'test4','sex'=>1],]);
dump($res->getData());
2.通過模型update更新數據
* save($data=[],$where=[]) 單條更新 實例化調用 返回值:影響記錄數
* saveAll($data=[],true) 批量更新 實例化調用 返回值:模型對象數組
* update($data=[],$where=[],$field=[]) 單條更新 靜態調用 返回值:模型對象
* 不允許無條件更新,必須設置更新條件
* 可以將更新條件,如主鍵寫在更新數據中,方法可以自動識別
* 更新條件可以使用閉包,完成更復雜的業務邏輯
eg:
$staff = new Staff();
$data = [
//'id'=>1032,
'name'=>'張三豐',
'age'=>100
];
$where = ['id'=>1032];
//$staff->isUpdate(true)->save($data);
$staff->save($data,$where);
dump($staff->getData());
$staff = new Staff();
$data = [
['id'=>1032,'name'=>'張三豐', 'age'=>100],
['id'=>1033,'name'=>'張三豐1', 'age'=>101],
['id'=>1034,'name'=>'張三豐2', 'age'=>102],
];
$staff->isUpdate(true)->saveAll($data);
dump($staff->getData());
//update(更新數據,更新條件,允許更新的字段)
$data = ['name'=>'張三豐2', 'age'=>102,'salary'=>5000];
//$where = ['id'=>1034];
$where = function($query){ //閉包 好處是支持外部傳入變量
$query->where('id',1034);
};
$field = ['name','age'];
$res = Staff::update($data,$where,$field);
dump($res);
3.通過模型查詢表中數據
* ORM模型(對象關系映射)
-> ThinkPHP5實現了基于ActiveRecords模式的ORM模型
模型與數據表的對應關系.png
* Read讀取操作
-> find($where)和get($where) 調用方式:實例化/靜態 返回值:模型對象
-> select($where)和all($where) 調用方式:實例化/靜態 返回值:模型對象數組
-> 原則來說,查詢都應該采用靜態查詢方法
-> 盡可能采用get()和all()方法代替find()和select()
-> 牢記一條原則:一個模型對象實例應該唯一對應數據表的一條記錄
eg:
$staff = new Staff();
$where = function($query){
$query->field(['name','age'])
->where('salary','>',8000);
};
$res = $staff->select($where);
dump($res);
$res = Staff::find($where);
$res = Staff::get($where);
$res = Staff::all($where);
4.通過模型delete刪除表中記錄
* delete() 調用方式:實例化 返回值:受影響記錄數量
* destory(條件/閉包) 調用方式:靜態 返回值:受影響記錄數量
* delete()方法不要傳任何參數,它只刪除當前模型對象對應的記錄
* destory()中的刪除條件,推薦采用閉包方式
* 推薦用軟刪除替代該方法,即用更新方式來實現刪除操作
eg:
$res = Staff::get(['id'=>['>',1000]]);
$affected = $res->delete(); //不能有參數,只刪除一條
$where = function($query){
$query->where('id','>',1030)
->where('age','>',40)
->whereOr('salary','>',40000);
};
$value = Staff::all($where);
$res = Staff::destroy($where);
模型高級操作
1.模型讀取器
* 觸發條件:當用模型對象讀取表中字段值的字段
* 應用場景:日期時間字段,集合或枚舉數據數字狀態與文本轉換字段拼裝
* 設置位置:在模型中設置,訪問屬性通常為protected,不允許外部直接訪問
* 方法名稱:get屬性名稱Attr($name,$data=[])
//FieldName 命名規則,駝峰命名法,對應表中字段 field_name
get FieldNameAttr($name,$data[]) //data 是表中所有的數據
eg:
//控制器中調用
$staff = Staff::get(1033);
return '入職時間:'.$staff->hiredate;
//模型中
protected function getHireDateAttr($hiredate)
{
return date('Y-m-d',$hiredate);
}
2.模型的讀取器工作原理圖
* 模型記錄元素數據保存在$data屬性中,可用getData()方法獲取!$staff->getData('字段');
模型的讀取器工作原理圖.png
3.模型的修改器
* 觸發條件:當用模型對象想數據表中新增記錄或更新字段值的時候
* 應用場景:日期時間字段,集合或枚舉數據,數字狀態與文本轉換,字段拼裝
* 設置位置:在模型中設置,訪問屬性通常為protected,不允許外部直接訪問
* 方法名稱:set 屬性名稱Attr($name,$data=[])
set FieldNameAttr($name) //FieldName 對應表中字段:field_name
eg:
控制器中:
$staff->hiredate = '2017-12-20';
$staff->save();
模型中:
protected function setHireDateAttr($hiredate)
{
return strtotime($hiredate);
}
4.模型的修改器工作原理圖
模型的修改器工作原理圖.png
5.模型的獲取器與修改器,是模型中最常用的自定義方法,配合驗證器,可以
讓用戶更安全的讀寫數據表中的數據
模型類型轉換
1.類型轉換
protected $type = [
'name'=>'array', //以json格式寫入,取出自動解碼為array
'age'=>'integer', //該字段寫入和輸出的時候都會自動轉換為整型
'salary'=>'float', //該字段寫入和輸出的時候都會自動轉換為浮點型
'dept'=>'serialize', //自動序列寫入,讀取的時候自動反序列化
'home'=>'json', //json_enocde寫入,讀取時json_decode處理
'hiredate'=>'timestamp',//用strtotime轉為時間戳寫入,讀出按$dateFormat格式輸出
//'hiredate'=>'timestamp:Y/m/d',
'birthday'=>'datetime' //讀寫時都按$dateFormat格式處理
];
eg:模型中
class Staff extends Model
{
protected $type = [
'name'=>'array',
'age'=>'integer',
'salary'=>'float',
'hiredate'=>'timestamp'
];
}
//控制器中
public function index()
{
$staff = Staff::get(1023);
$staff->name = '張無忌';
$staff->age = '100';
$staff->salary = '6000.23';
$staff->hiredate = '2017-12-20';
$staff->isUpdate(true)->save();
//轉換數據
$staff = Staff::get(1033);
dump($staff->name);
dump($staff->age);
dump($staff->salary);
dump($staff->hiredate);
//查看原始數據
$staff->getData();
}
* 通過配置屬性值的方式,來完成寫入數據的類型自動轉換,比用修改器和讀取器更加方便。
如果數據處理邏輯不復雜,推薦使用這種方式來替代傳統的讀取器和修改器方法。