laravel 框架大量使用了traits. 簡單舉幾個例子:
在Eloquent中使用了trait 。然後在model初始化的時候,有個boot方法,會自動判斷當前的類用了哪些trait。然後得到一個數組。程序會遍歷這個數組,尋找有沒有符合 “bootTraitName”的方法(在trait中定義),如果有就執行。
/**
* Boot all of the bootable traits on the model.
*
* @return void
*/
protected static function bootTraits()
{
foreach (class_uses_recursive(get_called_class()) as $trait) {
if (method_exists(get_called_class(), $method = 'boot'.class_basename($trait))) {
forward_static_call([get_called_class(), $method]);
}
}
}
Eloquent用這種方法,在初始化一個model的時候,就可以做許多自動加載. laravel自帶的功能中,softDelete就是通過trait來實現的。簡單來說,use了softDelete的model,會在boot的時候自動執行bootSoftDelete,然後該方法在model所有的查詢都默認加入一個判斷deleted_at 字段的環節,以只調取未被刪除的數據。
/**
* Boot the soft deleting trait for a model.
*
* @return void
*/
public static function bootSoftDeletes()
{
static::addGlobalScope(new SoftDeletingScope);
}
這種做法提供了很多便利,也提供了trait的使用示範。
舉個簡單的例子: 我們可以用這種方法 , 給model加載一個自動清除緩存的trait . 在每一個模型每次saved之後,自動刷新它的緩存:
trait ModelCache {
public static function bootModelCache(){
static::saved(function($model){
$cacheKey = get_class($model).'_'.$model->id;
Cache::forget($cacheKey);
});
}
}
trait在laravel的其它場景中也經常使用。例如User模型,是Laravel用來做身份驗證的驅動。與身份驗證的相關方法就是用一個trait來加載的。
<?php
namespace Illuminate\Auth;
trait Authenticatable
{
/**
* Get the unique identifier for the user.
*
* @return mixed
*/
public function getAuthIdentifier()
{
return $this->getKey();
}
.................
/**
* Get the token value for the "remember me" session.
*
* @return string
*/
public function getRememberToken()
{
return $this->{$this->getRememberTokenName()};
}
}
這樣當我們需要換別的模型,別的控制器做驗證驅動,只要寫一行use 代碼,就自動得到了相關方法。
laravel使用trait還有一個比較典型的,就是dispatch。主要在laravel的controller中調用了這個trait。這樣laravel的控制器就可以用$this->dispatch() 直接來調度任務。
trait DispatchesJobs
{
/**
* Dispatch a job to its appropriate handler.
*
* @param mixed $job
* @return mixed
*/
protected function dispatch($job)
{
return app('Illuminate\Contracts\Bus\Dispatcher')->dispatch($job);
}
...........
}
任何一個類只要use了這個DispatchJob的trait,都能用同樣的調度方法(其實就是用app()得到了一個dispatch的單例)。
靈活使用trait , 還是能創造各種魔法 . 我有一個設想就是通過模仿laravel的trait機制實現的 .
簡單來說 , 在做一個複雜的資訊站時 , 可能要創建許多種model . 然而每個model 總有一部分模塊是一樣的,例如:
- 文章(標題,作者,正文)
- 圖片組(圖片,簡介)
- 視頻(標題,來源,源碼,簡介)
- 添加到tag
- 相關專題
- 等等
創建model時,重複添加這些字段是一個很頭疼的工作. 而現在,我們可以用trait:
abstract class Installer {
//引用文章,圖片,視頻相關字段的構造trait
use ArticleTrait,ImageTrait,VideoTrait;
/**
* 創建數據表
*/
protected function CreateTable()
{
//用Schema創建數據表
Schema::Create($this->table,function(Blueprint $table){
//生成默認字段
$table = $this->makeDefaultFields($table);
//生成trait中的字段
$table = $this->makeTraitFields($table);
});
}
/**
* 生成默認字段
*/
protected function makeDefaultFields(Blueprint $table)
{
$table->increaments('id');
$table->timestamps();
return $table;
}
/**
* 按照trait內的方法,生成模塊的字段
*/
protected function makeTraitFields(Blueprint $table)
{
foreach (class_uses_recursive(get_called_class()) as $trait) {
//如果trait內有 makeTraitNameFields方法, 就用該方法生成字段
if (method_exists($this, $method = 'make'.class_basename($trait).'Fields')) {
$table = $this->{$method}($table);
}
}
return $table;
}
}
這樣 , 建完幾個標準的trait後, 要建其它複雜的模型,代碼上就非常簡單了