使用 View Presenter 模式管理自定义属性

未匹配的标注

在 Laravel 视图中,经常要使用到一些格式化的属性,例如将姓名的首字母大写后显示

{{ ucwords($user->name) }}

解决方案就是使用 Laravel 提供的访问器来自定义属性格式

<?php

public function getNameAttribute($value){
  return ucwords($value);
}

这样做有两个不足之处,一个是会覆盖原始属性,如果要获取原有属性,就需要使用专门的方法

$user->getOriginal('name');

另外一个不足之处在于,将姓名的首字母格式化只是对外显示的时候使用,并不是在所有场景中都需要用到。

我们从单一职责的角度出发,可以将需要格式化显示的属性用专门的类来管理,我们称之为 Presenter 类。

定义专门的 UserPresenter

<?php

namespace App\Presenter;

use Illuminate\Database\Eloquent\Model;

class UserPresenter
{

    protected $model;

    /**
     * PostPresenter constructor.
     *
     * @param $model
     */
    public function __construct(Model $model)
    {
        $this->model = $model;
    }


    public function createdAt()
    {
        return $this->model->created_at->format('Y-m-d');
    }

    public function name()
    {
        return ucwords($this->model->name)
    }

    public function __get($property)
    {
        if(method_exists($this, $property)){
            return $this->$property();
        }

        return $this->model->getAttribute($property);
    }
}

UserPresenter 绑定到对应的模型中

<?php

class User {

  protected $presenterInstance;
  protected $presenter = UserPresenter::class;

  public function presenter()
  {   
    if(! $this->presenterInstance){
      $this->presenterInstance = new $this->presenter($this);
    }

    return $this->presenterInstance;
  }
}

使用

<?php

$user->presenter->name();
// 使用动态属性访问
$user->presenter->name; 
$user->presenter->created_at;
// 当对应方法不存在时,自动获取对应模型的属性
$user->presenter->email;  

最后,封装代码,让所有 Presenter 类继承一个基类

<?php

namespace App\Presenter;

use Illuminate\Database\Eloquent\Model;

abstract class AbstractPresenter
{
    protected $model;

    /**
     * PostPresenter constructor.
     *
     * @param $model
     */
    public function __construct(Model $model)
    {
        $this->model = $model;
    }

    public function __get($property)
    {
        if(method_exists($this, $property)){
            return $this->$property();
        }

        return $this->model->getAttribute($property);
    }
}

定义对应模型使用的 Trait

<?php

namespace App\Presenter;

trait Presenterable
{
    protected $presenterInstance;

    public function presenter()
    {
        if(! $this->presenterInstance){
            $this->presenterInstance = new $this->presenter($this);
        }

        return $this->presenterInstance;
    }
}

使用

<?php

namespace App\Presenter;

class UserPresenter extends AbstractPresenter
{

    public function createdAt()
    {
        return $this->model->created_at->format('Y-m-d');
    }

    public function name()
    {
        return ucwords($this->model->name);
    }
}
<?php

use App\Presenter\Presenterable;
use App\Presenter\UserPresenter;

class User {

  use Presenterable;

  protected $presenter = UserPresenter::class;
}

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 1
发起讨论 只看当前版本


miaotiao
另外一种思路
0 个点赞 | 0 个回复 | 分享