大家可能经常看到以下几种情况:

    1. class Post extends Model
    2. {
    3. protected static function boot()
    4. {
    5. parent::boot();
    6. static::saving(function ($post){
    7. $post->creator_id = $post->creator_id ?? \auth()->id();
    8. });
    9. }
    10. }

    或者

    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 App\Traits;
    
    trait HasCreator
    {
    
    }
    

    定义方法(我们先从简单的来)

    namespace App\Traits;
    
    trait HasCreator
    {
        public static function hasCreator()
        {
            static::saving(function ($model) {
                    $model->creator_id = $model->creator_id ?? 1;
            });
        }
    }
    

    可以看到在 Trait 中声明了一个 setCreator 方法,里面里面依旧是对 creator 设置默认值
    调用

    namespace App;
    
    use App\Traits\HasCreator;
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\SoftDeletes;
    
    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 Illuminate\Database\Eloquent;
    
    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 App\Traits;
    
    trait HasCreator
    {
        public static function hasCreator()  // -> 改为 bootHasCreator
        {
            static::saving(function ($model) {
                $model->creator_id = $model->creator_id ?? 1;
            });
        }
    }
    

    已经自动调用了,那么:

    namespace App;
    
    use App\Traits\HasCreator;
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\SoftDeletes;
    
    class Post extends Model
    {
            use HasCreator, SoftDeletes;
            protected $fillable = ['title', 'user_id'];
    }
    

    这样就可以啦!
    后面的那个方法和之前的 hasCreator 是一样的,当作自身的方法调用就好啦,是否声明为静态就看自己的需要了。
    下面给大家推荐一些在项目中用得到的 Trait,都是从超哥那里摘下来的,哈哈。
    小案例
    HasCreator
    指定创建者

    namespace App\Traits;
    
    use App\User;
    
    /**
    • Trait HasCreator.
    • 
    • @property  \App\User $creator 
    */
    trait HasCreator
    {
        public static function bootHasCreator()
        {
                static::saving(function ($model) {
                $model->creator_id = $model->creator_id ?? \auth()->id();
                });
        }
    
        /**
        • @return  \Illuminate\Database\Eloquent\Relations\BelongsTo 
        */
        public function creator()
        {
                return $this->belongsTo(User::class, 'creator_id')->withTrashed();
        }
    
        /**
        • @param  \App\User|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 App\Traits;
    
    use App\User;
    
    /**
    • Trait BelongsToUser.
    • 
    • @property  \App\User $user 
    */
    trait BelongsToUser
    {
        public static function bootBelongsToUser()
        {
            static::creating(function ($model) {
                if (!$model->user_id) {
                        $model->user_id = \auth()->id();
                }
            });
        }
    
        /**
        • @return  \Illuminate\Database\Eloquent\Relations\BelongsTo 
        */
        public function user()
        {
                return $this->belongsTo(User::class)->withTrashed();
        }
    
        /**
        • @param  \App\User|int $user 
        • 
        • @return  bool 
        */
        public function isOwnedBy($user)
        {
            if ($user instanceof User) {
                    $user = $user->id;
            }
                return $this->user_id == \intval($user);
        }
    
    }
    

    我就不解释啦,和上面的是差不多的,大家看看就明白了。