CodeIgniter4 使用实体类

2020-08-17 16:32 更新

CodeIgniter支持实体类作为其数据库层中的一等公民,同时使它们完全可选使用。它们通常用作存储库模式的一部分,但如果更适合您的需求,则可以直接与模型一起使用。

实体使用

实体类的核心只是代表单个数据库行的类。它具有表示数据库列的类属性,并提供了用于实现该行的业务逻辑的任何其他方法。但是,核心功能是它对如何持久化一无所知。这是模型或存储库类的责任。这样,如果需要保存对象的方式发生任何变化,则无需更改在整个应用程序中使用该对象的方式。这样就可以在快速原型制作阶段使用JSON或XML文件存储对象,然后在证明该概念行得通的情况下轻松地切换到数据库。

让我们来看一个非常简单的用户实体,以及如何使用它来使事情变得清晰。

假设您有一个users具有以下架构的数据库表:

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

创建实体类

现在创建一个新的实体类。由于没有默认位置可存储这些类,并且它不适合现有的目录结构,因此请在app / Entities处创建一个新目录。在app / Entities / User.php中创建实体本身。

<?php namespace App\Entities;


use CodeIgniter\Entity;


class User extends Entity
{
    //
}

简单地说,这就是您需要做的,尽管我们将在一分钟内使它变得更加有用。

创建模型

首先在app / Models / UserModel.php中创建模型,以便我们可以与之交互:

<?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属性设置为包括我们希望外部类更改的所有字段。的idcreated_atupdated_at字段由类或数据库中自动处理的,所以我们不希望改变这些。最后,我们将Entity类设置为$returnType。这确保了模型上从数据库返回行的所有方法都将返回我们的User Entity类的实例,而不是像通常那样返回对象或数组。

使用实体类

现在所有部分都准备就绪,您将像其他任何类一样使用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类没有为列设置任何属性,但是您仍然可以像访问它们一样将其作为公共属性来访问它们。基类CodeIgniterEntity为您解决了这一问题,并提供了使用isset()unset()属性检查属性的能力,并跟踪自创建或拉出对象以来哪些列已更改从数据库中。

当User传递给模型的save()方法时,它将自动负责读取属性并将对模型的$ allowedFields属性中列出的列的所有更改保存。它还知道是创建新行还是更新现有行。

快速填充属性

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);

处理业务逻辑

尽管上面的示例很方便,但它们并不能帮助您实施任何业务逻辑。基实体类实现一些智能__get()__set()方法,将检查特别的方法和使用这些而不是直接使用属性,让你执行你需要的任何业务逻辑或数据转换。

这是一个更新的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对象时,请确保我们的时区为UTC,以便我们可以轻松地转换查看器的当前时区。在中getCreatedAt(),它将时间转换为应用程序当前时区中的格式化字符串。

这些示例虽然相当简单,但是却表明使用Entity类可以提供一种非常灵活的方式来强制执行业务逻辑并创建易于使用的对象。

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

资料对应

在您的职业生涯中的很多时候,您都会遇到以下情况:应用程序的使用已发生更改,并且数据库中的原始列名不再有意义。或者,您发现您的编码风格更喜欢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,
    ];
}

您的老板来找您,并说没有人再使用用户名,因此您将切换为仅使用电子邮件进行登录。但是他们确实希望对应用程序进行一些个性化设置,因此他们希望您更改名称字段以现在表示用户的全名,而不是像现在这样表示用户名。为了使事情保持整洁并确保事情在数据库中继续有意义,您进行了一次迁移,以将名称字段重命名为full_name以便清楚。

忽略此示例有多难为情,我们现在有两个关于如何修复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'];
}

现在,当设置了这些属性中的任何一个时,它们将使用应用程序的当前时区(如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中的属性转换为通用数据类型。此选项应该是一个数组,其中键是类属性的名称,而值是应强制转换为的数据类型。投射仅在读取值时影响。不会发生影响实体或数据库中的永久值的转换。可以将属性强制转换为以下任何数据类型: integerfloatdoublestringbooleanobjectarraydatetimetimestamp。在类型的开头添加一个问号,以将属性标记为可为空,即?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属性自创建以来是否已更改。唯一的参数是要检查的属性的名称:

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


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

或检查整个实体是否有更改的值,请省略参数:

$user->hasChanged();            // true
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号