Laravel 7 即将到来(预定3月3日发布),它提供了一大批令人兴奋的新功能与改进。我最期待的功能之一就是 自定义Eloquent类型转换。
在历史上,你只能使用Laravel提供的默认类型转换集,它覆盖了基本语言类型以及日期。虽然有一些现有的包提供了自定义类型转换,但它们有一个主要的缺点。由于它们通过特质覆盖了 setAttribute
和 getAttribute
方法,因此它们不能与任何其他也覆盖这些方法的包一起使用。
现在Laravel 7原生支持了这些类型转换,从而在库之间不会出现兼容性问题!
自定义Eloquent类型转换是如何工作的
任何实现Laravel提供的新 CastsAttributes
合约的对象现在都可以用于模型上的 $casts
属性。当访问模型上的属性时,Eloquent会首先检查是否存在自定义类型转换来转换值,然后再将值返回给你。
请记住,你的类型转换将在模型的每个单个获取和设置操作上被调用,所以请考虑缓存耗时的操作。
创建自定义类型转换
Laravel的一个受欢迎的功能建议是允许选择性对模型属性进行加密,而使用自定义类型转换,这可以非常简单实现。
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class EncryptCast implements CastsAttributes
{
public function get($model, $key, $value, $attributes)
{
return encrypt($value);
}
public function set($model, $key, $value, $attributes)
{
return decrypt($value);
}
}
在你的模型中,你还可以将一个属性分配给我们刚刚创建的类型转换。
class TestModel extends Model
{
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'secret' => EncryptCast::class,
];
}
现在我们已经设置好了,让我们来测试一下!由于 encrypt
/decrypt
函数会序列化输入,所以你可以存储几乎任何内容(但你可能应该坚持使用简单的内置类型)。
$model = new TestModel();
$model->secret = 'Hello World'; // Plain text value
// Encrypted value (which will be saved to the database)
// Raw Value: eyJpdiI6InV4Q25ZN0FZUW5YSEZkRCtZSGlVXC9BPT0iLCJ2Y...
$model->getAttributes()['secret'];
$model->save(); // Save & reload the model from the DB
$model->fresh();
echo $model->secret; // Hello World
因为自定义转换仅是对象,所以可以根据需要设置得简单或复杂。在实现中还有一些附加功能,包括“入站”转换(只转换设置的值)以及从模型中的 $casts
声明中获取配置的能力。
例如,我们可以限制字符串的长度(只有当设置值时),因此任何现有的值长度将保持不变。
class LimitCaster implements CastsInboundAttributes
{
public function __construct($length = 25)
{
$this->length = $length;
}
public function set($model, $key, $value, $attributes)
{
return [$key => Str::limit((string) $value, $this->length)];
}
}
在模型中,用冒号将转换的类名和参数分开。
class TestModel extends Model
{
protected $casts = [
'limited_str' => LimitCaster::class . ':10', // Limit to 10 characters
];
}
总结
我很期待看到社区在 Laravel 7 测试版发布后提供的那些精彩的转换。将自定义转换发布为包也非常简单,所以希望未来能看到许多包含自定义转换的包。
在我之前的项目中错过的一个是 DateInterval
或 CarbonInterval
转换,所以我 发布了一个包,以防其他人也遇到过相同的问题。
有什么疑问吗?欢迎在下面评论,我将尽力回答。
进一步阅读
driesvints, mariosoftmedic 赞同这篇文章