使用 CodeIgniter 的模型
模型
模型提供了一种与数据库中的特定表进行交互的方式。它们带有帮助程序方法的开箱即用,可用于与数据库表进行交互的许多标准方式,包括查找记录,更新记录,删除记录等。
访问模型
模型通常存储在app/Models
目录中。它们应具有与其目录中的位置匹配的名称空间,例如。namespace App\Models
您可以通过创建新实例或使用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的模型
CodeIgniter确实提供了一个模型类,该类提供了一些不错的功能,包括:
- 自动数据库连接
- 基本的CRUD方法
- 模型内验证
- 自动分页
- 和更多
此类为构建自己的模型提供了坚实的基础,使您可以快速构建应用程序的模型层。
建立模型
要利用CodeIgniter的模型,您只需创建一个扩展了以下内容的新模型类CodeIgniter\Model
:
<?php namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
}
这个空类提供了对数据库连接,查询生成器和许多其他便利方法的便捷访问。
连接到数据库
首次实例化该类时,如果没有数据库连接实例传递给构造函数,则它将自动连接到配置中设置的默认数据库组。您可以通过将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 $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
指定此模型主要使用的数据库表。这仅适用于内置CRUD方法。您不限于在自己的查询中仅使用此表。
$primaryKey
这是唯一标识此表中记录的列的名称。这不一定必须与数据库中指定的主键匹配,而是与诸如find()
知道要将指定值匹配到哪一列的方法一起使用。
注解
所有模型都必须指定一个primaryKey,以允许所有功能按预期工作。
$ returnType
该模型的CRUD方法将使您远离工作,并自动返回结果数据,而不是Result对象。此设置使您可以定义返回的数据类型。有效值是“数组”,“对象”或可以与Result对象的getCustomResultObject()方法一起使用的类的完全限定名称。
$ useSoftDeletes
如果为true,则将deleted_at
在数据库中设置所有delete 方法调用,而不是实际删除该行。当可以在其他地方引用数据时,这可以保留数据,也可以维护可以恢复的对象的“回收站”,甚至可以将其保留为安全跟踪的一部分。如果为true,则除非在调用find 方法之前调用withDeleted()方法,否则find *方法将仅返回未删除的行。
根据模型的$ dateFormat设置,这需要数据库中的DATETIME或INTEGER字段。但是,默认字段名称是deleted_at
此名称,可以使用$ deletedField属性将该名称配置为您选择的任何名称。
$ allowedFields
应该使用可以在保存,插入或更新方法期间设置的字段名称来更新此数组。除此以外的任何字段名称都将被丢弃。这有助于防止仅从表单中获取输入并将其全部扔给模型,从而导致潜在的批量分配漏洞。
$ useTimestamps
此布尔值确定是否将当前日期自动添加到所有插入和更新中。如果为true,将以$ dateFormat指定的格式设置当前时间。这要求表在适当的数据类型中具有名为“ created_at”和“ updated_at”的列。
$ createdField
指定用于保留数据记录创建时间戳的数据库字段。将其保留为空以避免更新(即使启用了useTimestamps)
$ updatedField
指定应使用哪个数据库字段来保留数据记录更新时间戳。将其保留为空以避免更新(即使启用了useTimestamps)
$ dateFormat
该值与$ useTimestamps和$ useSoftDeletes一起使用,以确保将正确类型的日期值插入数据库中。默认情况下,这将创建DATETIME值,但有效的选项为:datetime,date或int(PHP时间戳)。在无效或缺少dateFormat的情况下使用“ useSoftDeletes”或“ useTimestamps”会导致异常。
$ validationRules
包含如如何保存规则中所述的验证规则数组, 或包含同一部分中所述的包含验证组名称的字符串。在下面更详细地描述。
$ validationMessages
包含在验证期间应使用的自定义错误消息数组,如设置自定义错误消息中所述。在下面更详细地描述。
$ skipValidation
是否在全部inserts
和期间跳过验证updates
。默认值为false,表示将始终尝试验证数据。该方法主要使用该skipValidation()
方法,但可能会更改为该方法,true
因此该模型将永远无法验证。
$ beforeInsert $ afterInsert $ beforeUpdate $ afterUpdate $ afterFind $ afterDelete
这些数组允许您指定将在属性名称中指定的时间在数据上运行的回调方法。
处理数据
查找数据
提供了一些用于在表上执行基本CRUD工作的功能,包括find(),insert(),update(),delete()等。
find()
返回一行,其中主键与作为第一个参数传入的值匹配:
$user = $userModel->find($user_id);
该值以$ returnType中指定的格式返回。
您可以通过传递primaryKey值而不是一个数组来指定要返回的一行以上:
$users = $userModel->find([1,2,3]);
如果未传入任何参数,则将返回该模型表中的所有行,尽管不那么显式,但实际上类似于findAll()。
findColumn()
返回null或列值的索引数组:
$user = $userModel->findColumn($column_name);
$ column_name应该是单列的名称,否则您将获得DataException。
findAll()
返回所有结果:
$users = $userModel->findAll();
在调用此方法之前,可以根据需要插入查询生成器命令来修改此查询:
$users = $userModel->where('active', 1)
->findAll();
您可以分别将极限值和偏移值作为第一个和第二个参数传递:
$users = $userModel->findAll($limit, $offset);
first()
返回结果集中的第一行。最好与查询构建器结合使用。
$user = $userModel->where('deleted', 0)
->first();
withDeleted()
如果$ useSoftDeletes为true,则find 方法将不返回“ deleted_at IS NOT NULL”的任何行。要临时重写此方法,可以在调用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()
关联的数据数组作为唯一参数传递到此方法中,以在数据库中创建新的数据行。数组的键必须与$ table中的列名匹配,而数组的值是要为该键保存的值:
$data = [
'username' => 'darth',
'email' => 'd.vader@theempire.com'
];
$userModel->insert($data);
update()
更新数据库中的现有记录。第一个参数是要更新的记录的$ primaryKey。数据的关联数组作为第二个参数传递到此方法中。数组的键必须与$ table中的列名匹配,而数组的值是要为该键保存的值:
$data = [
'username' => 'darth',
'email' => 'd.vader@theempire.com'
];
$userModel->update($id, $data);
通过传递主键数组作为第一个参数,可以通过一次调用更新多个记录:
$data = [
'active' => 1
];
$userModel->update([1, 2, 3], $data);
当您需要更灵活的解决方案时,可以将参数留空,其功能类似于Query Builder的update命令,并具有验证,事件等附加优点:
$userModel
->whereIn('id', [1,2,3])
->set(['active' => 1])
->update();
save()
这是对insert()和update()方法的包装,这些方法根据是否找到与$ primaryKey值匹配的数组键来自动处理插入或更新记录:
// 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方法还可以通过识别非简单对象并将其公共值和受保护的值捕获到数组中,然后将其传递给适当的insert或update方法,从而使自定义类结果对象的使用变得更加简单。这使您可以非常干净地使用Entity类。实体类是表示对象类型的单个实例的简单类,例如用户,博客文章,工作等。此类负责维护围绕对象本身的业务逻辑,例如以某种方式格式化元素等。他们对如何将其保存到数据库一无所知。最简单地说,它们可能如下所示:
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
表中的数据,并以的实例返回所有结果App\Entities\Job
。当您需要将该记录保存到数据库中时,您将需要编写自定义方法,或者使用模型的save()
方法来检查类,获取所有公共和私有属性并将它们保存到数据库中:
// Retrieve a Job instance
$job = $model->find(15);
// Make some changes
$job->name = "Foobar";
// Save the changes
$model->save($job);
注解
如果您发现自己经常使用实体,则CodeIgniter提供了一个内置的Entity class ,该类提供了一些方便的功能,这些特性使开发实体变得更加简单。
删除资料
delete()
将主键值作为第一个参数,并从模型的表中删除匹配的记录:
$userModel->delete(12);
如果模型的$ useSoftDeletes值为true,则将更新该行以将其设置deleted_at
为当前日期和时间。您可以通过将第二个参数设置为true来强制永久删除。
可以将一个主键数组作为第一个参数传入,以一次删除多个记录:
$userModel->delete([1,2,3]);
如果没有传入任何参数,将像查询生成器的delete方法一样,之前需要进行where调用:
$userModel->where('id', 12)->delete();
purgeDeleted()
通过永久删除所有具有“ deleted_at IS NOT NULL”的行来清除数据库表。
$userModel->purgeDeleted();
验证数据
对于许多人来说,验证模型中的数据是确保数据保持单一标准而不重复代码的首选方法。模型类提供了一个方法可以自动保存到数据库中现有经确认的所有数据insert()
,update()
或save()
方法。
第一步是$validationRules
使用应该应用的字段和规则填充class属性。如果您有要使用的自定义错误消息,请将它们放在$validationMessages
数组中:
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.'
]
];
}
通过功能将验证消息设置为字段的另一种方法,
setValidationMessage
($ field,$ fieldMessages )
参数: | $ field(string)– |
---|---|
$ fieldMessages(array)- |
此功能将设置现场智能错误消息。
用法示例:
$fieldName = 'name';
$fieldValidationMessage = [
'required' => 'Your name is required here',
];
$model->setValidationMessage($fieldName, $fieldValidationMessage);
setValidationMessages
($ fieldMessages )
参数: | $ fieldMessages(array)- |
---|---|
此功能将设置现场消息。
用法示例:
$fieldValidationMessage = [
'name' => [
'required' => 'Your baby name is missing.',
'min_length' => 'Too short, man!',
],
];
$model->setValidationMessages($fieldValidationMessage);
现在,只要您使用insert()
,update()
或save()
方法,数据将被验证。如果失败,则模型将返回boolean false。您可以使用该errors()
方法来检索验证错误:
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><?= $error ?></p>
<?php endforeach ?>
</div>
<?php endif ?>
如果您希望在Validation配置文件中组织规则和错误消息,则可以这样做,只需将其设置$validationRules
为创建的验证规则组的名称即可:
class UserModel extends Model
{
protected $validationRules = 'users';
}
检索验证规则
您可以通过访问模型的validationRules
属性来检索模型的验证规则:
$rules = $model->validationRules;
您还可以通过直接调用带有选项的accessor方法来仅检索这些规则的子集:
$rules = $model->getValidationRules($options);
该$options
参数是一个带有一个元素的关联数组,其键为“ except”或“ only”,并且其值为一个感兴趣的字段名数组。
// 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传入的字段(或数组键)的名称,并用大括号括起来。它将替换为匹配的传入字段的值。一个例子应该澄清这一点:
protected $validationRules = [
'email' => 'required|valid_email|is_unique[users.email,id,{id}]'
];
在这套规则中,它声明电子邮件地址在数据库中应该是唯一的,除了ID与占位符值匹配的行之外。假设表单POST数据具有以下内容:
$_POST = [
'id' => 4,
'email' => 'foo@example.com'
];
然后将{id}
占位符替换为数字4,从而给出此修改后的规则:
protected $validationRules = [
'email' => 'required|valid_email|is_unique[users.email,id,4]'
];
因此,它将在id=4
验证电子邮件唯一时忽略数据库中具有该行的行。
只要您确保传入的任何动态键都不会与表单数据冲突,这也可以用于在运行时创建更多动态规则。
保护领域
为了防止大规模分配攻击,Model类要求您在$allowedFields
class属性中列出所有在插入和更新期间可以更改的字段名称。除这些数据外,在访问数据库之前,还将删除所有提供的数据。这对于确保不更改时间戳或主键非常有用。
protected $allowedFields = ['name', 'email', 'address'];
有时,您会发现需要更改这些元素的时间。这通常是在测试,迁移或种子期间。在这些情况下,您可以打开或关闭保护:
$model->protect(false)
->insert($data)
->protect(true);
使用查询生成器
您可以在需要时随时访问该模型的数据库连接的查询生成器的共享实例:
$builder = $userModel->builder();
该构建器已使用模型的$ table设置。
您还可以在同一链接调用中使用查询生成器方法和模型的CRUD方法,从而可以非常优雅地使用:
$users = $userModel->where('status', 'active')
->orderBy('last_login', 'asc')
->findAll();
注解
您还可以无缝访问模型的数据库连接:
$user_name = $userModel-&escape($name);
运行时返回类型更改
您可以指定将find *()方法用作类属性$ returnType时应返回数据的格式。但是,有时候您可能希望数据以其他格式返回。该模型提供了允许您执行此操作的方法。
注解
这些方法仅更改下一个find *()方法调用的返回类型。之后,将其重置为其默认值。
asArray()
从下一个find *()方法作为关联数组返回数据:
$users = $userModel->asArray()->where('status', 'active')->findAll();
asObject()
从下一个find *()方法返回数据作为标准对象或自定义类实例:
// 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()方法获取较小的数据块,然后再进行处理。第一个参数是单个块中要检索的行数。第二个参数是一个Closure,将为每一行数据调用。
最好在cronjobs,数据导出或其他大型任务期间使用。
$userModel->chunk(100, function ($data)
{
// do something.
// $data is a single row of data.
});
模型事件
您可以在模型的执行中指定多个要运行的回调方法。这些方法可用于规范化数据,哈希密码,保存相关实体等等。可以通过类属性来影响模型执行中的以下几点:$ beforeInsert,$ afterInsert, $ beforeUpdate,afterUpdate,afterFind和afterDelete。
定义回调
通过首先在模型中创建要使用的新类方法来指定回调。此类将始终收到$ data数组作为其唯一参数。$ data数组的确切内容在事件之间会有所不同,但始终包含一个名为data的键,该键包含传递给原始方法的主要数据。对于insert 或update 方法,这将是要插入数据库中的键/值对。主数组还将包含传递给该方法的其他值,稍后将进行详细说明。回调方法必须返回原始的$ data数组,以便其他回调具有完整的信息。
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等)中,可以指定何时运行回调。可以将多个回调添加到单个事件中,并将它们依次处理。您可以在多个事件中使用相同的回调:
protected $beforeInsert = ['hashPassword'];
protected $beforeUpdate = ['hashPassword'];
事件参数
由于传递给每个回调的确切数据略有不同,因此以下是传递给每个事件的$ data参数的详细信息:
事件 | $数据内容 |
---|---|
beforeInsert | data =要插入的键/值对。如果将对象或实体类传递给insert方法,则首先将其转换为数组。 |
afterInsert | id =新行的主键,失败则为0。 data =要插入的键/值对。 result =通过查询生成器使用的insert()方法的结果。 |
beforeUpdate | id =要更新的行的主键。 data =要插入的键/值对。如果将对象或实体类传递给insert方法,则首先将其转换为数组。 |
afterUpdate | id =要更新的行的主键。 data =要更新的键/值对。 result =通过查询生成器使用的update()方法的结果。 |
afterFind | 因find *方法而异。请参阅以下内容: |
find() | id =要搜索的行的主键。 data =结果数据行;如果未找到结果,则为null。 |
findAll() | data = data的结果行;如果未找到结果,则为null。 limit =查找的行数。 offset =搜索期间要跳过的行数。 |
first() | data =在搜索过程中找到的结果行;如果未找到,则返回null。 |
beforeDelete | 因delete *方法而异。请参阅以下内容: |
delete() | id =要删除的行的主键。 purge = boolean是否应该硬删除软删除行。 |
afterDelete | 因delete *方法而异。请参阅以下内容: |
delete() | id =要删除的行的主键。 purge = boolean是否应该硬删除软删除行。 result =查询生成器上delete()调用的结果。 data =未使用。 |
手动创建模型
您无需扩展任何特殊的类即可为您的应用程序创建模型。您所需要做的就是获取数据库连接的实例,一切顺利。这使您可以绕开CodeIgniter的模型为您提供的即用型功能,并创建完全自定义的体验。
<?php namespace App\Models;
use CodeIgniter\Database\ConnectionInterface;
class UserModel
{
protected $db;
public function __construct(ConnectionInterface &$db)
{
$this->db =& $db;
}
}