使用模型修改器和访问器来格式化数据库字段
Laravel中有一个非常简洁的特性,但由于它的名字听起来很复杂,常常被忽略。 我们谈论的是 访问器 和 修改器。
到底是什么 accessors and mutators?
- 访问器: 当中数据库检索时,格式化某些结果内容
- 修改器: 保存到数据库时格式化某些内容
这两个都可以帮助我们节省大量的时间,因为我们知道每当检索或保存信息到我们的数据库时,我们都会知道它将始终以正确的格式保存。
修改器最常见的用途之一是确保在将用户密码保存到数据库之前始终对其进行哈希处理。 这可确保您无需始终记住在保存用户的任何位置对密码哈希处理。
基础用法
对于我们的例子,假设我们有一个 User
Eloquent 模型。 这些是我们用户的字段:
- first_name
- last_name
- email
- username
- password
- expires_at
- explodes_at
- gets_mad_at
对于最基本的示例,假设我们总是希望确保在获取用户时,他们的名字是大写的。这样我们就可以向用户显示它,而不必担心所有的小写名称。
以下是我们创建用户时的代码:
$user = App\User::create([
'first_name' => 'chris',
'last_name' => 'sevilleja',
'email' => 'chris&64;scotch.io',
'password' => 'password',
'expires_at' => Carbon::now()->addMonth(),
'explodes_at' => Carbon::now()->addDay(),
'gets_mad_at' => Carbon::yesterday()
]);
我们的用户并不关心使用正确的标点输入他们的名字或姓氏,但我们仍然希望将他们的名字显示为大写。 如果我们将用户保存到数据库中,我们将需要使用访问器来格式化用户的 first_name
和 last_name
。
访问器使用 getAttribute
我们的 User
模型将定义一个访问器:
<?php
// app/User.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model {
/**
* 我们检索时始终将名字大写
*/
public function getFirstNameAttribute($value) {
return ucfirst($value);
}
/**
* 我们检索时始终将姓氏大写
*/
public function getLastNameAttribute($value) {
return ucfirst($value);
}
}
非常容易做到。 我们只定义一个 getAttribute()
方法,并确保我们使用属性名称( first_name
变为 getFirstName
)。
然后我们使用PHP的 ucfirst 的函数来大写第一个字母. 简单!
修改器使用 setAttribute
现在假设我们希望在将用户名保存到数据库时处理用户名的大写,以便我们的数据库在其所有名称字段中都是统一的。 就像我们创建 getAttribute
方法一样,我们将创建一个 setAttribute
方法。
这个过程本身会有所不同。
<?php
// app/User.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model {
/**
* 将名称保存到数据库时,始终将名字大写
*/
public function setFirstNameAttribute($value) {
$this->attributes['first_name'] = ucfirst($value);
}
/**
* 将名称保存到数据库时,始终将姓氏大写
*/
public function setLastNameAttribute($value) {
$this->attributes['last_name'] = ucfirst($value);
}
}
最大的区别是我们在这里给命名约定添加了 Attribute
而不是返回一些东西(我们没有得到任何东西),我们将使用 $this->attributes
直接调用模型上的属性。
现在每当我们保存到数据库时,我们都可以确保这些字段是大写的。
我该使用哪一种?
既然我们已经看到了访问器和修改器的作用,那么你将在什么时候使用它们呢?这完全取决于具体情况。有时你可能只需要使用访问器或者修改器,但有时候你可能需要同时使用这两种方法。
将密码哈希仅仅使用修改器的一个实例,我们不会在检索密码时取消隐藏密码。
在 Postgres 数据库中,同时使用访问器和修改器用于 json_encode
和 json_decode
JSON 对象。
让我们看一些更多的例子来获得窍门。
哈希密码
下面是一个非常简单的例子,来说明我们如何确保密码不会以纯文本的形式保存到数据库中。
<?php
// app/User.php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Hash;
class User extends Model {
/**
* 我们将密码保存至数据库时,总是哈希后保存。
*/
public function setPasswordAttribute($value) {
$this->attributes['password'] = Hash::make($value);
}
}
译者注:代码片段中的注释可能有误,所以我根据代码修改了注释
编码和解码JSON对象
假设我们在 User
模型中添加了一个名为 settings
的字段,我们将它们的所有设置存储在JSON串中。
// 获取 `scotchyscotch` 用户
$user = App\User::where('username', 'scotchyscotch')->first();
// 添加一些设置
$user->settings = [
'notifications' => true,
'location' => 'Las Vegas'
];
现在不会保存到我们的数据库中,因为他是一个数组相当于一个 json
字段。 我们可以在 json_encode
中设置 settings
字段,但记住在我们的应用程序中执行它会很繁琐。
当我们抓取用户时,我们还必须记住 json_decode
这个 settings
字段。
幸运的是,我们可以使用一个访问器和一个变量来确保这个 settings
字段总是我们想要的那样。
<?php
// app/User.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model {
/**
* 总是 json_decode settings字段,所以它们是可用的
*/
public function getSettingsAttribute($value) {
return json_decode($value);
// 你可以确保同时返回一个数组
// return json_decode($value, true);
}
/**
* 保存到数据库时,总是对 settings 字段进行 json_encode
* Always json_encode the settings when saving to the database
*/
public function setSettingsAttribute($value) {
$this->attributes['settings'] = json_encode($value);
}
}
Slug 化
另外一个简单的例子是,当我们将一篇文章保存到数据库中时,我们文章需要有一个 slug
。
// 文章
$title = 'What Does Water on Mars Mean?';
// slug
$slug = 'what-does-water-on-mars-mean';
我们不希望每次用户保存一篇文章时都让他们输入 slug 。我们可以使用一个修改器来为我们创建 slug !
<?php
// app/User.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model {
/**
* 根据标题创建 slug
*/
public function setSlugAttribute($value) {
// grab the title and slugify it
$this->attributes['slug'] = str_slug($this->title);
}
}
我们调用 $this->title
和 Laravel helper 的 str_slug 方法在保存模型时创建 slug
属性。
我们使用 $this->title
而不是 $this->attributes['title']
,因为实际上我们并没有设置标题,我们只是想获取它。
始终获取日期对象
它更容易使用 Carbon, 处理日期或时间时本机PHP DateTime
类的扩展。
// 这更容易
$date = Carbon::now();
$date = $date->addMonth();
// 比处理这个
$date = '2012-01-31 00:00:00';
$date = date('Y-m-d', strtotime('+1 month', $date));
DateTime
字符串不是最容易阅读或使用的字符串,因此我们通常必须使用Carbon来处理日期。 Laravel 轻松确保我们所有的日期都以正确的格式存储。
当我们保存日期时,我们可以像这样传入不同的日期:
// 获取 awesomedude 用户
$user = App\User::where('username', 'awesomedude')->first();
// 所有这些都是设置日期的有效方法
$user->expires_at = '2015-09-30 23:01:34';
$user->expires_at = '2015-06-26';
$user->expires_at = Carbon::now();
$user->expires_at = Carbon::tomorrow();
$user->expires_at = 1443765240; // unix 时间戳
// 保存用户
$user->save();
但是 Laravel 是如何知道哪些字段是日期的呢?这可以在 Eloquent 模型中 $dates
属性上定义。默认情况下, created_at
和 updated_at
字段会自动转换为日期。
与上面定义特定方法的访问器/修改器不同,我们可以覆盖默认的日期,并通过像这样覆盖模型上的 $dates
属性来设置更多要修改的日期字段:
<?php
// app/User.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model {
/**
* 更改为日期类型的属性
*
* @var array
*/
protected $dates = [
'created_at',
'updated_at',
'expires_at',
'gets_mad_at'
];
}
保存模型时,上面的所有字段都将设置为正确的日期类型。
相反,所有 $dates
字段将在检索后被转换为 Carbon 实例。
// 获取 awesomedudette 用户
$user = App\User::where('username', 'awesomedudette')->first();
// 查找此用户的过期时间前一小时
$explodeWarning = $user->explodes_at->subHour();
结论
在处理大型应用程序和大型开发团队时,在精确的位置安装安全阀总是一个好主意,这样简单的错误就不会出现。
使用访问器和修改器,整个应用程序在处理数据库时将始终采取某些操作。 没有纯文本密码,永远不必记住 json_decode
字段,并且始终具有易于使用的日期是使用另一个有用的 Laravel 功能的重要原因。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
推荐文章: