https://blog.csdn.net/self_realian/article/details/78596261php
1、什麼是模型 thinkphp
爲何咱們要在項目中使用模型,其實咱們知道,咱們能夠直接在控制器中進行數據庫的增、刪、改、查,其實已經能基本完成咱們的需求,可是,爲何還要有模型的存在呢? 數據庫
好比說咱們如今要作一個用戶註冊的操做,用戶註冊咱們可能用兩個表來保存用戶的信息,一個是user表(保存用戶基本信息),一個是user_info表(保存用戶擴展信息,好比愛好等等),若是咱們如今直接在咱們的控制器中編寫,那麼咱們須要對數據庫兩個表進行操做,咱們就須要寫兩個db的方法來進行操做,咱們須要將這些方法進行封裝,最後放在模型裏,只要執行這個模型裏的某一個方法,那麼就會自動完成咱們全部操做的操做,也就是說,把咱們數據庫完成同一件事情的操做,放在一個公共方法裏,這樣咱們在控制器裏進行調用就會變得很方便。特別是代碼複用的部分,咱們能夠編寫這樣的方法,讓咱們在任何地方可使用用戶註冊這個方法。那麼下面就看看如何定義模型: 數組
首先在咱們的application/index/建立一個model目錄,專門放模型文件,而後在model目錄下建立一個User.php文件 瀏覽器
模型文件的命名規範: 首先咱們的模型名和咱們的表名要是對應的。好比咱們要寫咱們數據庫中的shulv_user表,那麼咱們的模型名就是去掉前綴以後使用駝峯的命名方式,也就是User.php,當數據庫中有這種包含下劃線的表名時,去掉下劃線,並將下劃線後邊的字母大寫,即時模型名,shulv_user_info 對應模型名就是UserInfo,這樣他就會自動對應到數據庫中的表。下邊寫代碼驗證一下:安全
namespace app\index\controller;閉包
use think\Controller;app
use app\index\model\User;//使用咱們剛剛新建的那個模型函數
class Index extends Controller學習
{
public function index()
{
$res = User::get(51);//經過get()方法,取出user表中的id=51的這條數據(如今能夠不用知道爲何使用get方法,後邊會說)
$res = $res->toArray();//將取出的數據轉換成數組
dump($res);
}
}
你會發現,咱們沒有在模型中的User.php中編寫任何的代碼,可是在控制器中use進來User模型以後,就能夠直接使用靜態方法,這是爲何呢?
由於在咱們的模型文件中的類都繼承了think\Model這個類,think\Model中其實有這樣的方法,這些方法,後邊會說到。一樣咱們還能夠在控制器中使用new的方式
$user = new User();
$res = $user::get(53);
$res = $res->toArray();
dump($res);
當咱們,不想經過use的方式將User模型引入進來的時候,咱們還能夠經過下邊的方法:
namespace app\index\controller;
use think\Controller;
//use app\index\model\User;//使用咱們剛剛新建的那個模型
use think\Loader;//經過引入think下的Loader類
class Index extends Controller
{
public function index()
{
$user = Loader::model("User");//經過Loader下的model這個靜態方法來引入對應模型
$res = $user::get(52);
$res = $res->toArray();
dump($res);
}
}
//說明:這種方式,是當咱們的控制器下有多個模型的時候,咱們使用這種方式,這樣咱們就不用在使用每個模型的時候都use一下,咱們直接使用Loader::model()這種方法直接引入。另外,它還提供了一個助手函數model(),此時咱們連Loader類都不用引入了
/*
$user = model("User");
$res = $user::get(52);
$res = $res->toArray();
dump($res);
*/
//在這裏建議你們使用前兩種方式,由於助手函數式可能被覆蓋掉的,固然這種狀況不多發生,可是爲了不,或者讓咱們的代碼可讀性更高,建議你們使用第一種方式,也就是將須要的模型都use進來,經過它的靜態方法來獲取數據,這樣咱們的代碼看起來更清晰,咱們能夠在最上邊就看見咱們當前的控制器使用了哪些模型類,這樣在後期維護中會變得很方便
2、經過模型進行數據查詢
namespace app\index\controller;
use think\Controller;
use app\index\model\User;//使用咱們剛剛新建的那個模型
//use think\Loader;//經過引入think下的Loader類
class Index extends Controller
{
public function index()
{
//以前咱們查詢一條數據的時候是經過給get()方法傳遞一個主鍵來獲取對應數據,咱們還能夠傳遞一個閉包函數
$res = User::get(function($query){//在這裏邊構造查詢條件
$query->where("id", "eq", 55);//這裏條件的構造和前邊的博客中說的同樣(一樣能夠在後邊繼續添加鏈式方法)
});
$res = $res->toArray();
dump($res);
}
}
咱們還能夠經過下邊這種方法構造where條件
namespace app\index\controller;
use think\Controller;
use app\index\model\User;//使用咱們剛剛新建的那個模型
//use think\Loader;//經過引入think下的Loader類
class Index extends Controller
{
public function index()
{
$res = User::where("id", 51)
->find();//一樣咱們能夠在後邊添加更多的鏈式方法
$res = $res->toArray();
dump($res);
}
}
/*
當咱們想獲取表中的多條數據時,咱們可使用User::all();參數是主鍵名,可使用字符串的方式,也可使用數組的方式
$res = User::all("1,2,3");//這個時候返回的每一條記錄都是一個對象,咱們就能夠經過foreach進行遍歷輸出
//或$res = User::all([1,2,3]);
foreach($res as $value){
dump($value->toArray());//這個時候咱們就會得到三條數據
}
//這個all()還能夠接收一個閉包函數做爲參數,來構造where條件
$res = User::all(function($query){
$query->where("id", "<", 5); //一樣能夠添加更多的鏈式方法
});
*/
當咱們想要獲取單獨的一個字段,咱們知道Db類有一個value() 方法,模型咱們也能夠這樣
$res = User::where()->value("email");//直接返回的是字符串
dump($res);
//獲取某一個字段那一列時
$res = User::column("email");//返回的是一個數組
3、使用模型添加數據
在thinkphp,它的model爲咱們提供了一個create()方法(靜態方法),直接向數據庫中插入數據
namespace app\index\controller;
use think\Controller;
use app\index\model\User;//使用咱們剛剛新建的那個模型
//use think\Loader;//經過引入think下的Loader類
class Index extends Controller
{
public function index()
{
$res = User::create([
'username' => 'shulv',
'password' => md5('shulv'),
'email' => 'shulv@qq.com',
'num' => 100
]);//它的返回結果依然是一個對象
dump($res->id);//得到此時的自增id
dump($res);
}
}
namespace app\index\controller;
use think\Controller;
use app\index\model\User;//使用咱們剛剛新建的那個模型
//use think\Loader;//經過引入think下的Loader類
class Index extends Controller
{
public function index()
{
// $res = User::create([
// 'username' => 'shulv',
// 'password' => md5('shulv'),
// 'email' => 'shulv@qq.com',
// 'num' => 100
// 'demo' => 15245
// ]);//當咱們要插入一個表中並無的字段時,不傳遞第二個參數時,運行時會報錯,說該字段不存在(此時數據庫也並不會插入這條數據)
/*
咱們知道,當前臺傳遞數據時,每每直接傳到$_POST中,咱們會將POST中全部的數據插入到表中,那麼咱們就應該讓它有的就插入表中,沒有的就不插入,此時咱們就須要給它傳入第二個參數"true"
*/
$res = User::create([
'username' => 'shulv1',
'password' => md5('shulv1'),
'email' => 'shulv1@qq.com',
'num' => 101,
'demo' => 15245
],true);
dump($res);
}
}
當咱們僅容許添加指定的字段時,咱們能夠將第二個參數傳遞一個數組,數組中的元素就是,你指定添加的字段
public function index()
{
$res = User::create([
'username' => 'shulv2',
'password' => md5('shulv2'),
'email' => 'shulv2@qq.com',
'num' => 102,
'demo' => 145
],['username','password']);
dump($res->id);
}
模型中還爲咱們提供了一個save()方法來插入數據(若是咱們想使用save()就要先實例化咱們的模型)
namespace app\index\controller;
use think\Controller;
use app\index\model\User;//使用咱們剛剛新建的那個模型
//use think\Loader;//經過引入think下的Loader類
class Index extends Controller
{
public function index()
{
$userModel = new User;
//那麼此時,咱們就能夠對數據庫進行添加的操做了
$userModel->username = 'shulv3';
$userModel->password = md5('shulv3');
$userModel->email = 'shulv3@qq.com';
$userModel->num = 103;
$userModel->save();
dump($userModel->id);
}
}
固然咱們還可使用一種簡單的方法,就是直接給save()傳遞一個數組,數組的元素爲插入的值,就像create()方法傳遞參數那樣。不同的地方是,但碰見插入的字段,表中不存在的時候,它的處理方法是調用一個allowField()方法
$userModel = new User;
$res = $userModel
->allowField(true)
->save([//allowField('true')也能夠傳遞一個數組,元素爲容許插入的字段名
'username' => 'shulv4',
'password' => md5('shulv5'),
'email' => 'shulv@qq.com',
'demo' => 123
]);//返回值是插入的條數
dump($res);
那麼當咱們想要添加多條記錄時,咱們可使用saveAll()方法,參數爲一個二維數組
public function index()
{
$userModel = new User;
$res = $userModel->saveAll([
['email' => 'shulv4@qq.com'],
['email' => 'shulv5@qq.com']
]);//我這裏只插入了email
foreach($res as $value){
dump($value->id);
//dump($value->toArray());
}
}
1、使用模型更新數據
若是咱們想更新數據,咱們在前邊說Db類的時候咱們知道,咱們須要一些where條件,我直接用代碼演示:
public function index()
{
$res = User::update([//經過Model的update()方法,傳遞一個數組進行數據更新
'username' => '3404767031'
], ['id'=>80]);//表示更新id=80的這條記錄的username
dump($res);
}
第二個參數,還能夠支持一個閉包函數,和以前說的同樣:
public function index()
{
$res = User::update([//經過Model的update()方法,傳遞一個數組進行數據更新
'username' => 'test'
], function($query){
$query->where("id", "<", 85);
});//表示更新id<85的這幾條記錄的username
dump($res);
//固然能夠直接經過咱們以前說的那種,鏈式操做的方式來構造where條件:User::where()->update();
其實,咱們還有一種更新數據的方法,那就是,現獲取到某條記錄,而後再對記錄中的字段進行修改:
$userModel = User::get(80);
$userModel->username = 'shulv_80';
$res = $userModel->save();
dump($res);
咱們還能夠經過saveAll()的方法進行批量更新數據
$userModel = new User;
$res = $userModel->saveAll([
['id'=>88, 'username' => 'hhhhhh'],
['id'=>89, 'username' => 'tttttt']
]);
dump($res);
2、使用模型刪除數據
//Model類爲咱們提供了destory()靜態方法來刪除數據
$res = User::destroy(79);//若是表中存在主鍵,咱們能夠直接傳遞主鍵進行刪除
dump($res);
//Model類爲咱們提供了destory()靜態方法來刪除數據
// $res = User::destroy(79);//若是表中存在主鍵,咱們能夠直接傳遞主鍵進行刪除
// dump($res);
$res = User::destroy(['id'=>80]);//也能夠經過傳遞數組的方式
$res = User::destroy(function($query){
$query->where("id", "<", 85);
});//還能夠經過閉包函數的方式
//經過先獲取到執行的記錄,而後進行刪除
$userModel = User::get(90);
$res = $userModel->delete();
//添加where條件進行刪除
$res = User::where("id", "=", 91)->delete();//返回值是刪除的記錄數
//刪除全部數據 $res = User::where("1=1")->delete();
dump($res);
3、模型聚合操做
在thinkphp的model中爲咱們提供了很方便獲取平均值、最大值、最小值、數據條數的方法。下邊就用代碼演示這些函數的用法:
$res1 = User::count();//獲取總記錄數
dump($res1);
$res2 = User::where("id", ">", 90)//獲取id>90的記錄數
->count();
dump($res2);
$res3 = User::max('num');//獲取num字段中的最大值
dump($res3);
$res4 = User::where("id", ">", 90)//獲取id>90的記錄中num值最大的那個記錄
->max('num');
dump($res4);
//其他的還有sum(),avg()等方法的使用都是這樣,就不一一寫了
4、模型獲取器
在咱們的實際應用中,咱們常常會存在這樣的場景,好比說咱們存用戶的性別,咱們可能在數據庫中只使用0,1,20或1,2,3的方式來存用戶的性別。0:男1:女2:未知。咱們在數據庫中存的是0,1,2而咱們在頁面中展現的是男女和未知,那麼在獲取這個數據的過程當中,咱們就使用獲取器,能夠自動的將咱們的數據轉化成咱們想要的顯示格式
app/index/model/User.php
<?php
namespace app\index\model;
use think\Model;
class User extends Model{
public function getSexAttr($val){//注意這個方法名的寫法是固定的
//dump($val);//咱們在這裏先直接打印這個$val,能夠看到輸出的是int(0),也就是說咱們能直接獲取到咱們當前獲取的數據的value值,那麼咱們就能夠對其進行判斷
switch ($val) {
case '1':
return "男";
break;
case '2':
return "女";
break;
default:
return "未知";
break;
}
}//寫完這個函數以後,剛纔輸出的0,如今就會輸出 未知
app/index/controller/Index.php
<?php
namespace app\index\controller;
use think\Controller;
use app\index\model\User;//使用咱們剛剛新建的那個模型
//use think\Loader;//經過引入think下的Loader類
class Index extends Controller
{
public function index()
{
//首先咱們先看一下怎麼常規的獲取到一條記錄的sex字段值
$res = User::get(80);
echo $res->sex;
//那麼如今咱們若是想讓0:未知 1:男 2:女 那麼這個時候咱們須要在模型中編寫一個函數
dump($res->toArray());//此時會打印出id=80的這條記錄,咱們會發現sex顯示的是 未知
//那麼,若是咱們想獲取到原始數據,可使用getData()
dump($res->getData());
}
1、模型修改器+自動完成
將這兩個放在一塊兒學習,是由於這兩個有不少的類似之處,下邊看一下修改器
控制器Index.php
public function index()
{
//首先咱們向數據庫中添加一條數據
$res = User::create([
'username' => 'liangyu',
'password' => 'liangyu',//注意,我這裏沒有對其加密,後邊我會讓它自動完成修改,也就是要說的修改器
'email' => 'liangyu@qq.com',
'num' => 520
]);
dump($res->id);
//密碼沒有加密,那麼咱們如何經過修改器讓它進行自動的修改,這個時候就須要在模型中編寫代碼
}
模型User.php
public function setPasswordAttr($val){
return md5($val);//定義了這個方法以後,它會自動傳遞password給$val
//這個時候,當咱們再進行數據插入的時候,它就會對密碼自動的進行加密
}
//固然咱們還能夠傳遞第二個參數$data,它接收的是,咱們插入的這條數據的全部元素(以前說的那個自動將0在輸出的時候打印爲「未知」,那個getSexAttr的方法,也能夠傳遞第二個參數$data,表示的含義是同樣的)
public function setPasswordAttr($val, $data){
return $val . $data['email'];//這個就表示,將密碼+email總體當作最終密碼,固然還能夠對總體進行加密,這樣更安全
//return md5($val . $data['email']);
}
而後咱們說一下自動完成:
控制器Index.php
public function index()
{
//首先咱們向數據庫中添加一條數據
$res = User::create([
'username' => 'liangyu',
'password' => 'liangyu',
'email' => 'liangyu@qq.com',
'num' => 520
]);
dump($res->id);
}
模型User.php
//若是想使用自動完成,那麼就須要在模型中聲明
protected $auto = [//數組中的內容,就是咱們要自動的字段
'time'
];//這個$auto是在咱們進行數據添加和更新的時候都會發生變化,都會發生的操做。它的方法和咱們的修改器是同樣的
public function setTimeAttr(){//首先,咱們修改一下本身的數據庫,在咱們的數據庫中添加一個time字段
return time();
}//這個時候,刷新數據庫,新添加的這條數據就有了time()
控制器:Index.php
public function index()
{
//首先咱們向數據庫中添加一條數據
$res = User::create([
'username' => 'liangyu',
'password' => 'liangyu',
'email' => 'liangyu@qq.com',
'num' => 520
]);
// $userModel = new User;
// $userModel->get(101);
// $userModel->sex = 1;
// $res = $userModel->save();
dump($res->id);
}
模型User.php
namespace app\index\model;
use think\Model;
class User extends Model{
//若是想使用自動完成,那麼就須要在模型中聲明
protected $auto = [//數組中的內容,就是咱們要自動的字段
'time'
];//這個$auto是在咱們進行數據添加和更新的時候都會發生變化,都會發生的操做。它的方法和咱們的修改器是同樣的
//另外,咱們還能夠聲明一個$insert數組,這個數組,它只在數據插入的時候有效
protected $insert = [
'time_insert'
];
//一樣咱們還能夠添加$update數組,它是在數據更新的時候發生改變
$protected $update = [//咱們在數據庫中首先使用以前學過的修改記錄的值,進行修改,那麼該修改器會被自動調用
'time_update'
];
public function setTimeAttr(){//首先,咱們修改一下本身的數據庫,在咱們的數據庫中添加一個time字段
return time();
}//這個時候,刷新數據庫,新添加的這條數據就有了time()
public function setTimeInsertAttr(){//這個時候,咱們先去給咱們的數據表添加一個time_insert字段
return time();//插入時間
}//這樣咱們再刷新一下咱們的頁面,機會看見數據表中新添的記錄中有了time_insert值
public function setTimeUpdateAttr(){
return time();
}
//這種修改器函數的命名是set+字段名(駝峯命名法)+Attr()
}
2、模型時間戳+軟刪除
時間戳是作什麼使用的呢?
好比說咱們對數據進行新增或更新的操做時,咱們每每須要在數據庫中記錄咱們的插入時間和更改時間。經過前邊的知識咱們知道,咱們可使用自動完成的方式來實現這個功能,可是,咱們大多數的數據庫會有這樣的字段,若是咱們每個都要這樣去編寫這樣自動更新或自動完成的操做,那麼咱們的代碼就會變得很臃腫,咱們實現起來也不是很方便。因此thinkphp的model類,爲咱們提供了自動的時間戳功能,它會將咱們數據的更新時間和建立時間記錄到數據庫中。
軟刪除
在咱們對數據進行刪除的操做,咱們每每不會將這條數據真正的從數據庫中刪除,而是將它的某一個字段設置爲一個特定的值,表明這個字段已經被刪除,或者表明這條記錄已經被刪除。下邊用代碼演示一下:
首先我會將以前的那個表刪除掉,從新建立一個表,由於以前的表字段太多,模擬起來不方便(表名不變)
表結構
控制器:Index.php
namespace app\index\controller;
use think\Controller;
use app\index\model\User;//使用咱們剛剛新建的那個模型
//use think\Loader;//經過引入think下的Loader類
class Index extends Controller
{
public function index()
{
//第一種方式
//若是咱們想全局的開啓添加時間和更新時間,時間戳的自動完成這個操做,那麼咱們能夠修改配置文件(database.php中找到auto_timestamp,將它的值設置爲true)
//而後咱們如今開始插入數據
$res = User::create([
'name' => 'shulv',
'password' => md5('liangyu')
]);
dump($res);//當咱們刷新頁面的時候就會發現,create_time和update_time字段已經有值了
//可是,並非全部的表都有新增時間和更新時間的字段,因此通常狀況下不建議直接在配置文件中進行修改。由於它若是修改的話,若是你的一個表中不存在時間戳的字段,那麼程序可能就會報錯,因此咱們仍是將它設置爲false
//這個時候咱們進入對應的模型(User.php)中添加一個屬性$autoWriteTimestamp
//咱們再進行一下修改的操做
$user = User::get(0);
$user->name = 'liangyu';
$res = $user->save();
dump($res);//此時咱們會發現數據庫中id=0的這條記錄的兩個時間戳字段的值也發生了更新
/*
如今有一個問題就是,若是咱們數據庫中的兩個時間戳字段名不是create_time和update_time,好比說我給它們改爲create_at和update_at,那麼此時咱們再刷新的時候,他就會報錯,說update_time和update_time不存在。由於咱們在使用配置的時候,它的默認配置就是update_time,因此咱們須要在model中繼續申請一個屬性
*/
}
}
模型:User.php
namespace app\index\model;
use think\Model;
class User extends Model{
protected $autoWriteTimestamp = true;//利用這個屬性來開啓時間戳功能。這個時候咱們再更新頁面,那兩個時間戳字段也會被添加進去
protected $createTime = 'create_at';//這裏直接寫上咱們的字段名
protected $updateTime = 'update_at';//這個時候咱們再刷新瀏覽器就不會報錯了,時間也會被更新
//固然咱們還能夠將$createTime或$updateTime設置爲false,即是關閉該項,那麼它會使用默認值(0),就不會去更新這個字段了
}
軟刪除
控制器:Index.php
namespace app\index\controller;
use think\Controller;
use app\index\model\User;//使用咱們剛剛新建的那個模型
//use think\Loader;//經過引入think下的Loader類
class Index extends Controller
{
public function index()
{
//在說軟刪除的時候,咱們先向數據庫中添加一個delete_time字段(容許爲null,null表明這條數據沒有被刪除)
//若是咱們想使用軟刪除,咱們須要在模型中引入traits\modelSoftDelete(去看模型User.php)
//在模型中引入traits\modelSoftDelete以後,就能夠直接在控制器中進行刪除了
$res = User::destroy(0);
dump($res);
//這個時候咱們在數據表中能夠看見id=0的字段並無被真正的刪除,它的delete_time字段名變成了時間戳,說明這條數據在指定的時間點被刪除(軟刪除)。這個時候當你嘗試獲取剛剛被軟刪除的記錄時,返回的是null,也就是說不能再獲取了。若是你確實須要獲取到咱們包含軟刪除的這些記錄,那麼咱們就可使用下邊這樣的方式
$res = User::withTrashed(true)->find(0);//這個時候就能獲得了
dump($res);
dump($res->getData());//能夠獲取原始數據
//還有一種方式就是,好比說咱們軟刪除的記錄都放在了垃圾箱裏,那麼咱們要獲取這些被軟刪除的數據,就能夠這樣
$res = User::onlyTrashed()->select();//返回的是數組,咱們能夠經過foreach遍歷
dump($res);
//那麼,若是咱們想對數據進行恢復,能夠直接使用update方法,將delete_time的值設置爲null便可
//當咱們不想軟刪除這個字段名爲delete_time時,咱們仍是使用更改create_time那種在model中聲明變量的方式進行更改
//那麼若是咱們真正的想刪除某一個記錄時應該怎麼作呢?
$res = User::destroy(0, true);//傳遞第二個參數爲true便可
dump($res);
//還能夠經過先獲取,再刪除的方式
$user = User::get(0);
$res = $user->delete(0, true);
dump($res);
}
}
模型:User.php
namespace app\index\model;
use think\Model;
use traits\modelSoftDelete;
class User extends Model{
use SoftDelete;
protected $autoWriteTimestamp = true;//利用這個屬性來開啓時間戳功能。這個時候咱們再更新頁面,那兩個時間戳字段也會被添加進去
protected $createTime = 'create_at';//這裏直接寫上咱們的字段名
protected $updateTime = 'update_at';//這個時候咱們再刷新瀏覽器就不會報錯了,時間也會被更新
//固然咱們還能夠將$createTime或$updateTime設置爲false,即是關閉該項,那麼它會使用默認值(0),就不會去更新這個字段了
protected $updateTime = 'delete_at';
}