前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >在 Laravel 中使用 Trait 优化代码结构

在 Laravel 中使用 Trait 优化代码结构

作者头像
overtrue
发布2019-03-11 16:41:48
1.5K0
发布2019-03-11 16:41:48
举报

今天给大家介绍的是在 Laravel 中使用 Trait 优化代码结构,说起 Trait ,我一开始不知道是什么样的存在,有个模糊的印象是:复用。一直以来对复用的理解和使用就是:写在一个公共类中,哪里需要哪里调用,目的就是少写些代码,哈哈。

前言

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

代码语言:javascript
复制
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

可以看到,这些代码明显是重复的,可是到底怎么分离出去达到复用的效果呢。

这样?

代码语言:javascript
复制
public function hasCreator($model){    $model->creator_id = $model->creator_id ?? \auth()->id();}
// 封装一个上述公共方法,然后在模型中调用,或者在控制器中调用。

从上面的示例中发现这些操作都不是很好,不够优雅,哈哈。现在我们来看看 laravelTrait 是如何定义和使用的:

代码语言:javascript
复制
// 定义
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

代码语言:javascript
复制
namespace App\Traits;
trait HasCreator{}

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

代码语言:javascript
复制
namespace App\Traits;
trait HasCreator{    public static function hasCreator()    {        static::saving(function ($model) {            $model->creator_id = $model->creator_id ?? 1;        });    }}

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

3.调用

代码语言:javascript
复制
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 一下,然后当自己声明的一样去调用就好了。

大家可以看到上面的例子中还 useSoftDeletes , 我们来简单的看一下它的源码:

代码语言:javascript
复制
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 改一下呢:

代码语言:javascript
复制
    namespace App\Traits;
    trait HasCreator    {        public static function hasCreator()  // -> 改为 bootHasCreator        {            static::saving(function ($model) {                $model->creator_id = $model->creator_id ?? 1;            });        }    }

已经自动调用了,那么:

代码语言:javascript
复制
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

指定创建者

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

  1. bootHasCreator:默认给定当前认证用户。至于下面的 static::saving 不明白的,可以看之前的文章哒。
  2. creator:定义模型关联
  3. isCreatedBy:判断传入的用户是否为当前创建者
BelongsToUser

指定用户

代码语言:javascript
复制
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);    }}

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

结束语

就简单的给大家介绍一下 TraitLaravel 中如何使用的,写的不对的地方和补充欢迎大家留言噢,哈哈。

相关链接:https://overtrue.me/articles/2016/04/about-php-trait.html

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-02-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 假装我会写代码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 开始
  • 小案例
    • HasCreator
      • BelongsToUser
      • 结束语
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档