模型提供了一種與數(shù)據(jù)庫中的特定表進行交互的方式。它們帶有幫助程序方法的開箱即用,可用于與數(shù)據(jù)庫表進行交互的許多標準方式,包括查找記錄,更新記錄,刪除記錄等。
模型通常存儲在app/Models
目錄中。它們應(yīng)具有與其目錄中的位置匹配的名稱空間,例如。namespace App\Models
您可以通過創(chuàng)建新實例或使用model()
幫助程序功能來訪問類中的模型。
// Create a new class manually
$userModel = new \App\Models\UserModel();
// Create a new class with the model function
$userModel = model('App\Models\UserModel', false);
// Create a shared instance of the model
$userModel = model('App\Models\UserModel');
// Create shared instance with a supplied database connection
// When no namespace is given, it will search through all namespaces
// the system knows about and attempt to located the UserModel class.
$db = db_connect('custom');
$userModel = model('UserModel', true, $db);
CodeIgniter確實提供了一個模型類,該類提供了一些不錯的功能,包括:
此類為構(gòu)建自己的模型提供了堅實的基礎(chǔ),使您可以快速構(gòu)建應(yīng)用程序的模型層。
要利用CodeIgniter的模型,您只需創(chuàng)建一個擴展了以下內(nèi)容的新模型類CodeIgniter\Model
:
<?php namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
}
這個空類提供了對數(shù)據(jù)庫連接,查詢生成器和許多其他便利方法的便捷訪問。
首次實例化該類時,如果沒有數(shù)據(jù)庫連接實例傳遞給構(gòu)造函數(shù),則它將自動連接到配置中設(shè)置的默認數(shù)據(jù)庫組。您可以通過將DBGroup屬性添加到類來修改每個模型使用的組。這樣可以確保在模型內(nèi)$this->db
通過適當?shù)倪B接進行任何引用。
<?php namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $DBGroup = 'group_name';
}
您可以使用數(shù)據(jù)庫配置文件中定義的數(shù)據(jù)庫組的名稱替換“ group_name”。
模型類具有一些配置選項,可以對其進行設(shè)置,以允許類的方法為您無縫地工作。所有CRUD方法都使用前兩個方法來確定要使用的表以及如何查找所需的記錄:
<?php namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $table = 'users';
protected $primaryKey = 'id';
protected $returnType = 'array';
protected $useSoftDeletes = true;
protected $allowedFields = ['name', 'email'];
protected $useTimestamps = false;
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
}
$table
指定此模型主要使用的數(shù)據(jù)庫表。這僅適用于內(nèi)置CRUD方法。您不限于在自己的查詢中僅使用此表。
$primaryKey
這是唯一標識此表中記錄的列的名稱。這不一定必須與數(shù)據(jù)庫中指定的主鍵匹配,而是與諸如find()
知道要將指定值匹配到哪一列的方法一起使用。
注解
所有模型都必須指定一個primaryKey,以允許所有功能按預(yù)期工作。
$ returnType
該模型的CRUD方法將使您遠離工作,并自動返回結(jié)果數(shù)據(jù),而不是Result對象。此設(shè)置使您可以定義返回的數(shù)據(jù)類型。有效值是“數(shù)組”,“對象”或可以與Result對象的getCustomResultObject()方法一起使用的類的完全限定名稱。
$ useSoftDeletes
如果為true,則將deleted_at
在數(shù)據(jù)庫中設(shè)置所有delete 方法調(diào)用,而不是實際刪除該行。當可以在其他地方引用數(shù)據(jù)時,這可以保留數(shù)據(jù),也可以維護可以恢復(fù)的對象的“回收站”,甚至可以將其保留為安全跟蹤的一部分。如果為true,則除非在調(diào)用find 方法之前調(diào)用withDeleted()方法,否則find *方法將僅返回未刪除的行。
根據(jù)模型的$ dateFormat設(shè)置,這需要數(shù)據(jù)庫中的DATETIME或INTEGER字段。但是,默認字段名稱是deleted_at
此名稱,可以使用$ deletedField屬性將該名稱配置為您選擇的任何名稱。
$ allowedFields
應(yīng)該使用可以在保存,插入或更新方法期間設(shè)置的字段名稱來更新此數(shù)組。除此以外的任何字段名稱都將被丟棄。這有助于防止僅從表單中獲取輸入并將其全部扔給模型,從而導致潛在的批量分配漏洞。
$ useTimestamps
此布爾值確定是否將當前日期自動添加到所有插入和更新中。如果為true,將以$ dateFormat指定的格式設(shè)置當前時間。這要求表在適當?shù)臄?shù)據(jù)類型中具有名為“ created_at”和“ updated_at”的列。
$ createdField
指定用于保留數(shù)據(jù)記錄創(chuàng)建時間戳的數(shù)據(jù)庫字段。將其保留為空以避免更新(即使啟用了useTimestamps)
$ updatedField
指定應(yīng)使用哪個數(shù)據(jù)庫字段來保留數(shù)據(jù)記錄更新時間戳。將其保留為空以避免更新(即使啟用了useTimestamps)
$ dateFormat
該值與$ useTimestamps和$ useSoftDeletes一起使用,以確保將正確類型的日期值插入數(shù)據(jù)庫中。默認情況下,這將創(chuàng)建DATETIME值,但有效的選項為:datetime,date或int(PHP時間戳)。在無效或缺少dateFormat的情況下使用“ useSoftDeletes”或“ useTimestamps”會導致異常。
$ validationRules
包含如如何保存規(guī)則中所述的驗證規(guī)則數(shù)組, 或包含同一部分中所述的包含驗證組名稱的字符串。在下面更詳細地描述。
$ validationMessages
包含在驗證期間應(yīng)使用的自定義錯誤消息數(shù)組,如設(shè)置自定義錯誤消息中所述。在下面更詳細地描述。
$ skipValidation
是否在全部inserts
和期間跳過驗證updates
。默認值為false,表示將始終嘗試驗證數(shù)據(jù)。該方法主要使用該skipValidation()
方法,但可能會更改為該方法,true
因此該模型將永遠無法驗證。
$ beforeInsert $ afterInsert $ beforeUpdate $ afterUpdate $ afterFind $ afterDelete
這些數(shù)組允許您指定將在屬性名稱中指定的時間在數(shù)據(jù)上運行的回調(diào)方法。
提供了一些用于在表上執(zhí)行基本CRUD工作的功能,包括find(),insert(),update(),delete()等。
find()
返回一行,其中主鍵與作為第一個參數(shù)傳入的值匹配:
$user = $userModel->find($user_id);
該值以$ returnType中指定的格式返回。
您可以通過傳遞primaryKey值而不是一個數(shù)組來指定要返回的一行以上:
$users = $userModel->find([1,2,3]);
如果未傳入任何參數(shù),則將返回該模型表中的所有行,盡管不那么顯式,但實際上類似于findAll()。
findColumn()
返回null或列值的索引數(shù)組:
$user = $userModel->findColumn($column_name);
$ column_name應(yīng)該是單列的名稱,否則您將獲得DataException。
findAll()
返回所有結(jié)果:
$users = $userModel->findAll();
在調(diào)用此方法之前,可以根據(jù)需要插入查詢生成器命令來修改此查詢:
$users = $userModel->where('active', 1)
->findAll();
您可以分別將極限值和偏移值作為第一個和第二個參數(shù)傳遞:
$users = $userModel->findAll($limit, $offset);
first()
返回結(jié)果集中的第一行。最好與查詢構(gòu)建器結(jié)合使用。
$user = $userModel->where('deleted', 0)
->first();
withDeleted()
如果$ useSoftDeletes為true,則find 方法將不返回“ deleted_at IS NOT NULL”的任何行。要臨時重寫此方法,可以在調(diào)用find 方法之前使用withDeleted()方法。
// Only gets non-deleted rows (deleted = 0)
$activeUsers = $userModel->findAll();
// Gets all rows
$allUsers = $userModel->withDeleted()
->findAll();
onlyDeleted()
鑒于withDeleted()將同時返回已刪除和未刪除的行,此方法將修改下一個find *方法以僅返回軟刪除的行:
$deletedUsers = $userModel->onlyDeleted()
->findAll();
insert()
關(guān)聯(lián)的數(shù)據(jù)數(shù)組作為唯一參數(shù)傳遞到此方法中,以在數(shù)據(jù)庫中創(chuàng)建新的數(shù)據(jù)行。數(shù)組的鍵必須與$ table中的列名匹配,而數(shù)組的值是要為該鍵保存的值:
$data = [
'username' => 'darth',
'email' => 'd.vader@theempire.com'
];
$userModel->insert($data);
update()
更新數(shù)據(jù)庫中的現(xiàn)有記錄。第一個參數(shù)是要更新的記錄的$ primaryKey。數(shù)據(jù)的關(guān)聯(lián)數(shù)組作為第二個參數(shù)傳遞到此方法中。數(shù)組的鍵必須與$ table中的列名匹配,而數(shù)組的值是要為該鍵保存的值:
$data = [
'username' => 'darth',
'email' => 'd.vader@theempire.com'
];
$userModel->update($id, $data);
通過傳遞主鍵數(shù)組作為第一個參數(shù),可以通過一次調(diào)用更新多個記錄:
$data = [
'active' => 1
];
$userModel->update([1, 2, 3], $data);
當您需要更靈活的解決方案時,可以將參數(shù)留空,其功能類似于Query Builder的update命令,并具有驗證,事件等附加優(yōu)點:
$userModel
->whereIn('id', [1,2,3])
->set(['active' => 1])
->update();
save()
這是對insert()和update()方法的包裝,這些方法根據(jù)是否找到與$ primaryKey值匹配的數(shù)組鍵來自動處理插入或更新記錄:
// Defined as a model property
$primaryKey = 'id';
// Does an insert()
$data = [
'username' => 'darth',
'email' => 'd.vader@theempire.com'
];
$userModel->save($data);
// Performs an update, since the primary key, 'id', is found.
$data = [
'id' => 3,
'username' => 'darth',
'email' => 'd.vader@theempire.com'
];
$userModel->save($data);
save方法還可以通過識別非簡單對象并將其公共值和受保護的值捕獲到數(shù)組中,然后將其傳遞給適當?shù)膇nsert或update方法,從而使自定義類結(jié)果對象的使用變得更加簡單。這使您可以非常干凈地使用Entity類。實體類是表示對象類型的單個實例的簡單類,例如用戶,博客文章,工作等。此類負責維護圍繞對象本身的業(yè)務(wù)邏輯,例如以某種方式格式化元素等。他們對如何將其保存到數(shù)據(jù)庫一無所知。最簡單地說,它們可能如下所示:
namespace App\Entities;
class Job
{
protected $id;
protected $name;
protected $description;
public function __get($key)
{
if (property_exists($this, $key))
{
return $this->$key;
}
}
public function __set($key, $value)
{
if (property_exists($this, $key))
{
$this->$key = $value;
}
}
}
一個非常簡單的模型可能類似于:
use CodeIgniter\Model;
class JobModel extends Model
{
protected $table = 'jobs';
protected $returnType = '\App\Entities\Job';
protected $allowedFields = [
'name', 'description'
];
}
此模型使用jobs
表中的數(shù)據(jù),并以的實例返回所有結(jié)果App\Entities\Job
。當您需要將該記錄保存到數(shù)據(jù)庫中時,您將需要編寫自定義方法,或者使用模型的save()
方法來檢查類,獲取所有公共和私有屬性并將它們保存到數(shù)據(jù)庫中:
// Retrieve a Job instance
$job = $model->find(15);
// Make some changes
$job->name = "Foobar";
// Save the changes
$model->save($job);
注解
如果您發(fā)現(xiàn)自己經(jīng)常使用實體,則CodeIgniter提供了一個內(nèi)置的Entity class ,該類提供了一些方便的功能,這些特性使開發(fā)實體變得更加簡單。
delete()
將主鍵值作為第一個參數(shù),并從模型的表中刪除匹配的記錄:
$userModel->delete(12);
如果模型的$ useSoftDeletes值為true,則將更新該行以將其設(shè)置deleted_at
為當前日期和時間。您可以通過將第二個參數(shù)設(shè)置為true來強制永久刪除。
可以將一個主鍵數(shù)組作為第一個參數(shù)傳入,以一次刪除多個記錄:
$userModel->delete([1,2,3]);
如果沒有傳入任何參數(shù),將像查詢生成器的delete方法一樣,之前需要進行where調(diào)用:
$userModel->where('id', 12)->delete();
purgeDeleted()
通過永久刪除所有具有“ deleted_at IS NOT NULL”的行來清除數(shù)據(jù)庫表。
$userModel->purgeDeleted();
對于許多人來說,驗證模型中的數(shù)據(jù)是確保數(shù)據(jù)保持單一標準而不重復(fù)代碼的首選方法。模型類提供了一個方法可以自動保存到數(shù)據(jù)庫中現(xiàn)有經(jīng)確認的所有數(shù)據(jù)insert()
,update()
或save()
方法。
第一步是$validationRules
使用應(yīng)該應(yīng)用的字段和規(guī)則填充class屬性。如果您有要使用的自定義錯誤消息,請將它們放在$validationMessages
數(shù)組中:
class UserModel extends Model
{
protected $validationRules = [
'username' => 'required|alpha_numeric_space|min_length[3]',
'email' => 'required|valid_email|is_unique[users.email]',
'password' => 'required|min_length[8]',
'pass_confirm' => 'required_with[password]|matches[password]'
];
protected $validationMessages = [
'email' => [
'is_unique' => 'Sorry. That email has already been taken. Please choose another.'
]
];
}
通過功能將驗證消息設(shè)置為字段的另一種方法,
setValidationMessage
($ field,$ fieldMessages )
參數(shù): | $ field(string)– |
---|---|
$ fieldMessages(array)- |
此功能將設(shè)置現(xiàn)場智能錯誤消息。
用法示例:
$fieldName = 'name';
$fieldValidationMessage = [
'required' => 'Your name is required here',
];
$model->setValidationMessage($fieldName, $fieldValidationMessage);
setValidationMessages
($ fieldMessages )
參數(shù): | $ fieldMessages(array)- |
---|---|
此功能將設(shè)置現(xiàn)場消息。
用法示例:
$fieldValidationMessage = [
'name' => [
'required' => 'Your baby name is missing.',
'min_length' => 'Too short, man!',
],
];
$model->setValidationMessages($fieldValidationMessage);
現(xiàn)在,只要您使用insert()
,update()
或save()
方法,數(shù)據(jù)將被驗證。如果失敗,則模型將返回boolean false。您可以使用該errors()
方法來檢索驗證錯誤:
if ($model->save($data) === false)
{
return view('updateUser', ['errors' => $model->errors()];
}
這將返回一個包含字段名稱及其相關(guān)錯誤的數(shù)組,這些數(shù)組可用于在表單頂部顯示所有錯誤,也可以單獨顯示它們:
<?php if (! empty($errors)) : ?>
<div class="alert alert-danger">
<?php foreach ($errors as $field => $error) : ?>
<p><?= $error ?></p>
<?php endforeach ?>
</div>
<?php endif ?>
如果您希望在Validation配置文件中組織規(guī)則和錯誤消息,則可以這樣做,只需將其設(shè)置$validationRules
為創(chuàng)建的驗證規(guī)則組的名稱即可:
class UserModel extends Model
{
protected $validationRules = 'users';
}
您可以通過訪問模型的validationRules
屬性來檢索模型的驗證規(guī)則:
$rules = $model->validationRules;
您還可以通過直接調(diào)用帶有選項的accessor方法來僅檢索這些規(guī)則的子集:
$rules = $model->getValidationRules($options);
該$options
參數(shù)是一個帶有一個元素的關(guān)聯(lián)數(shù)組,其鍵為“ except”或“ only”,并且其值為一個感興趣的字段名數(shù)組。
// get the rules for all but the "username" field
$rules = $model->getValidationRules(['except' => ['username']]);
// get the rules for only the "city" and "state" fields
$rules = $model->getValidationRules(['only' => ['city', 'state']]);
該模型提供了一種簡單的方法,可以根據(jù)傳遞給規(guī)則的數(shù)據(jù)替換規(guī)則的某些部分。這聽起來很晦澀,但是對于is_unique
驗證規(guī)則來說尤其方便。占位符只是作為$ data傳入的字段(或數(shù)組鍵)的名稱,并用大括號括起來。它將替換為匹配的傳入字段的值。一個例子應(yīng)該澄清這一點:
protected $validationRules = [
'email' => 'required|valid_email|is_unique[users.email,id,{id}]'
];
在這套規(guī)則中,它聲明電子郵件地址在數(shù)據(jù)庫中應(yīng)該是唯一的,除了ID與占位符值匹配的行之外。假設(shè)表單POST數(shù)據(jù)具有以下內(nèi)容:
$_POST = [
'id' => 4,
'email' => 'foo@example.com'
];
然后將{id}
占位符替換為數(shù)字4,從而給出此修改后的規(guī)則:
protected $validationRules = [
'email' => 'required|valid_email|is_unique[users.email,id,4]'
];
因此,它將在id=4
驗證電子郵件唯一時忽略數(shù)據(jù)庫中具有該行的行。
只要您確保傳入的任何動態(tài)鍵都不會與表單數(shù)據(jù)沖突,這也可以用于在運行時創(chuàng)建更多動態(tài)規(guī)則。
為了防止大規(guī)模分配攻擊,Model類要求您在$allowedFields
class屬性中列出所有在插入和更新期間可以更改的字段名稱。除這些數(shù)據(jù)外,在訪問數(shù)據(jù)庫之前,還將刪除所有提供的數(shù)據(jù)。這對于確保不更改時間戳或主鍵非常有用。
protected $allowedFields = ['name', 'email', 'address'];
有時,您會發(fā)現(xiàn)需要更改這些元素的時間。這通常是在測試,遷移或種子期間。在這些情況下,您可以打開或關(guān)閉保護:
$model->protect(false)
->insert($data)
->protect(true);
您可以在需要時隨時訪問該模型的數(shù)據(jù)庫連接的查詢生成器的共享實例:
$builder = $userModel->builder();
該構(gòu)建器已使用模型的$ table設(shè)置。
您還可以在同一鏈接調(diào)用中使用查詢生成器方法和模型的CRUD方法,從而可以非常優(yōu)雅地使用:
$users = $userModel->where('status', 'active')
->orderBy('last_login', 'asc')
->findAll();
注解
您還可以無縫訪問模型的數(shù)據(jù)庫連接:
$user_name = $userModel-&escape($name);
您可以指定將find *()方法用作類屬性$ returnType時應(yīng)返回數(shù)據(jù)的格式。但是,有時候您可能希望數(shù)據(jù)以其他格式返回。該模型提供了允許您執(zhí)行此操作的方法。
注解
這些方法僅更改下一個find *()方法調(diào)用的返回類型。之后,將其重置為其默認值。
asArray()
從下一個find *()方法作為關(guān)聯(lián)數(shù)組返回數(shù)據(jù):
$users = $userModel->asArray()->where('status', 'active')->findAll();
asObject()
從下一個find *()方法返回數(shù)據(jù)作為標準對象或自定義類實例:
// Return as standard objects
$users = $userModel->asObject()->where('status', 'active')->findAll();
// Return as custom class instances
$users = $userModel->asObject('User')->where('status', 'active')->findAll();
有時,您需要處理大量數(shù)據(jù),并且存在內(nèi)存不足的風險。為了簡化操作,您可以使用chunk()方法獲取較小的數(shù)據(jù)塊,然后再進行處理。第一個參數(shù)是單個塊中要檢索的行數(shù)。第二個參數(shù)是一個Closure,將為每一行數(shù)據(jù)調(diào)用。
最好在cronjobs,數(shù)據(jù)導出或其他大型任務(wù)期間使用。
$userModel->chunk(100, function ($data)
{
// do something.
// $data is a single row of data.
});
您可以在模型的執(zhí)行中指定多個要運行的回調(diào)方法。這些方法可用于規(guī)范化數(shù)據(jù),哈希密碼,保存相關(guān)實體等等??梢酝ㄟ^類屬性來影響模型執(zhí)行中的以下幾點:$ beforeInsert,$ afterInsert, $ beforeUpdate,afterUpdate,afterFind和afterDelete。
通過首先在模型中創(chuàng)建要使用的新類方法來指定回調(diào)。此類將始終收到$ data數(shù)組作為其唯一參數(shù)。$ data數(shù)組的確切內(nèi)容在事件之間會有所不同,但始終包含一個名為data的鍵,該鍵包含傳遞給原始方法的主要數(shù)據(jù)。對于insert 或update 方法,這將是要插入數(shù)據(jù)庫中的鍵/值對。主數(shù)組還將包含傳遞給該方法的其他值,稍后將進行詳細說明?;卣{(diào)方法必須返回原始的$ data數(shù)組,以便其他回調(diào)具有完整的信息。
protected function hashPassword(array $data)
{
if (! isset($data['data']['password']) return $data;
$data['data']['password_hash'] = password_hash($data['data']['password'], PASSWORD_DEFAULT);
unset($data['data']['password'];
return $data;
}
通過將方法名稱添加到適當?shù)念悓傩裕╞eforeInsert,afterUpdate等)中,可以指定何時運行回調(diào)??梢詫⒍鄠€回調(diào)添加到單個事件中,并將它們依次處理。您可以在多個事件中使用相同的回調(diào):
protected $beforeInsert = ['hashPassword'];
protected $beforeUpdate = ['hashPassword'];
由于傳遞給每個回調(diào)的確切數(shù)據(jù)略有不同,因此以下是傳遞給每個事件的$ data參數(shù)的詳細信息:
事件 | $數(shù)據(jù)內(nèi)容 |
---|---|
beforeInsert | data =要插入的鍵/值對。如果將對象或?qū)嶓w類傳遞給insert方法,則首先將其轉(zhuǎn)換為數(shù)組。 |
afterInsert | id =新行的主鍵,失敗則為0。 data =要插入的鍵/值對。 result =通過查詢生成器使用的insert()方法的結(jié)果。 |
beforeUpdate | id =要更新的行的主鍵。 data =要插入的鍵/值對。如果將對象或?qū)嶓w類傳遞給insert方法,則首先將其轉(zhuǎn)換為數(shù)組。 |
afterUpdate | id =要更新的行的主鍵。 data =要更新的鍵/值對。 result =通過查詢生成器使用的update()方法的結(jié)果。 |
afterFind | 因find *方法而異。請參閱以下內(nèi)容: |
find() | id =要搜索的行的主鍵。 data =結(jié)果數(shù)據(jù)行;如果未找到結(jié)果,則為null。 |
findAll() | data = data的結(jié)果行;如果未找到結(jié)果,則為null。 limit =查找的行數(shù)。 offset =搜索期間要跳過的行數(shù)。 |
first() | data =在搜索過程中找到的結(jié)果行;如果未找到,則返回null。 |
beforeDelete | 因delete *方法而異。請參閱以下內(nèi)容: |
delete() | id =要刪除的行的主鍵。 purge = boolean是否應(yīng)該硬刪除軟刪除行。 |
afterDelete | 因delete *方法而異。請參閱以下內(nèi)容: |
delete() | id =要刪除的行的主鍵。 purge = boolean是否應(yīng)該硬刪除軟刪除行。 result =查詢生成器上delete()調(diào)用的結(jié)果。 data =未使用。 |
您無需擴展任何特殊的類即可為您的應(yīng)用程序創(chuàng)建模型。您所需要做的就是獲取數(shù)據(jù)庫連接的實例,一切順利。這使您可以繞開CodeIgniter的模型為您提供的即用型功能,并創(chuàng)建完全自定義的體驗。
<?php namespace App\Models;
use CodeIgniter\Database\ConnectionInterface;
class UserModel
{
protected $db;
public function __construct(ConnectionInterface &$db)
{
$this->db =& $db;
}
}
更多建議: