zoukankan      html  css  js  c++  java
  • 如何在 Laravel 中灵活的使用 Trait

    如何在 Laravel 中灵活的使用 Trait
     

    @这是小豪的第九篇文章

    好久没有更新文章了,说好了周更结果还是被自己对时间的安排打败了。。。。

    今天给大家介绍的是在 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 类相关典型问题。。

    1. 首先我们得知道如何定义一个 Trait, 使用的关键字是 trait 而不是 class

      namespace AppTraits;
      
      trait HasCreator
      {
      }
      
    2. 定义方法(我们先从简单的来)

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

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

    3. 调用

      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 中定义了三个方法,现在给大家简单的解释一哈:

    1. bootHasCreator:默认给定当前认证用户。至于下面的 static::saving 不明白的,可以看之前的文章哒。
    2. creator:定义模型关联
    3. 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 的概念和用法》

  • 相关阅读:
    UVA 10617 Again Palindrome
    UVA 10154 Weights and Measures
    UVA 10201 Adventures in Moving Part IV
    UVA 10313 Pay the Price
    UVA 10271 Chopsticks
    Restore DB後設置指引 for maximo
    每行SQL語句加go換行
    种服务器角色所拥有的权限
    Framework X support IPV6?
    模擬DeadLock
  • 原文地址:https://www.cnblogs.com/mouseleo/p/10982963.html
Copyright © 2011-2022 走看看