使用 CodeIgniter 的模型
模型
CodeIgniter 的模型提供了便利的功能和额外的功能,这些功能通常用于使与数据库中 **单个表** 的交互更加方便。
它自带了许多标准方法的辅助方法,这些方法用于与数据库表进行交互,包括查找记录、更新记录、删除记录等等。
访问模型
模型通常存储在 **app/Models** 目录中。它们应该具有与目录中位置匹配的命名空间,例如 namespace App\Models
。
您可以在类中通过创建新实例或使用 model()
辅助函数来访问模型。
<?php
// Create a new class manually.
$userModel = new \App\Models\UserModel();
// Create a shared instance of the model.
$userModel = model('UserModel');
// or
$userModel = model('App\Models\UserModel');
// or
$userModel = model(\App\Models\UserModel::class);
// Create a new class with the model() function.
$userModel = model('UserModel', false);
// Create shared instance with a supplied database connection.
$db = db_connect('custom');
$userModel = model('UserModel', true, $db);
model()
在内部使用 Factories::models()
。有关第一个参数的详细信息,请参阅 加载类。
CodeIgniter 的模型
CodeIgniter 提供了一个模型类,它提供了一些不错的功能,包括
自动数据库连接
基本的 CRUD 方法
模型内验证
等等
此类提供了一个坚实的基础,您可以从中构建自己的模型,从而快速构建应用程序的模型层。
创建您的模型
要利用 CodeIgniter 的模型,您只需创建一个新的模型类,该类扩展了 CodeIgniter\Model
<?php
namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
// ...
}
这个空类提供了对数据库连接、查询构建器和许多其他便利方法的便捷访问。
initialize()
如果您需要在模型中进行额外的设置,您可以扩展 initialize()
方法,该方法将在模型的构造函数之后立即运行。这允许您执行额外的步骤,而无需重复构造函数参数,例如扩展其他模型。
<?php
namespace App\Models;
use Modules\Authentication\Models\UserAuthModel;
class UserModel extends UserAuthModel
{
/**
* Called during initialization. Appends
* our custom field to the module's model.
*/
protected function initialize()
{
$this->allowedFields[] = 'middlename';
}
}
连接到数据库
当类首次实例化时,如果未将数据库连接实例传递给构造函数,并且您未在模型类上设置 $DBGroup
属性,它将自动连接到默认数据库组,如数据库配置中所设置。
您可以通过在类中添加 $DBGroup
属性来修改每个模型使用的组。这确保了在模型中,对 $this->db
的任何引用都是通过适当的连接进行的。
<?php
namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $DBGroup = 'group_name';
}
您需要将“group_name”替换为数据库配置文件中定义的数据库组的名称。
配置您的模型
模型类有一些配置选项,可以设置这些选项以使类的所有方法都能为您无缝地工作。前两个选项由所有 CRUD 方法使用,以确定要使用哪个表以及如何找到所需的记录。
<?php
namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $table = 'users';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = true;
protected $allowedFields = ['name', 'email'];
protected bool $allowEmptyInserts = false;
// Dates
protected $useTimestamps = false;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
}
$table
指定此模型主要使用的数据库表。这仅适用于内置的 CRUD 方法。您不受限于仅在自己的查询中使用此表。
$primaryKey
这是唯一标识此表中记录的列的名称。这并不一定需要与数据库中指定的主键匹配,但与 find()
等方法一起使用,以了解要将指定的值与哪个列匹配。
注意
所有模型都必须指定一个 primaryKey,以使所有功能都能按预期工作。
$useAutoIncrement
指定表是否为 $primaryKey 使用自动递增功能。如果设置为 false
,则您负责为表中的每条记录提供主键值。当我们想要实现 1:1 关系或使用 UUID 来表示我们的模型时,此功能可能很方便。默认值为 true
。
注意
如果您将 $useAutoIncrement 设置为 false
,那么请确保将数据库中的主键设置为 unique
。这样,您将确保模型的所有功能都将与以前一样工作。
$returnType
模型的 CRUD 方法将为您省去一步操作,并自动返回结果数据,而不是 Result 对象。此设置允许您定义返回数据的类型。有效值为“**array**”(默认值)、“**object**”或可与 Result 对象的 getCustomResultObject()
方法一起使用的**类的完全限定名称**。使用类的特殊 ::class
常量将允许大多数 IDE 自动完成名称,并允许重构等功能更好地理解您的代码。
$useSoftDeletes
如果为 true,则任何 delete()
方法调用都将在数据库中设置 deleted_at
,而不是实际删除该行。这可以在数据可能在其他地方被引用时保留数据,或维护一个可以恢复的对象“回收站”,甚至只是将其作为安全跟踪的一部分保留。如果为 true,则 **find*()** 方法将仅返回未删除的行,除非在调用 **find*()** 方法之前调用了 withDeleted()
方法。
这需要数据库中的 DATETIME 或 INTEGER 字段,如模型的 $dateFormat 设置所示。默认字段名称为 deleted_at
,但此名称可以通过使用 $deletedField 属性配置为任何您选择的名称。
重要
deleted_at
字段必须可为空。
$allowedFields
此数组应使用在 save()
、insert()
或 update()
方法期间可以设置的字段名称进行更新。除这些字段名称之外的任何字段名称都将被丢弃。这有助于防止仅从表单中获取输入并将所有输入都抛给模型,从而导致潜在的大规模分配漏洞。
注意
$primaryKey 字段永远不应该成为允许的字段。
$allowEmptyInserts
版本 4.3.0 中新增。
是否允许插入空数据。默认值为 false
,这意味着如果您尝试插入空数据,将引发异常“没有数据可插入”。
您也可以使用 allowEmptyInserts() 方法更改此设置。
日期
$useTimestamps
此布尔值决定是否将当前日期自动添加到所有插入和更新中。如果为 true
,将以 $dateFormat 指定的格式设置当前时间。这要求表具有以适当数据类型命名的 created_at、updated_at 和 deleted_at 列。另请参阅 $createdField、$updatedField 和 $deletedField。
$dateFormat
此值与 $useTimestamps 和 $useSoftDeletes 协同工作,以确保将正确类型的日期值插入数据库。默认情况下,这将创建 DATETIME 值,但有效选项为:'datetime'
、'date'
或 'int'
(PHP 时间戳)。使用 $useSoftDeletes 或 $useTimestamps 以及无效或缺少的 $dateFormat 将导致异常。
$createdField
指定要用于数据记录创建时间戳的数据库字段。设置为空字符串 (''
) 以避免更新它(即使 $useTimestamps 已启用)。
$updatedField
指定应使用哪个数据库字段来保存数据记录更新时间戳。设置为空字符串 (''
) 以避免更新它(即使 $useTimestamps 已启用)。
$deletedField
指定用于软删除的数据库字段。参见 $useSoftDeletes.
验证
$validationRules
包含一个验证规则数组,如 如何保存规则 中所述,或包含一个验证组名称的字符串,如同一节所述。下面将详细介绍。
$validationMessages
包含一个自定义错误消息数组,这些消息应在验证期间使用,如 设置自定义错误消息 中所述。下面将详细介绍。
$skipValidation
是否应在所有 **插入** 和 **更新** 操作期间跳过验证。默认值为 false
,这意味着数据将始终尝试进行验证。这主要由 skipValidation()
方法使用,但可以更改为 true
,以便此模型永远不会进行验证。
$cleanValidationRules
是否应删除传递数据中不存在的验证规则。这在 **更新** 操作中使用。默认值为 true
,这意味着传递数据中不存在的字段的验证规则将在验证之前(暂时)删除。这是为了避免在仅更新某些字段时出现验证错误。
您也可以通过 cleanRules()
方法更改该值。
注意
在 v4.2.7 之前,$cleanValidationRules
由于错误而无法正常工作。
回调
$allowCallbacks
是否使用下面定义的回调函数。请参阅 模型事件.
$beforeInsert
$afterInsert
$beforeUpdate
$afterUpdate
$beforeFind
$afterFind
$beforeDelete
$afterDelete
$beforeInsertBatch
$afterInsertBatch
$beforeUpdateBatch
$afterUpdateBatch
这些数组允许您指定回调方法,这些方法将在属性名称中指定的时间点对数据运行。请参阅 模型事件.
数据操作
查找数据
提供了一些函数用于对您的表格进行基本的 CRUD 操作,包括 find()
、insert()
、update()
、delete()
等。
find()
返回主键与作为第一个参数传入的值匹配的单行
<?php
$user = $userModel->find($user_id);
返回值以 $returnType 中指定的格式返回。
您可以通过传递主键值的数组而不是单个值来指定返回多行
<?php
$users = $userModel->find([1, 2, 3]);
注意
如果未传入任何参数,find()
将返回该模型表格中的所有行,实际上等同于 findAll()
,尽管不太明确。
findColumn()
返回 null 或列值的索引数组
<?php
$user = $userModel->findColumn($column_name);
$column_name
应为单个列的名称,否则您将获得 DataException
。
findAll()
返回所有结果
<?php
$users = $userModel->findAll();
在调用此方法之前,可以通过插入 Query Builder 命令来修改此查询
<?php
$users = $userModel->where('active', 1)->findAll();
您可以将限制和偏移值分别作为第一个和第二个参数传递
<?php
$users = $userModel->findAll($limit, $offset);
first()
返回结果集中的第一行。这最好与查询构建器结合使用。
<?php
$user = $userModel->where('deleted', 0)->first();
withDeleted()
如果 $useSoftDeletes 为真,则 **find*()** 方法将不会返回任何 deleted_at IS NOT NULL
的行。要暂时覆盖此操作,您可以在调用 **find*()** 方法之前使用 withDeleted()
方法。
<?php
// Only gets non-deleted rows (deleted = 0)
$activeUsers = $userModel->findAll();
// Gets all rows
$allUsers = $userModel->withDeleted()->findAll();
onlyDeleted()
虽然 withDeleted()
将返回已删除和未删除的行,但此方法会修改下一个 **find*()** 方法以仅返回软删除的行
<?php
$deletedUsers = $userModel->onlyDeleted()->findAll();
保存数据
insert()
第一个参数是关联数组,用于在数据库中创建新数据行。如果传递的是对象而不是数组,它将尝试将其转换为数组。
数组的键必须与 $table 中的列名匹配,而数组的值是为该键保存的值。
可选的第二个参数是布尔类型,如果将其设置为 false,则该方法将返回一个布尔值,该值指示查询的成功或失败。
您可以使用 getInsertID()
方法检索最后插入行的主键。
<?php
$data = [
'username' => 'darth',
'email' => '[email protected]',
];
// Inserts data and returns inserted row's primary key
$userModel->insert($data);
// Inserts data and returns true on success and false on failure
$userModel->insert($data, false);
// Returns inserted row's primary key
$userModel->getInsertID();
allowEmptyInserts()
版本 4.3.0 中新增。
您可以使用 allowEmptyInserts()
方法插入空数据。默认情况下,当您尝试插入空数据时,模型会抛出异常。但是,如果您调用此方法,则不再执行检查。
<?php
$userModel->allowEmptyInserts()->insert([]);
您也可以使用 $allowEmptyInserts 属性更改此设置。
您可以通过调用 allowEmptyInserts(false)
再次启用检查。
update()
更新数据库中现有记录。第一个参数是要更新的记录的 $primaryKey。一个关联数据数组作为第二个参数传递到此方法中。数组的键必须与 $table 中列的名称匹配,而数组的值是要为该键保存的值。
<?php
$data = [
'username' => 'darth',
'email' => '[email protected]',
];
$userModel->update($id, $data);
重要
从 v4.3.0 开始,如果此方法生成没有 WHERE 子句的 SQL 语句,它将引发 DatabaseException
。在之前的版本中,如果在没有指定 $primaryKey 的情况下调用它,并且生成了没有 WHERE 子句的 SQL 语句,则查询仍然会执行,并且表中的所有记录都会被更新。
可以通过将主键数组作为第一个参数传递,用单个调用更新多个记录。
<?php
$data = [
'active' => 1,
];
$userModel->update([1, 2, 3], $data);
当您需要更灵活的解决方案时,您可以将参数留空,它就像 Query Builder 的 update 命令一样工作,并且具有验证、事件等的额外优势。
<?php
$userModel
->whereIn('id', [1, 2, 3])
->set(['active' => 1])
->update();
save()
这是围绕 insert()
和 update()
方法的包装器,它根据是否找到与 **主键** 值匹配的数组键,自动处理插入或更新记录。
<?php
// Defined as a model property
$primaryKey = 'id';
// Does an insert()
$data = [
'username' => 'darth',
'email' => '[email protected]',
];
$userModel->save($data);
// Performs an update, since the primary key, 'id', is found.
$data = [
'id' => 3,
'username' => 'darth',
'email' => '[email protected]',
];
$userModel->save($data);
save 方法还可以通过识别非简单对象并将它的公共和受保护的值放入数组中,然后将其传递给相应的插入或更新方法,使使用自定义类结果对象变得更加简单。这允许您以非常干净的方式使用实体类。实体类是表示对象类型单个实例的简单类,例如用户、博客文章、工作等。此类负责维护围绕对象本身的业务逻辑,例如以特定方式格式化元素等。它们不应该知道它们是如何保存到数据库中的。在最简单的情况下,它们可能看起来像这样
<?php
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;
}
}
}
一个非常简单的模型来使用它可能看起来像
<?php
namespace App\Models;
use CodeIgniter\Model;
class JobModel extends Model
{
protected $table = 'jobs';
protected $returnType = \App\Entities\Job::class;
protected $allowedFields = [
'name', 'description',
];
}
此模型使用来自 jobs
表的数据,并将所有结果作为 App\Entities\Job
的实例返回。当您需要将该记录持久化到数据库时,您需要编写自定义方法,或者使用模型的 save()
方法来检查类,获取所有公共和私有属性,并将它们保存到数据库。
<?php
// Retrieve a Job instance
$job = $model->find(15);
// Make some changes
$job->name = 'Foobar';
// Save the changes
$model->save($job);
注意
如果您发现自己经常使用实体,CodeIgniter 提供了一个内置的 实体类,它提供了一些方便的功能,使开发实体变得更简单。
删除数据
delete()
以主键值作为第一个参数,从模型的表中删除匹配的记录。
<?php
$userModel->delete(12);
如果模型的 $useSoftDeletes 值为 true,这将更新行,将 deleted_at
设置为当前日期和时间。您可以通过将第二个参数设置为 true 来强制永久删除。
可以将主键数组作为第一个参数传递进来,以一次删除多条记录。
<?php
$userModel->delete([1, 2, 3]);
如果没有传递参数,将像 Query Builder 的 delete 方法一样,需要之前调用 where。
<?php
$userModel->where('id', 12)->delete();
purgeDeleted()
通过永久删除所有 'deleted_at IS NOT NULL' 的行来清理数据库表。
<?php
$userModel->purgeDeleted();
模型内验证
验证数据
对于许多人来说,在模型中验证数据是确保数据保持单一标准的最佳方式,无需重复代码。Model 类提供了一种方法,可以自动在使用 insert()
、update()
或 save()
方法保存到数据库之前对所有数据进行验证。
重要
当您更新数据时,默认情况下,模型类中的验证只验证提供的字段。这是为了避免在只更新某些字段时出现验证错误。
但这意味着 required*
规则在更新时无法按预期工作。如果您想检查必填字段,可以通过配置更改行为。有关详细信息,请参阅 $cleanValidationRules。
设置验证规则
第一步是填写 $validationRules 类属性,其中包含应应用的字段和规则。如果您有要使用的自定义错误消息,请将它们放在 $validationMessages 数组中
<?php
namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $validationRules = [
'username' => 'required|max_length[30]|alpha_numeric_space|min_length[3]',
'email' => 'required|max_length[254]|valid_email|is_unique[users.email]',
'password' => 'required|max_length[255]|min_length[8]',
'pass_confirm' => 'required_with[password]|max_length[255]|matches[password]',
];
protected $validationMessages = [
'email' => [
'is_unique' => 'Sorry. That email has already been taken. Please choose another.',
],
];
}
如果您希望在验证配置文件中组织规则和错误消息,您可以这样做,只需将 $validationRules 设置为您创建的验证规则组的名称即可
<?php
namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $validationRules = 'users';
}
另一种方法是通过函数将验证规则设置为字段,
- class CodeIgniter\Model
- CodeIgniter\Model::setValidationRule($field, $fieldRules)
- 参数:
$field (
string
) –$fieldRules (
array
) –
此函数将设置字段验证规则。
用法示例
<?php $fieldName = 'username'; $fieldRules = 'required|max_length[30]|alpha_numeric_space|min_length[3]'; $model->setValidationRule($fieldName, $fieldRules);
- CodeIgniter\Model::setValidationRules($validationRules)
- 参数:
$validationRules (
array
) –
此函数将设置验证规则。
用法示例
<?php $validationRules = [ 'username' => 'required|max_length[30]|alpha_numeric_space|min_length[3]', 'email' => [ 'rules' => 'required|max_length[254]|valid_email|is_unique[users.email]', 'errors' => [ 'required' => 'We really need your email.', ], ], ]; $model->setValidationRules($validationRules);
另一种方法是通过函数将验证消息设置为字段,
- CodeIgniter\Model::setValidationMessage($field, $fieldMessages)
- 参数:
$field (
string
) –$fieldMessages (
array
) –
此函数将设置字段级的错误消息。
用法示例
<?php $fieldName = 'name'; $fieldValidationMessage = [ 'required' => 'Your name is required here', ]; $model->setValidationMessage($fieldName, $fieldValidationMessage);
- CodeIgniter\Model::setValidationMessages($fieldMessages)
- 参数:
$fieldMessages (
array
) –
此函数将设置字段消息。
用法示例
<?php $fieldValidationMessage = [ 'name' => [ 'required' => 'Your baby name is missing.', 'min_length' => 'Too short, man!', ], ]; $model->setValidationMessages($fieldValidationMessage);
获取验证结果
现在,无论何时调用 insert()
、update()
或 save()
方法,数据都会被验证。如果验证失败,模型将返回布尔值 **false**。
获取验证错误
可以使用 errors()
方法检索验证错误。
<?php
if ($model->save($data) === false) {
return view('updateUser', ['errors' => $model->errors()]);
}
这将返回一个包含字段名称及其关联错误的数组,可用于在表单顶部显示所有错误,或单独显示它们。
<?php if (! empty($errors)): ?>
<div class="alert alert-danger">
<?php foreach ($errors as $field => $error): ?>
<p><?= esc($error) ?></p>
<?php endforeach ?>
</div>
<?php endif ?>
检索验证规则
可以通过访问模型的 validationRules
属性来检索模型的验证规则。
<?php
$rules = $model->validationRules;
也可以通过直接调用访问器方法并传入选项来检索这些规则的子集。
<?php
$rules = $model->getValidationRules($options);
$options
参数是一个关联数组,包含一个元素,其键为 'except'
或 'only'
,其值为感兴趣的字段名称数组。
<?php
// 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']]);
验证占位符
模型提供了一种简单的方法,可以根据传递给它的数据替换规则中的部分内容。这听起来可能比较晦涩,但对于 is_unique
验证规则来说特别有用。占位符只是作为 $data
传入的字段(或数组键)的名称,用花括号括起来。它将被匹配的传入字段的 **值** 替换。一个例子应该可以说明这一点。
<?php
namespace App\Models;
use CodeIgniter\Model;
class MyModel extends Model
{
protected $validationRules = [
'id' => 'max_length[19]|is_natural_no_zero',
'email' => 'required|max_length[254]|valid_email|is_unique[users.email,id,{id}]',
];
}
注意
从 v4.3.5 开始,必须为占位符字段 (id
) 设置验证规则。
在这组规则中,它指出电子邮件地址在数据库中应该是唯一的,除了与占位符值匹配的行的 id 除外。假设表单 POST 数据如下:
<?php
$_POST = [
'id' => 4,
'email' => '[email protected]',
];
那么 {id}
占位符将被数字 **4** 替换,从而得到以下修改后的规则:
<?php
namespace App\Models;
use CodeIgniter\Model;
class MyModel extends Model
{
protected $validationRules = [
'id' => 'max_length[19]|is_natural_no_zero',
'email' => 'required|max_length[254]|valid_email|is_unique[users.email,id,4]',
];
}
因此,当验证电子邮件是否唯一时,它将忽略数据库中具有 id=4
的行。
注意
从 v4.3.5 版本开始,如果占位符(id
)的值未通过验证,则不会替换占位符。
这也可以用于在运行时创建更动态的规则,只要您注意传入的任何动态键不会与您的表单数据冲突。
保护字段
为了帮助防止大规模赋值攻击,Model 类**要求**您在 $allowedFields 类属性中列出所有在插入和更新期间可以更改的字段名称。除了这些字段之外提供的任何数据都将在到达数据库之前被删除。这对于确保时间戳或主键不会被更改非常有用。
<?php
namespace App\Models;
use CodeIgniter\Model;
class MyModel extends Model
{
protected $allowedFields = ['name', 'email', 'address'];
}
有时,您会发现需要能够更改这些元素。这通常发生在测试、迁移或种子过程中。在这些情况下,您可以打开或关闭保护。
<?php
$model->protect(false)
->insert($data)
->protect(true);
运行时返回类型更改
您可以使用**find*()** 方法指定数据应以何种格式返回,作为类属性 $returnType。但是,有时您可能希望以不同的格式获取数据。Model 提供了允许您执行此操作的方法。
注意
这些方法只更改下一个**find*()** 方法调用的返回类型。之后,它将重置为其默认值。
asArray()
将下一个**find*()** 方法中的数据作为关联数组返回。
<?php
$users = $userModel->asArray()->where('status', 'active')->findAll();
asObject()
将下一个**find*()** 方法中的数据作为标准对象或自定义类实例返回。
<?php
// Return as standard objects
$users = $userModel->asObject()->where('status', 'active')->findAll();
// Return as custom class instances
$users = $userModel->asObject('User')->where('status', 'active')->findAll();
处理大量数据
有时,您需要处理大量数据,并且可能会面临内存不足的风险。为了简化此过程,您可以使用 `chunk()` 方法获取更小的数据块,然后对这些数据块进行处理。第一个参数是每次获取的行的数量。第二个参数是一个闭包,它将针对数据的每一行被调用。
这最适合在 cron 作业、数据导出或其他大型任务中使用。
<?php
$userModel->chunk(100, static function ($data) {
// do something.
// $data is a single row of data.
});
使用查询构建器
获取模型表的查询构建器
CodeIgniter 模型为该模型的数据库连接拥有一个查询构建器实例。您可以随时获取对 **共享** 查询构建器实例的访问权限。
<?php
$builder = $userModel->builder();
此构建器已使用模型的 $table 设置。
注意
获取查询构建器实例后,您可以调用 查询构建器 的方法。但是,由于查询构建器不是模型,因此您无法调用模型的方法。
获取另一个表的查询构建器
如果您需要访问另一个表,您可以获取另一个查询构建器实例。将表名作为参数传递,但请注意,这 **不会** 返回共享实例。
<?php
$groupBuilder = $userModel->builder('groups');
混合使用查询构建器和模型的方法
您也可以在同一个链式调用中使用查询构建器方法和模型的 CRUD 方法,从而实现非常优雅的使用方式。
<?php
$users = $userModel->where('status', 'active')
->orderBy('last_login', 'asc')
->findAll();
在这种情况下,它将操作模型持有的共享查询构建器实例。
重要
模型没有提供一个完美的查询构建器接口。模型和查询构建器是具有不同目的的独立类。不应期望它们返回相同的数据。
如果查询构建器返回结果,则会按原样返回。在这种情况下,结果可能与模型方法返回的结果不同,并且可能不是预期结果。模型的事件不会被触发。
为了防止意外行为,请勿使用返回结果的查询构建器方法,并在方法链的末尾指定模型的方法。
注意
您还可以无缝地访问模型的数据库连接。
<?php
$user_name = $userModel->escape($name);
模型事件
在模型执行过程中,您可以指定多个回调方法在多个点运行。这些方法可用于规范化数据、哈希密码、保存相关实体等等。
模型执行过程中的以下点可以通过类属性进行影响
注意
$beforeInsertBatch
, $afterInsertBatch
, $beforeUpdateBatch
和 $afterUpdateBatch
可从 v4.3.0 版本开始使用。
定义回调
您可以通过首先在模型中创建一个新的类方法来指定回调。
此类方法将始终接收一个 $data
数组作为其唯一参数。
$data
数组的具体内容在不同事件之间会有所不同,但始终包含一个名为 data
的键,其中包含传递给原始方法的主要数据。在 **insert*()** 或 **update*()** 方法的情况下,这将是正在插入数据库的键值对。主要的 $data
数组还将包含传递给方法的其他值,并在 事件参数 中详细说明。
回调方法必须返回原始的 $data
数组,以便其他回调拥有完整的信息。
<?php
namespace App\Models;
use CodeIgniter\Model;
class MyModel extends Model
{
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;
}
}
指定回调函数执行
您可以通过将方法名称添加到相应的类属性 ($beforeInsert,$afterUpdate 等) 来指定回调函数何时执行。多个回调函数可以添加到单个事件中,它们将按顺序依次处理。您可以在多个事件中使用相同的回调函数。
<?php
namespace App\Models;
use CodeIgniter\Model;
class MyModel extends Model
{
protected $beforeInsert = ['hashPassword'];
protected $beforeUpdate = ['hashPassword'];
}
此外,每个模型可以通过设置其 $allowCallbacks 属性来允许(默认)或拒绝回调函数在类级别执行。
<?php
namespace App\Models;
use CodeIgniter\Model;
class MyModel extends Model
{
protected $allowCallbacks = false;
}
您也可以使用 allowCallbacks()
方法暂时更改单个模型调用的此设置。
<?php
$model->allowCallbacks(false)->find(1); // No callbacks triggered
$model->find(1); // Callbacks subject to original property value
事件参数
由于传递给每个回调函数的精确数据略有不同,以下是有关传递给每个事件的 $data
参数中包含的内容的详细信息。
事件 |
$data 内容 |
---|---|
beforeInsert |
data = 要插入的键值对。如果将对象或 Entity 类传递给 |
afterInsert |
id = 新行的主键,或失败时为 0。data = 要插入的键值对。result = 通过 Query Builder 使用的 |
beforeUpdate |
id = 要更新的行的主键数组。data = 要更新的键值对。如果将对象或 Entity 类传递给 |
afterUpdate |
id = 要更新的行的主键数组。data = 要更新的键值对。result = 通过 Query Builder 使用的 |
beforeFind |
调用方法的名称,是否请求了单例,以及以下附加字段 |
|
无附加字段 |
|
id = 要搜索的行的主键。 |
|
limit = 要查找的行数。offset = 搜索时要跳过的行数。 |
afterFind |
与beforeFind相同,但包括结果数据行(如果未找到结果,则为 null)。 |
beforeDelete |
id = 传递给 |
afterDelete |
id = 传递给 |
beforeInsertBatch |
data = 要插入的值的关联数组。如果将对象或实体类传递给 |
afterInsertBatch |
data = 要插入的值的关联数组。result = 通过 Query Builder 使用的 |
beforeUpdateBatch |
data = 要更新的值的关联数组。如果将对象或实体类传递给 |
afterUpdateBatch |
data = 要更新的键值对。result = 通过 Query Builder 使用的 |
修改 Find* 数据
beforeFind
和afterFind
方法都可以返回一组修改后的数据,以覆盖模型的正常响应。对于afterFind
,对返回数组中的data
所做的任何更改都将自动传递回调用上下文。为了使beforeFind
拦截查找工作流,它还必须返回一个额外的布尔值returnData
<?php
namespace App\Models;
use CodeIgniter\Model;
class MyModel extends Model
{
protected $beforeFind = ['checkCache'];
// ...
protected function checkCache(array $data)
{
// Check if the requested item is already in our cache
if (isset($data['id']) && $item = $this->getCachedItem($data['id'])) {
$data['data'] = $item;
$data['returnData'] = true;
return $data;
}
// ...
}
}
手动模型创建
您无需扩展任何特殊类来为您的应用程序创建模型。您只需要获取数据库连接的实例,就可以开始了。这使您可以绕过 CodeIgniter 的 Model 为您提供的开箱即用的功能,并创建完全自定义的体验。
<?php
namespace App\Models;
use CodeIgniter\Database\ConnectionInterface;
class UserModel
{
protected $db;
public function __construct(ConnectionInterface $db)
{
$this->db = $db;
}
}