https://blog.csdn.net/self_realian/article/details/78596261
一、什么是模型
為什么我們要在項目中使用模型,其實我們知道,我們可以直接在控制器中進行數據庫的增、刪、改、查,其實已經能基本完成我們的需求,但是,為什么還要有模型的存在呢?
比如說我們現在要做一個用戶注冊的操作,用戶注冊我們可能用兩個表來保存用戶的信息,一個是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;
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進來,通過它的靜態方法來獲取數據,這樣我們的代碼看起來更清晰,我們可以在最上邊就看見我們當前的控制器使用了哪些模型類,這樣在后期維護中會變得很方便
二、通過模型進行數據查詢
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");//返回的是一個數組
三、使用模型添加數據
在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());
}
}
一、使用模型更新數據
如果我們想更新數據,我們在前邊說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);
二、使用模型刪除數據
//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);
三、模型聚合操作
在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()等方法的使用都是這樣,就不一一寫了
四、模型獲取器
在我們的實際應用中,我們經常會存在這樣的場景,比如說我們存用戶的性別,我們可能在數據庫中只使用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());
}
一、模型修改器+自動完成
將這兩個放在一起學習,是因為這兩個有很多的相似之處,下邊看一下修改器
控制器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()
}
二、模型時間戳+軟刪除
時間戳是做什么使用的呢?
比如說我們對數據進行新增或更新的操作時,我們往往需要在數據庫中記錄我們的插入時間和更改時間。通過前邊的知識我們知道,我們可以使用自動完成的方式來實現這個功能,但是,我們大多數的數據庫會有這樣的字段,如果我們每一個都要這樣去編寫這樣自動更新或自動完成的操作,那么我們的代碼就會變得很臃腫,我們實現起來也不是很方便。所以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';
}