Repository

使用 Repository 辅助 Model

若将数据库逻辑都写在 model,会造成 model 的肥大而难以维护,基于 SOLID 原则,我们应该使用 Repository 模式辅助 model,将相关的数据库逻辑封装在不同的 repository,方便中大型项目的维护。

数据库逻辑

在 CRUD 中,CUD 比较稳定,但 R 的部分则千变万化,大部分的数据库逻辑都在描述R 的部分,若将数据库逻辑写在 controller 或 model 都不适当,会造成 controller 与 model 肥大,造成日后难以维护。

Model

如果使用 Eloquent ORM 来实现 Repository 模式,需要修改 model 不要包含数据库逻辑,仅保留以下部分:

  • Property : 如$table,$fillable…等。

  • Mutator: 包括 mutator 與 accessor。

  • Method : relation 類的 method,如使用 hasMany() 與 belongsTo()。

简化后的Eloquent 的 model 类我们可以称之为Eloquent Class,由于继承了Illuminate\Database\Eloquent\Model 类,所以Eloquent Class仍然有很多方法可以操作数据库。

Eloquent Class 代码结构如下:

namespace MyBlog;

use Illuminate\Database\Eloquent\Model;
/**
 * MyBlog\User
 *
 */
class User extends Model {

    /**
     * The database table used by the model.
     *
     * @var string
     */
    protected $table = 'users';

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['name', 'email', 'password'];

    /**
     * The attributes excluded from the model's JSON form.
     *
     * @var array
     */
    protected $hidden = ['password', 'remember_token'];
}

而在 ecshopx 中,由于我们采用了 Doctrine ORM,其自带Repository模式。在 Doctrine ORM 中也有简化的 model 类: Entity Class

相比 Eloquent Class Entity Class 更简单,其只包含 protectedprivate的属性和gettersetter 方法(在 JAVA 中,这种对象成为值对象)。

Entity Class 代码结构如下:

Repository

初学者常会在 controller 直接调用 model 写数据库逻辑:

数据库逻辑是获取 20 岁以上的用户

在中大型项目中,会有几个问题 :

  • 将数据库逻辑写在 controller,造成 controller 的肥大难以维护。

  • 违反 SOLID 的单一职责原则 : 数据库逻辑不应该写在 controller。

  • controller 直接相依于model,使得我们无法对 controller 做单元测试。

比较好的方式是使用 repository :

  • 将 model 依赖注入到 repository。

  • 将数据库逻辑写在 repository。

  • 将 repository 依赖注入到 service。

以此原则,我们看下,采用 Eloquent ORM 如何使用 Repository 模式。

UserRepository.php

第 8 行

将依赖的 User model 依赖注入到 UserRepository。

UserController.php

将依赖的 UserRepository 依赖注入到 UserController。

26行

从原本直接依赖 User model,改成依赖注入的 UserRepository。

改用这种写法,有几个优点:

  • 将数据库逻辑写在存储库中,解决控制器肥大问题。

  • 符合SOLID的单一职责原则:数据库逻辑写在repository,没写在controller。

  • 符合SOLID的依赖转换原则:controller 并非直接相依于存储库,而是将存储库依赖注入进controller。

是否该建立存储库接口?

理论上使用依赖注入时,应该使用接口,不过接口目的在于通过抽象化,方便在后期更换接口的具体实现,以便让程序达到开放封闭的要求,但是实际上要更换存储库的机会不高,除非你有更换资料库的需求,如从MySQL抽换到MongoDB,此时就该建立存储库接口。

不过由于我们使用了依赖注入,将来要从类改成interface也很方便,只要在构造方法的类型提示改成interface即可,维护成本很低,所以在此大可使用的存储库类,用接口反而会造成设计过度,等真正需求来时再重构成接口即可。

采用 Doctrine ORM 如何使用 Repository 模式。

由于 Doctrine ORM 自带 Repository 模式,所以直接使用即可。

UserRepository.php

UserController.php

Last updated

Was this helpful?