CodeIgniter4 使用實(shí)體類(lèi)

2020-08-17 16:32 更新

CodeIgniter支持實(shí)體類(lèi)作為其數(shù)據(jù)庫(kù)層中的一等公民,同時(shí)使它們完全可選使用。它們通常用作存儲(chǔ)庫(kù)模式的一部分,但如果更適合您的需求,則可以直接與模型一起使用。

實(shí)體使用

實(shí)體類(lèi)的核心只是代表單個(gè)數(shù)據(jù)庫(kù)行的類(lèi)。它具有表示數(shù)據(jù)庫(kù)列的類(lèi)屬性,并提供了用于實(shí)現(xiàn)該行的業(yè)務(wù)邏輯的任何其他方法。但是,核心功能是它對(duì)如何持久化一無(wú)所知。這是模型或存儲(chǔ)庫(kù)類(lèi)的責(zé)任。這樣,如果需要保存對(duì)象的方式發(fā)生任何變化,則無(wú)需更改在整個(gè)應(yīng)用程序中使用該對(duì)象的方式。這樣就可以在快速原型制作階段使用JSON或XML文件存儲(chǔ)對(duì)象,然后在證明該概念行得通的情況下輕松地切換到數(shù)據(jù)庫(kù)。

讓我們來(lái)看一個(gè)非常簡(jiǎn)單的用戶(hù)實(shí)體,以及如何使用它來(lái)使事情變得清晰。

假設(shè)您有一個(gè)users具有以下架構(gòu)的數(shù)據(jù)庫(kù)表:

  1. id - integer
  2. username - string
  3. email - string
  4. password - string
  5. created_at - datetime

創(chuàng)建實(shí)體類(lèi)

現(xiàn)在創(chuàng)建一個(gè)新的實(shí)體類(lèi)。由于沒(méi)有默認(rèn)位置可存儲(chǔ)這些類(lèi),并且它不適合現(xiàn)有的目錄結(jié)構(gòu),因此請(qǐng)?jiān)?strong>app / Entities處創(chuàng)建一個(gè)新目錄。在app / Entities / User.php中創(chuàng)建實(shí)體本身。

  1. <?php namespace App\Entities;
  2. use CodeIgniter\Entity;
  3. class User extends Entity
  4. {
  5. //
  6. }

簡(jiǎn)單地說(shuō),這就是您需要做的,盡管我們將在一分鐘內(nèi)使它變得更加有用。

創(chuàng)建模型

首先在app / Models / UserModel.php中創(chuàng)建模型,以便我們可以與之交互:

  1. <?php namespace App\Models;
  2. use CodeIgniter\Model;
  3. class UserModel extends Model
  4. {
  5. protected $table = 'users';
  6. protected $allowedFields = [
  7. 'username', 'email', 'password'
  8. ];
  9. protected $returnType = 'App\Entities\User';
  10. protected $useTimestamps = true;
  11. }

該模型將users數(shù)據(jù)庫(kù)中的表用于其所有活動(dòng)。我們將$allowedFields屬性設(shè)置為包括我們希望外部類(lèi)更改的所有字段。的id,created_atupdated_at字段由類(lèi)或數(shù)據(jù)庫(kù)中自動(dòng)處理的,所以我們不希望改變這些。最后,我們將Entity類(lèi)設(shè)置為$returnType。這確保了模型上從數(shù)據(jù)庫(kù)返回行的所有方法都將返回我們的User Entity類(lèi)的實(shí)例,而不是像通常那樣返回對(duì)象或數(shù)組。

使用實(shí)體類(lèi)

現(xiàn)在所有部分都準(zhǔn)備就緒,您將像其他任何類(lèi)一樣使用Entity類(lèi):

  1. $user = $userModel->find($id);
  2. // Display
  3. echo $user->username;
  4. echo $user->email;
  5. // Updating
  6. unset($user->username);
  7. if (! isset($user->username)
  8. {
  9. $user->username = 'something new';
  10. }
  11. $userModel->save($user);
  12. // Create
  13. $user = new \App\Entities\User();
  14. $user->username = 'foo';
  15. $user->email = 'foo@example.com';
  16. $userModel->save($user);

您可能已經(jīng)注意到,User類(lèi)沒(méi)有為列設(shè)置任何屬性,但是您仍然可以像訪問(wèn)它們一樣將其作為公共屬性來(lái)訪問(wèn)它們?;?lèi)CodeIgniterEntity為您解決了這一問(wèn)題,并提供了使用isset()unset()屬性檢查屬性的能力,并跟蹤自創(chuàng)建或拉出對(duì)象以來(lái)哪些列已更改從數(shù)據(jù)庫(kù)中。

當(dāng)User傳遞給模型的save()方法時(shí),它將自動(dòng)負(fù)責(zé)讀取屬性并將對(duì)模型的$ allowedFields屬性中列出的列的所有更改保存。它還知道是創(chuàng)建新行還是更新現(xiàn)有行。

快速填充屬性

Entity類(lèi)還提供一種方法,fill()該方法允許您將鍵/值對(duì)的數(shù)組推入類(lèi)并填充類(lèi)屬性。數(shù)組中的任何屬性都將在實(shí)體上設(shè)置。但是,在通過(guò)模型進(jìn)行保存時(shí),實(shí)際上僅將$ allowedFields中的字段保存到數(shù)據(jù)庫(kù)中,因此您可以在實(shí)體上存儲(chǔ)其他數(shù)據(jù),而不必?fù)?dān)心會(huì)錯(cuò)誤地保存雜散字段。

  1. $data = $this->request->getPost();
  2. $user = new \App\Entities\User();
  3. $user->fill($data);
  4. $userModel->save($user);

您也可以在構(gòu)造函數(shù)中傳遞數(shù)據(jù),并且數(shù)據(jù)將在實(shí)例化過(guò)程中通過(guò)fill()方法傳遞。

  1. $data = $this->request->getPost();
  2. $user = new \App\Entities\User($data);
  3. $userModel->save($user);

處理業(yè)務(wù)邏輯

盡管上面的示例很方便,但它們并不能幫助您實(shí)施任何業(yè)務(wù)邏輯?;鶎?shí)體類(lèi)實(shí)現(xiàn)一些智能__get()__set()方法,將檢查特別的方法和使用這些而不是直接使用屬性,讓你執(zhí)行你需要的任何業(yè)務(wù)邏輯或數(shù)據(jù)轉(zhuǎn)換。

這是一個(gè)更新的User實(shí)體,提供了一些如何使用它的示例:

  1. <?php namespace App\Entities;
  2. use CodeIgniter\Entity;
  3. use CodeIgniter\I18n\Time;
  4. class User extends Entity
  5. {
  6. public function setPassword(string $pass)
  7. {
  8. $this->attributes['password'] = password_hash($pass, PASSWORD_BCRYPT);
  9. return $this;
  10. }
  11. public function setCreatedAt(string $dateString)
  12. {
  13. $this->attributes['created_at'] = new Time($dateString, 'UTC');
  14. return $this;
  15. }
  16. public function getCreatedAt(string $format = 'Y-m-d H:i:s')
  17. {
  18. // Convert to CodeIgniter\I18n\Time object
  19. $this->attributes['created_at'] = $this->mutateDate($this->attributes['created_at']);
  20. $timezone = $this->timezone ?? app_timezone();
  21. $this->attributes['created_at']->setTimezone($timezone);
  22. return $this->attributes['created_at']->format($format);
  23. }
  24. }

首先要注意的是我們添加的方法的名稱(chēng)。對(duì)于每個(gè)類(lèi),該類(lèi)都希望將snake_case列名轉(zhuǎn)換為PascalCase,并以set或作為前綴get。每當(dāng)您使用直接語(yǔ)法(即$ user-> email)設(shè)置或檢索class屬性時(shí),這些方法將被自動(dòng)調(diào)用。除非您希望從其他類(lèi)訪問(wèn)它們,否則這些方法不需要是公共的。例如,created_at 將通過(guò)setCreatedAt()getCreatedAt()方法訪問(wèn)class屬性。

注解

這僅在嘗試從類(lèi)外部訪問(wèn)屬性時(shí)有效。該類(lèi)內(nèi)部的任何方法都必須直接調(diào)用setX()getX()方法。

在該setPassword()方法中,我們確保始終對(duì)密碼進(jìn)行哈希處理。

setCreatedAt()我們將從模型接收的字符串轉(zhuǎn)換為DateTime對(duì)象時(shí),請(qǐng)確保我們的時(shí)區(qū)為UTC,以便我們可以輕松地轉(zhuǎn)換查看器的當(dāng)前時(shí)區(qū)。在中getCreatedAt(),它將時(shí)間轉(zhuǎn)換為應(yīng)用程序當(dāng)前時(shí)區(qū)中的格式化字符串。

這些示例雖然相當(dāng)簡(jiǎn)單,但是卻表明使用Entity類(lèi)可以提供一種非常靈活的方式來(lái)強(qiáng)制執(zhí)行業(yè)務(wù)邏輯并創(chuàng)建易于使用的對(duì)象。

  1. // Auto-hash the password - both do the same thing
  2. $user->password = 'my great password';
  3. $user->setPassword('my great password');

資料對(duì)應(yīng)

在您的職業(yè)生涯中的很多時(shí)候,您都會(huì)遇到以下情況:應(yīng)用程序的使用已發(fā)生更改,并且數(shù)據(jù)庫(kù)中的原始列名不再有意義?;蛘?,您發(fā)現(xiàn)您的編碼風(fēng)格更喜歡camelCase類(lèi)屬性,但是您的數(shù)據(jù)庫(kù)模式需要snake_case名稱(chēng)。使用Entity類(lèi)的數(shù)據(jù)映射功能可以輕松處理這些情況。

例如,假設(shè)您擁有在整個(gè)應(yīng)用程序中使用的簡(jiǎn)化用戶(hù)實(shí)體:

  1. <?php namespace App\Entities;
  2. use CodeIgniter\Entity;
  3. class User extends Entity
  4. {
  5. protected $attributes = [
  6. 'id' => null,
  7. 'name' => null, // Represents a username
  8. 'email' => null,
  9. 'password' => null,
  10. 'created_at' => null,
  11. 'updated_at' => null,
  12. ];
  13. }

您的老板來(lái)找您,并說(shuō)沒(méi)有人再使用用戶(hù)名,因此您將切換為僅使用電子郵件進(jìn)行登錄。但是他們確實(shí)希望對(duì)應(yīng)用程序進(jìn)行一些個(gè)性化設(shè)置,因此他們希望您更改名稱(chēng)字段以現(xiàn)在表示用戶(hù)的全名,而不是像現(xiàn)在這樣表示用戶(hù)名。為了使事情保持整潔并確保事情在數(shù)據(jù)庫(kù)中繼續(xù)有意義,您進(jìn)行了一次遷移,以將名稱(chēng)字段重命名為full_name以便清楚。

忽略此示例有多難為情,我們現(xiàn)在有兩個(gè)關(guān)于如何修復(fù)User類(lèi)的選擇。我們可以將class屬性從修改$name$full_name,但這需要在整個(gè)應(yīng)用程序中進(jìn)行更改。相反,我們可以簡(jiǎn)單地full_name將數(shù)據(jù)庫(kù)中的列映射到該$name屬性,并通過(guò)Entity更改來(lái)完成:

  1. <?php namespace App\Entities;
  2. use CodeIgniter\Entity;
  3. class User extends Entity
  4. {
  5. protected $attributes = [
  6. 'id' => null,
  7. 'name' => null, // Represents a username
  8. 'email' => null,
  9. 'password' => null,
  10. 'created_at' => null,
  11. 'updated_at' => null,
  12. ];
  13. protected $datamap = [
  14. 'full_name' => 'name'
  15. ],
  16. }

通過(guò)將新的數(shù)據(jù)庫(kù)名稱(chēng)添加到$datamap數(shù)組,我們可以告訴類(lèi)應(yīng)該通過(guò)其訪問(wèn)數(shù)據(jù)庫(kù)列的類(lèi)屬性。數(shù)組的鍵是數(shù)據(jù)庫(kù)中列的名稱(chēng),其中數(shù)組中的值是將其映射到的類(lèi)屬性。

在此示例中,當(dāng)模型full_name在User類(lèi)上設(shè)置字段時(shí),它實(shí)際上將該值分配給該類(lèi)的$name屬性,因此可以通過(guò)進(jìn)行設(shè)置和檢索$user->name。同樣,該值仍可以通過(guò)原始值訪問(wèn)$user->full_name,因?yàn)槟P托枰撝祦?lái)取回?cái)?shù)據(jù)并將其保存到數(shù)據(jù)庫(kù)。但是,unset并且isset僅適用于映射的屬性$name,不適用于原始名稱(chēng) full_name。

變異者

日期變量

默認(rèn)情況下,實(shí)體類(lèi)將轉(zhuǎn)換命名字段created_at,的updated_at,或deleted_at到 時(shí)間時(shí),他們?cè)O(shè)置或獲取的實(shí)例。Time類(lèi)以不變,本地化的方式提供了大量有用的方法。

您可以通過(guò)將名稱(chēng)添加到options ['dates']數(shù)組來(lái)定義自動(dòng)轉(zhuǎn)換的屬性:

  1. <?php namespace App\Entities;
  2. use CodeIgniter\Entity;
  3. class User extends Entity
  4. {
  5. protected $dates = ['created_at', 'updated_at', 'deleted_at'];
  6. }

現(xiàn)在,當(dāng)設(shè)置了這些屬性中的任何一個(gè)時(shí),它們將使用應(yīng)用程序的當(dāng)前時(shí)區(qū)(如app / Config / App.php中的設(shè)置)轉(zhuǎn)換為T(mén)ime實(shí)例:

  1. $user = new \App\Entities\User();
  2. // Converted to Time instance
  3. $user->created_at = 'April 15, 2017 10:30:00';
  4. // Can now use any Time methods:
  5. echo $user->created_at->humanize();
  6. echo $user->created_at->setTimezone('Europe/London')->toDateString();

財(cái)產(chǎn)鑄造

您可以使用casts屬性指定將Entity中的屬性轉(zhuǎn)換為通用數(shù)據(jù)類(lèi)型。此選項(xiàng)應(yīng)該是一個(gè)數(shù)組,其中鍵是類(lèi)屬性的名稱(chēng),而值是應(yīng)強(qiáng)制轉(zhuǎn)換為的數(shù)據(jù)類(lèi)型。投射僅在讀取值時(shí)影響。不會(huì)發(fā)生影響實(shí)體或數(shù)據(jù)庫(kù)中的永久值的轉(zhuǎn)換??梢詫傩詮?qiáng)制轉(zhuǎn)換為以下任何數(shù)據(jù)類(lèi)型: integer,float,double,string,boolean,object,array,datetimetimestamp。在類(lèi)型的開(kāi)頭添加一個(gè)問(wèn)號(hào),以將屬性標(biāo)記為可為空,即?string,?integer。

例如,如果您有一個(gè)具有is_banned屬性的User實(shí)體,則可以將其強(qiáng)制轉(zhuǎn)換為布爾值:

  1. <?php namespace App\Entities;
  2. use CodeIgniter\Entity;
  3. class User extends Entity
  4. {
  5. protected $casts = [
  6. 'is_banned' => 'boolean',
  7. 'is_banned_nullable' => '?boolean'
  8. ],
  9. }

陣列/杰森鑄造

數(shù)組/ Json強(qiáng)制轉(zhuǎn)換對(duì)于在其中存儲(chǔ)序列化數(shù)組或json的字段特別有用。轉(zhuǎn)換為:

  • 一個(gè)陣列,它們將自動(dòng)序列化,
  • 一個(gè)json,它們將自動(dòng)設(shè)置為json_decode($ value,false)的值,
  • 一個(gè)json-array,它們將自動(dòng)設(shè)置為json_decode($ value,true)的值,

當(dāng)您讀取屬性值時(shí)。與可以將屬性轉(zhuǎn)換為的其余數(shù)據(jù)類(lèi)型不同,它們是:

  • 數(shù)組類(lèi)型轉(zhuǎn)換將序列化,
  • jsonjson-array強(qiáng)制轉(zhuǎn)換將使用json_encode函數(shù)

設(shè)置屬性時(shí)的值:

  1. <?php namespace App\Entities;
  2. use CodeIgniter\Entity;
  3. class User extends Entity
  4. {
  5. protected $casts => [
  6. 'options' => 'array',
  7. 'options_object' => 'json',
  8. 'options_array' => 'json-array'
  9. ];
  10. }
  11. $user = $userModel->find(15);
  12. $options = $user->options;
  13. $options['foo'] = 'bar';
  14. $user->options = $options;
  15. $userModel->save($user);

檢查更改的屬性

您可以檢查Entity屬性自創(chuàng)建以來(lái)是否已更改。唯一的參數(shù)是要檢查的屬性的名稱(chēng):

  1. $user = new User();
  2. $user->hasChanged('name'); // false
  3. $user->name = 'Fred';
  4. $user->hasChanged('name'); // true

或檢查整個(gè)實(shí)體是否有更改的值,請(qǐng)省略參數(shù):

  1. $user->hasChanged(); // true
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)