CodeIgniter4 使用實體類

2020-08-17 16:32 更新

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

實體使用

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

讓我們來看一個非常簡單的用戶實體,以及如何使用它來使事情變得清晰。

假設您有一個users具有以下架構的數據庫表:

id          - integer
username    - string
email       - string
password    - string
created_at  - datetime

創(chuàng)建實體類

現(xiàn)在創(chuàng)建一個新的實體類。由于沒有默認位置可存儲這些類,并且它不適合現(xiàn)有的目錄結構,因此請在app / Entities處創(chuàng)建一個新目錄。在app / Entities / User.php中創(chuàng)建實體本身。

<?php namespace App\Entities;


use CodeIgniter\Entity;


class User extends Entity
{
    //
}

簡單地說,這就是您需要做的,盡管我們將在一分鐘內使它變得更加有用。

創(chuàng)建模型

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

<?php namespace App\Models;


use CodeIgniter\Model;


class UserModel extends Model
{
    protected $table         = 'users';
    protected $allowedFields = [
        'username', 'email', 'password'
    ];
    protected $returnType    = 'App\Entities\User';
    protected $useTimestamps = true;
}

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

使用實體類

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

$user = $userModel->find($id);


// Display
echo $user->username;
echo $user->email;


// Updating
unset($user->username);
if (! isset($user->username)
{
    $user->username = 'something new';
}
$userModel->save($user);


// Create
$user = new \App\Entities\User();
$user->username = 'foo';
$user->email    = 'foo@example.com';
$userModel->save($user);

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

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

快速填充屬性

Entity類還提供一種方法,fill()該方法允許您將鍵/值對的數組推入類并填充類屬性。數組中的任何屬性都將在實體上設置。但是,在通過模型進行保存時,實際上僅將$ allowedFields中的字段保存到數據庫中,因此您可以在實體上存儲其他數據,而不必擔心會錯誤地保存雜散字段。

$data = $this->request->getPost();


$user = new \App\Entities\User();
$user->fill($data);
$userModel->save($user);

您也可以在構造函數中傳遞數據,并且數據將在實例化過程中通過fill()方法傳遞。

$data = $this->request->getPost();


$user = new \App\Entities\User($data);
$userModel->save($user);

處理業(yè)務邏輯

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

這是一個更新的User實體,提供了一些如何使用它的示例:

<?php namespace App\Entities;


use CodeIgniter\Entity;
use CodeIgniter\I18n\Time;


class User extends Entity
{
    public function setPassword(string $pass)
    {
        $this->attributes['password'] = password_hash($pass, PASSWORD_BCRYPT);


        return $this;
    }


    public function setCreatedAt(string $dateString)
    {
        $this->attributes['created_at'] = new Time($dateString, 'UTC');


        return $this;
    }


    public function getCreatedAt(string $format = 'Y-m-d H:i:s')
    {
        // Convert to CodeIgniter\I18n\Time object
        $this->attributes['created_at'] = $this->mutateDate($this->attributes['created_at']);


        $timezone = $this->timezone ?? app_timezone();


        $this->attributes['created_at']->setTimezone($timezone);


        return $this->attributes['created_at']->format($format);
    }
}

首先要注意的是我們添加的方法的名稱。對于每個類,該類都希望將snake_case列名轉換為PascalCase,并以set或作為前綴get。每當您使用直接語法(即$ user-> email)設置或檢索class屬性時,這些方法將被自動調用。除非您希望從其他類訪問它們,否則這些方法不需要是公共的。例如,created_at 將通過setCreatedAt()getCreatedAt()方法訪問class屬性。

注解

這僅在嘗試從類外部訪問屬性時有效。該類內部的任何方法都必須直接調用setX()getX()方法。

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

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

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

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

資料對應

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

例如,假設您擁有在整個應用程序中使用的簡化用戶實體:

<?php namespace App\Entities;


use CodeIgniter\Entity;


class User extends Entity
{
    protected $attributes = [
        'id' => null,
        'name' => null,        // Represents a username
        'email' => null,
        'password' => null,
        'created_at' => null,
        'updated_at' => null,
    ];
}

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

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

<?php namespace App\Entities;


use CodeIgniter\Entity;


class User extends Entity
{
    protected $attributes = [
        'id' => null,
        'name' => null,        // Represents a username
        'email' => null,
        'password' => null,
        'created_at' => null,
        'updated_at' => null,
    ];


    protected $datamap = [
        'full_name' => 'name'
    ],
}

通過將新的數據庫名稱添加到$datamap數組,我們可以告訴類應該通過其訪問數據庫列的類屬性。數組的鍵是數據庫中列的名稱,其中數組中的值是將其映射到的類屬性。

在此示例中,當模型full_name在User類上設置字段時,它實際上將該值分配給該類的$name屬性,因此可以通過進行設置和檢索$user->name。同樣,該值仍可以通過原始值訪問$user->full_name,因為模型需要該值來取回數據并將其保存到數據庫。但是,unset并且isset僅適用于映射的屬性$name,不適用于原始名稱 full_name

變異者

日期變量

默認情況下,實體類將轉換命名字段created_at,的updated_at,或deleted_at到 時間時,他們設置或獲取的實例。Time類以不變,本地化的方式提供了大量有用的方法。

您可以通過將名稱添加到options ['dates']數組來定義自動轉換的屬性:

<?php namespace App\Entities;


use CodeIgniter\Entity;


class User extends Entity
{
    protected $dates = ['created_at', 'updated_at', 'deleted_at'];
}

現(xiàn)在,當設置了這些屬性中的任何一個時,它們將使用應用程序的當前時區(qū)(如app / Config / App.php中的設置)轉換為Time實例:

$user = new \App\Entities\User();


// Converted to Time instance
$user->created_at = 'April 15, 2017 10:30:00';


// Can now use any Time methods:
echo $user->created_at->humanize();
echo $user->created_at->setTimezone('Europe/London')->toDateString();

財產鑄造

您可以使用casts屬性指定將Entity中的屬性轉換為通用數據類型。此選項應該是一個數組,其中鍵是類屬性的名稱,而值是應強制轉換為的數據類型。投射僅在讀取值時影響。不會發(fā)生影響實體或數據庫中的永久值的轉換??梢詫傩詮娭妻D換為以下任何數據類型: integer,floatdouble,string,boolean,objectarray,datetimetimestamp。在類型的開頭添加一個問號,以將屬性標記為可為空,即?string,?integer。

例如,如果您有一個具有is_banned屬性的User實體,則可以將其強制轉換為布爾值:

<?php namespace App\Entities;


use CodeIgniter\Entity;


class User extends Entity
{
    protected $casts = [
        'is_banned' => 'boolean',
        'is_banned_nullable' => '?boolean'
    ],
}

陣列/杰森鑄造

數組/ Json強制轉換對于在其中存儲序列化數組或json的字段特別有用。轉換為:

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

當您讀取屬性值時。與可以將屬性轉換為的其余數據類型不同,它們是:

  • 數組類型轉換將序列化,
  • jsonjson-array強制轉換將使用json_encode函數

設置屬性時的值:

<?php namespace App\Entities;


use CodeIgniter\Entity;


class User extends Entity
{
    protected $casts => [
        'options' => 'array',
                'options_object' => 'json',
                'options_array' => 'json-array'
    ];
}


$user    = $userModel->find(15);
$options = $user->options;


$options['foo'] = 'bar';


$user->options = $options;
$userModel->save($user);

檢查更改的屬性

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

$user = new User();
$user->hasChanged('name');      // false


$user->name = 'Fred';
$user->hasChanged('name');      // true

或檢查整個實體是否有更改的值,請省略參數:

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號