@這是小豪的第九篇文章
好久沒有更新文章了,說好了周更結果還是被自己對時間的安排打敗了。。。。
今天給大家介紹的是在
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 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);
}
}
我就不解釋啦,和上面的是差不多的,大家看看就明白了。
結束語
就簡單的給大家介紹一下 Trait
在 Laravel
中如何使用的,寫的不對的地方和補充歡迎大家留言噢,哈哈。
相關連結: 《我所理解的 PHP Trait》 ( 對 Trait 更深層次的講解 -- 超哥出品,哈哈)、 《掌握 PHP Trait 的概念和用法》