2016年9月1日 星期四

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来加载的。


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后, 要建其它复杂的模型,代码上就非常简单了

reference : https://phphub.org/topics/1294

沒有留言:

wibiya widget