Laravel日常笔记系列之一

- 6 mins

1. 简介

当你在 Eloquent 模型实例中获取或设置某些属性值的时候,访问器和修改器允许你对 Eloquent 属性值进行格式化。例如,你可能想要使用 Laravel 加密器 来加密一个即将被保存在数据库中的值,当你从 Eloquent 模型访问该属性时,其值将被自动解密。

除了自定义访问器和修改器之外,Eloquent 也会自动将日期字段类型转换成 Carbon 实例或将 文本字段类型转换成 JSON。

1.1. 访问器&修改器

1.1.1. 定义一个访问器

若要定义一个访问器,则须在你的模型上创建一个 getFooAttribute 方法。要访问的Foo 字段需使用「驼峰式」来命名。在这个例子中,我们将为 first_name 属性定义一个访问器。当 Eloquent 尝试获取 first_name 的值时,将会自动调用此访问器:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 获取用户的名字。
     *
     * @param  string  $value
     * @return string
     */
    public function getFirstNameAttribute($value)
    {
        return ucfirst($value);
    }
}

如你所见,字段的原始值被传递到访问器中,允许你对它进行处理并返回结果。如果想获取被修改后的值,你可以在模型实例上访问 first_name 属性:

$user = App\User::find(1);

$firstName = $user->first_name;

当然,你也可以通过已有的属性,使用访问器返回新的计算值:

/**
 * 获取用户名全称
 *
 * @return string
 */
public function getFullNameAttribute()
{
  return "{$this->first_name} {$this->last_name}";
}

1.1.2. 定义一个修改器

若要定义一个修改器,则须在模型上定义一个 setFooAttribute 方法。要访问的 Foo 字段需使用「驼峰式」来命名。让我们再来定义 first_name 属性的修改器。当我们尝试在模型上设置 first_name 的值时,该修改器将被自动调用:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 设定用户的名字。
     *
     * @param  string  $value
     * @return void
     */
    public function setFirstNameAttribute($value)
    {
        $this->attributes['first_name'] = strtolower($value);
    }
}

修改器会获取属性已经被设置的值,允许你操作该值并将其设置到 Eloquent 模型内部的 $attributes 属性上。举个例子,如果我们尝试将 first_name 属性设置成 Sally

$user = App\User::find(1);

$user->first_name = 'Sally';

在这个例子中,setFirstNameAttribute 方法在调用的时候会接收 Sally 这个值作为参数。接着修改器会使用 strtolower 函数并将其值设置到内部的 $attributes 数组。

1.2. 访问器实现原理

  1. 当访问到不存在的成员属性时,则调用__get方法,然后调用getAttribute()方法 __get 2.在getAttribute方法中,在hasGetMutator方法中判断是否存在该修改器,存在则继续调用修改后值 在这里插入图片描述 3.在getAttributeValue方法中, 可以看到先判断是否有Mutator 然后在判断是否Cast 在这里插入图片描述 4.在mutateAttribute方法中可以看出使用「驼峰式」拼接出该方法并调用 在这里插入图片描述 修改器实现方式相似,是通过__set实现。

1.3. 日期转换器

默认情况下,Eloquent 将会把 created_at updated_at 字段转换成 Carbon 实例,它继承了 PHP 原生的 DateTime 类,并提供了各种有用的方法。你可以通过重写模型的 $dates 属性,自行定义哪些日期类型字段会被自动转换,或者完全禁止所有日期类型字段的转换:

?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 应被转换为日期的属性。
     *
     * @var array
     */
    protected $dates = [
        'created_at',
        'updated_at',
        'deleted_at'
    ];
}

当某个字段被认为是日期格式时,你或许想将其数值设置成一个 UNIX 时间戳、日期字符串(Y-m-d)、日期时间( date-time)字符串,当然还有 DateTimeCarbon实例,并且让日期值自动正确地保存到你的数据库中:

$user = App\User::find(1);

$user->deleted_at = Carbon::now();

$user->save();

就如上面所说的,当获取到的属性包含在 $dates 属性时,都将会自动转换成 Carbon 实例,允许你在属性上使用任意的 Carbon 方法:

$user = App\User::find(1);

return $user->deleted_at->getTimestamp();

1.3.1. 日期格式

默认情况下,时间戳将会以 'Y-m-d H:i:s' 的形式格式化。如果你想要自定义自己的时间戳格式,可在模型中设置 $dateFormat 属性。该属性决定了日期属性应以何种格式被保存到数据表中,以及当模型被序列化成数组或是 JSON 格式时,这些日期属性以何种格式被保存:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * 模型的日期字段的保存格式。
     *
     * @var string
     */
    protected $dateFormat = 'U';
}

指出$dateFormat 可以接受哪些值。 这里科普一下让小伙伴少走弯路。 这个$dateFormat可以接受的值和phpdate()函数第一个参数可以接受的值一样。 U的意思就是我们平常说的时间戳(10位到秒) php在线手册地址:http://php.net/manual/zh/function.date.php 在这里插入图片描述

1.4. 属性类型转换

$casts 属性在模型中提供了一个便利的方法来将属性转换为常见的数据类型。$casts 属性应是一个数组,且数组的键是那些需要被转换的属性名称,值则是你希望转换的数据类型。支持转换数据类型有:

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model { /** * 应该被转换成原生类型的属性。 * * @var array */ protected $casts = [ ‘is_admin’ => ‘boolean’, ]; }

现在当你访问 `is_admin` 属性时,它将会被转换成布尔值类型,即便保存在数据库里的的值是一个整数类型:
```php
$user = App\User::find(1);

if ($user->is_admin) {
    //
}

在上面getAttributeValue方法中看到castAttribute方法如下,即通过相应类型就行转换:

/**
     * Cast an attribute to a native PHP type.
     *
     * @param  string  $key
     * @param  mixed  $value
     * @return mixed
     */
    protected function castAttribute($key, $value)
    {
        if (is_null($value)) {
            return $value;
        }

        switch ($this->getCastType($key)) {
            case 'int':
            case 'integer':
                return (int) $value;
            case 'real':
            case 'float':
            case 'double':
                return (float) $value;
            case 'string':
                return (string) $value;
            case 'bool':
            case 'boolean':
                return (bool) $value;
            case 'object':
                return $this->fromJson($value, true);
            case 'array':
            case 'json':
                return $this->fromJson($value);
            case 'collection':
                return new BaseCollection($this->fromJson($value));
            case 'date':
                return $this->asDate($value);
            case 'datetime':
                return $this->asDateTime($value);
            case 'timestamp':
                return $this->asTimestamp($value);
            default:
                return $value;
        }
    }

1.4.1. 数组 & JSON 转换

如果一个字段是以被序列化的 JSON 来存储在数据库中, array 类型转换将会非常有用。例如,当你在 Eloquent 模型上访问的某个属性在数据库里是一个 JSON TEXT 字段类型,它包含了被序列化的 JSON,而且你对该字段添加了 array 类型转换,那么它将会自动反序列化成一个 PHP 数组:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 应该被转换成原生类型的属性。
     *
     * @var array
     */
    protected $casts = [
        'options' => 'array',
    ];
}

一旦类型转换被定义,你就可以访问 options 属性,它将会自动把 JSON 反序列化成一个 PHP 数组。当你设置 options 属性的值时,接收到的数组将会被自动序列化成 JSON 以便进行保存:

$user = App\User::find(1);

$options = $user->options;

$options['key'] = 'value';

$user->options = $options;

$user->save();

1.5. 参考文档

1.6. 最后

该文为我对Laravel一些常用知识的笔记,其中大部分来自于Laravel5.5官方手册,部分为laravel5.4源码截图。 写的不好请见谅!

rss facebook twitter github gitlab youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora