marcus-ma/myBlog

论如何使Model变得Flexible

Opened this issue · 1 comments

日常的MVC开发中,一般人都会习惯把大量的逻辑直接写在Controller当中,这样的后果是代码的阅读性很差。而好一点的developer会把跟DB交互的代码部分写在Model的方法里面,这样起码是个好的开始。

情景导入

假如现在有一个逻辑,把用户的Informations展示出来,而DB中User表里Gender字段是用1或者2来表示,你需要在View中渲染Data时显示对应的语义意思。一般的做法是

在View中使用模板标签(TP5)

{switch name="$user.gender"}
    {case value="1" break="0"}male{/case}
    {case value="2"}female{/case}
    {default /}None
{/switch}

这样是可以实现,但若是要使用API式的交互呢?直接把Data传给前端让前端再处理吗?


或许有人会说:在Model写段转换逻辑好了。

在Model中写转换逻辑

class UserModel
{
    //性别属性
    public $gender = 1;
    
    public function getGender()
    {
        switch ($this->gender){
            case 1:
                return 'male'; 
                break;
            case 2:
                return 'female';
                break;
            default:
                return 'None';
        }
    }
    
}

这样就行了吗?如果又有其他的属性字段要进行语义转换,那UserModel里就要写很多的function,如果也有其他的项目也需要这样的转换,也照样重写一次那些function吗?如此导致Model的结构就变得很重,相同的逻辑代码无法重用。
其实有一个好办法:使用`Trait`特性和`Presenter`扩展Model解耦和:

自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait。Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。Trait 和 Class 相似,但仅仅旨在用细粒度和一致的方式来组合功能。 无法通过 trait 自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用的几个 Class 之间不需要继承。


使用Trait特性和Presenter扩展Model

  • Model
class UserModel
{
    //使用trait
    use PresentableTrait;

    //指定所要调用的presenter
    protected static $presenter = 'UserPresenter';

    //测试属性
    public $gender = 1;
}
  • 定义一个trait,以便Model可以调用对应的Presenter
trait PresentableTrait
{
    protected static $presenterInstance;

    //单例模式,减少new的次数
    public static function presenter()
    {
        if (!self::$presenterInstance){
            self::$presenterInstance = new self::$presenter(new self);
        }
        return self::$presenterInstance;
    }
}
  • Presenter
class UserPresenter
{
    protected $user;
    //指定关联的Model
    public function __construct(UserModel $user)
    {
        $this->user = $user;
    }
    //性别的语义转换
    public function gender()
    {
        switch ($this->user->gender){
            case 1:
                return 'male'; 
                break;
            case 2:
                return 'female';
                break;
            default:
                return 'None';
        }
    }
}
  • 方法调用
echo UserModel::presenter()->gender();

这样就可以减轻Model本身的结构,又可以实现一些逻辑代码的重用,使整个结构变得Flexible易于后期的扩展。

除了Presenter外,还可以用trait来解决PHP的单继承问题,此外将DB的查询写成Repository会让修改和测试更加容易

class UserRepository
{
    protected $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }
    //取出用户所有Data
    public function getActiveAll()
    {
        return $this->user->all();
    }
}

总结:好的Model结构

  • Model本身只包含Table设置和关联关系设定
  • 使用Trait特性和Presenter扩展Model解耦和
  • 使用Trait特性线性继承解耦和

小记

最近看了**举办的LaravelConf的系列视频,感觉获益良多,见识了很多从来没见过的写法,原来PHP还有这么优雅的写法。同时也深深体会到Laravel这款框架的优雅性,有机会真得读读源码,增长点见识。

几时先学得你甘成功