Laravel 在哪些地方用了 trait?

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後, 要建其它複雜的模型,代碼上就非常簡單了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章