@这是小豪的第九篇文章
好久没有更新文章了,说好了周更结果还是被自己对时间的安排打败了。。。。
今天给大家介绍的是在
Laravel
中如何灵活的使用Trait
,说起Trait
,我一开始不知道是什么样的存在,有个模糊的印象是:复用。一直以来对复用的理解和使用就是:写在一个公共类中,哪里需要哪里调用,目的就是少写些代码,哈哈。废话不多说,现在开始。
前言
大家可能经常看到以下几种情况:
class Post extends Model
{
protected static function boot()
{
parent::boot();
static::saving(function ($post){
$post->creator_id = $post->creator_id ?? auth()->id();
});
}
}
// 或者
class Video extends Model
{
protected static function boot()
{
parent::boot();
static::saving(function ($post){
$post->creator_id = $post->creator_id ?? auth()->id();
});
}
}
// 或者直接在控制器中指定 creator_id
可以看到,这些代码明显是重复的,可是到底怎么分离出去达到复用的效果呢。
这样?
public function hasCreator($model)
{
$model->creator_id = $model->creator_id ?? auth()->id();
}
// 封装一个上述公共方法,然后在模型中调用,或者在控制器中调用。
从上面的示例中发现这些操作都不是很好,不够优雅,哈哈。现在我们来看看 laravel
中 Trait
是如何定义和使用的:
// 定义
trait HasCreator
{
public static function bootHasCreator()
{
static::saving(function ($model) {
$model->creator_id = $model->creator_id ?? auth()->id();
});
}
}
// 调用
class Post extends Model
{
use HasCreator;
}
// 可以了,哈哈,自动调用已经可以实现对 creator_id 的自动写入了,是不是很优雅,哈哈。
现在一步步的来解释一下是怎么写的。
开始
官方解释: Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。 Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。。
-
首先我们得知道如何定义一个
Trait
, 使用的关键字是trait
而不是class
namespace AppTraits; trait HasCreator { }
-
定义方法(我们先从简单的来)
namespace AppTraits; trait HasCreator { public static function hasCreator() { static::saving(function ($model) { $model->creator_id = $model->creator_id ?? 1; }); } }
可以看到在
Trait
中声明了一个setCreator
方法,里面里面依旧是对creator
设置默认值 -
调用
namespace App; use AppTraitsHasCreator; use IlluminateDatabaseEloquentModel; use IlluminateDatabaseEloquentSoftDeletes; class Post extends Model { use HasCreator, SoftDeletes; protected $fillable = ['title', 'user_id']; protected static function boot() { parent::boot(); self::hasCreator(); } }
用我的理解来说就是将
Trait
中的方法合并到 模型中去了,要想使用就use
一下,然后当自己声明的一样去调用就好了。
大家可以看到上面的例子中还 use
了 SoftDeletes
, 我们来简单的看一下它的源码:
namespace IlluminateDatabaseEloquent;
trait SoftDeletes
{
/**
* Indicates if the model is currently force deleting.
*
* @var bool
*/
protected $forceDeleting = false;
/**
* Boot the soft deleting trait for a model.
*
* @return void
*/
public static function bootSoftDeletes()
{
static::addGlobalScope(new SoftDeletingScope);
}
/**
* Force a hard delete on a soft deleted model.
*
* @return bool|null
*/
public function forceDelete()
{
$this->forceDeleting = true;
return tap($this->delete(), function ($deleted) {
$this->forceDeleting = false;
if ($deleted) {
$this->fireModelEvent('forceDeleted', false);
}
});
}
......
}
从展示的源码中我们可以看到,当前 Trait
定义了一个属性、两个方法,居然还可以定义属性,是不是很意外,哈哈。
大家可能会问,要是 Task
中也定义了 $forceDeleting
属性怎么办,哪个为主呢,这里面其实有个优先级的:调用类 >Trait > 父类,也就是说当 Trait 中出现于调用类重复的属性和方法的时候,默认是以调用类为主的。
接下来我们来看下面两个方法:
bootSoftDeletes
:静态、前缀加了 boot, 这表示啥呢?表示默认执行的操作,哈哈。
既然可以定义为自动调用,我们是不是把上面的 HasCreator
改一下呢:
namespace AppTraits;
trait HasCreator
{
public static function hasCreator() // -> 改为 bootHasCreator
{
static::saving(function ($model) {
$model->creator_id = $model->creator_id ?? 1;
});
}
}
已经自动调用了,那么:
namespace App;
use AppTraitsHasCreator;
use IlluminateDatabaseEloquentModel;
use IlluminateDatabaseEloquentSoftDeletes;
class Post extends Model
{
use HasCreator, SoftDeletes;
protected $fillable = ['title', 'user_id'];
}
这样就可以啦!
后面的那个方法和之前的 hasCreator
是一样的,当作自身的方法调用就好啦,是否声明为静态就看自己的需要了。
下面给大家推荐一些在项目中用得到的 Trait
,都是从超哥那里摘下来的,哈哈。
小案例
HasCreator
指定创建者
namespace AppTraits;
use AppUser;
/**
* Trait HasCreator.
*
* @property AppUser $creator
*/
trait HasCreator
{
public static function bootHasCreator()
{
static::saving(function ($model) {
$model->creator_id = $model->creator_id ?? auth()->id();
});
}
/**
* @return IlluminateDatabaseEloquentRelationsBelongsTo
*/
public function creator()
{
return $this->belongsTo(User::class, 'creator_id')->withTrashed();
}
/**
* @param AppUser|int $user
*
* @return bool
*/
public function isCreatedBy($user)
{
if ($user instanceof User) {
$user = $user->id;
}
return $this->creator_id == intval($user);
}
}
Trait
中定义了三个方法,现在给大家简单的解释一哈:
- bootHasCreator:默认给定当前认证用户。至于下面的
static::saving
不明白的,可以看之前的文章哒。 - creator:定义模型关联
- isCreatedBy:判断传入的用户是否为当前创建者
BelongsToUser
指定用户
namespace AppTraits;
use AppUser;
/**
* Trait BelongsToUser.
*
* @property AppUser $user
*/
trait BelongsToUser
{
public static function bootBelongsToUser()
{
static::creating(function ($model) {
if (!$model->user_id) {
$model->user_id = auth()->id();
}
});
}
/**
* @return IlluminateDatabaseEloquentRelationsBelongsTo
*/
public function user()
{
return $this->belongsTo(User::class)->withTrashed();
}
/**
* @param AppUser|int $user
*
* @return bool
*/
public function isOwnedBy($user)
{
if ($user instanceof User) {
$user = $user->id;
}
return $this->user_id == intval($user);
}
}
我就不解释啦,和上面的是差不多的,大家看看就明白了。
结束语
就简单的给大家介绍一下 Trait
在 Laravel
中如何使用的,写的不对的地方和补充欢迎大家留言噢,哈哈。
相关链接: 《我所理解的 PHP Trait》 ( 对 Trait 更深层次的讲解 -- 超哥出品,哈哈)、 《掌握 PHP Trait 的概念和用法》