使用存储库模式(和查询范围)与关系

10 浏览
0 Comments

使用存储库模式(和查询范围)与关系

在Laravel 4中,查询作用域适用于所有查询(包括由关系查询生成的查询)。这意味着对于以下(示例)模型:

Customer.php:

hasMany('Order'); }
}

Order.php:

where('delivered', '=', true); }
   public function customer() { return $this->belongsTo('Customer'); }
}

下面的两种方法都可以工作:

var_dump(Order::delivered()->get()); // 所有已交付的订单
var_dump(Customer::find(1)->orders()->delivered()->get()); // 只有客户#1的已交付订单

这对控制器非常有用,因为查找已交付订单的查询逻辑不需要重复。

然而,最近我被说服了,认为存储库模式不仅适用于关注点分离,而且适用于ORM/DB切换或添加缓存等中间件。存储库非常自然,因为现在与其让作用域膨胀我的模型,相关的查询实际上是存储库的一部分(这更有意义,因为这自然是集合的方法而不是项的方法)。

例如,

order = $order; }
    public function find($id) { /* ... */ }
    /* 等... */
    public function allDelievered() { return $this->order->where('delivered', '=', true)->get(); }
}

然而,现在我重复了已交付的作用域,为了避免违反DRY原则,我从模型中删除了它(根据上述理由,这似乎是合乎逻辑的)。但是现在,我不能再在关系上使用作用域(例如$customer->orders()->delivered())。我唯一看到的解决方法是在Relation基类中以某种方式使用预先制作的查询来实例化存储库(类似于传递给模型中的作用域的方式)。但这涉及更改(和覆盖)大量代码和默认行为,似乎使事情耦合度更高。

鉴于这个困境,这是对存储库的误用吗?如果不是,我的解决方案是恢复我想要的功能的唯一方法吗?或者在模型中使用作用域不够紧密耦合,以证明这个额外的代码是合理的?如果作用域不是紧密耦合的,那么是否有一种方法可以同时使用存储库模式和作用域,同时保持DRY原则?

注意:我知道有关类似主题的一些类似问题,但它们都没有解决这里提出的与关系生成的查询有关的问题,这些查询不依赖于存储库。

0
0 Comments

使用存储库模式(和查询范围)处理关联时的问题

问题的出现原因:

- 存储库模式无法处理关联模型的查询范围

- 传统的解决方案是在模型类中定义查询范围,但这违背了单一职责原则,不符合良好的设计原则

解决方法:

- 创建一个存储库接口(Repository)和相关的实现类,用于处理与关联模型的交互

- 在存储库中定义查询范围,并将其添加到每个由Eloquent模型创建的查询中

- 创建一个ModelScope类,用于将查询范围应用于查询构建器

- 使用ServiceProvider将存储库绑定到应用程序的容器中

- 在控制器中使用存储库进行数据操作

具体实现步骤如下:

1. 创建存储库接口和相关的实现类

- Repository接口定义了常用的增删改查方法

- OrderRepository接口继承自Repository接口

- EloquentOrderRepository类实现了OrderRepository接口,同时包含了查询范围的定义

2. 创建EloquentBaseRepository类

- EloquentBaseRepository是一个抽象类,实现了Repository接口

- 在EloquentBaseRepository的构造方法中,将查询范围添加到模型中

- 通过使用ReflectionObject类,遍历存储库类中的方法,找到以"scope"开头的方法,并将其添加到ModelScope中

- 调用模型的addGlobalScope方法,将ModelScope应用于模型

3. 创建ModelScope类

- ModelScope类实现了ScopeInterface接口,用于将查询范围应用于查询构建器

- 在apply方法中,将每个查询范围添加到查询构建器中

- 在remove方法中,将每个查询范围添加一个抛出异常的方法,以防止调用未定义的查询范围

- 使用addScope方法将查询范围添加到ModelScope中

4. 创建存储库服务提供者

- RepositoryServiceProvider类继承自ServiceProvider类

- 在register方法中,将存储库接口绑定到相应的实现类

5. 更新composer文件

- 在composer.json文件中,将存储库路径添加到自动加载的路径中

- 运行composer.phar dump-autoload命令更新自动加载

6. 创建模型类

- Customer和Order模型类继承自Eloquent类

- 在Order模型类中定义了与Customer模型类的关联关系

7. 在控制器中使用存储库进行数据操作

- 在OrderController的构造方法中,通过依赖注入方式获取OrderRepository实例

- 在test方法中,使用存储库进行数据操作,包括获取所有订单、获取已完成订单以及获取特定客户的已完成订单

问题的解决方法虽然能够实现所需功能,但存在以下问题:

- 代码量较大,功能相对简单

- 使用了hacky的方法,可能不符合最佳实践

- 需要手动实例化存储库,不够灵活

参考资源:

- [Creating flexible Controllers in Laravel 4 using Repositories](http://culttt.com/2013/07/08/creating-flexible-controllers-in-laravel-4-using-repositories/)

- [Eloquent tricks for better Repositories](http://culttt.com/2014/03/17/eloquent-tricks-better-repositories/)

0