上周我写了
<?php
interface PostRepository
{
public function getById($id): Post;
public function save(Post $post);
public function delete($id);
}
然而,在实际项目中,如果决定使用存储库,通常会在其中添加检索记录的方法:
<?php
interface PostRepository
{
public function getById($id): Post;
public function save(Post $post);
public function delete($id);
public function getLastPosts();
public function getTopPosts();
public function getUserPosts($userId);
}
这些方法可以通过 Eloquent 作用域来实现,但是重载实体类并承担获取自身的责任并不是最好的主意,将这种责任转移到存储库类似乎是合乎逻辑的。 是这样吗? 我特地直观地将这个界面分为两部分。 方法的第一部分将用于写操作。
标准的写操作是:
- 新对象的构建和挑战 发布存储库::保存
- PostRepository::getById、实体操纵和召唤 发布存储库::保存
- 通话 发布存储库::删除
写操作不使用获取方法。 在读取操作中,仅使用 get* 方法。 如果您读到 接口隔离原则 (信 I в SOLID),那么很明显我们的接口太大并且至少执行两个不同的职责。 是时候将其除以二了。 方法 获取标识符 两者都是必要的,但随着应用程序变得更加复杂,其实现将会有所不同。 我们稍后会看到这一点。 我在上一篇文章中写过 write 部分的无用之处,所以在这篇文章中我将简单地忘记它。
在我看来,Read 部分并不是那么无用,因为即使对于 Eloquent 来说,这里也可能有多种实现。 给班级起什么名字? 能 阅读帖子存储库,但是对于模板 存储库 他已经没有什么相关性了。 你可以只 后置查询:
<?php
interface PostQueries
{
public function getById($id): Post;
public function getLastPosts();
public function getTopPosts();
public function getUserPosts($userId);
}
使用 Eloquent 实现它非常简单:
<?php
final class EloquentPostQueries implements PostQueries
{
public function getById($id): Post
{
return Post::findOrFail($id);
}
/**
* @return Post[] | Collection
*/
public function getLastPosts()
{
return Post::orderBy('created_at', 'desc')
->limit(/*some limit*/)
->get();
}
/**
* @return Post[] | Collection
*/
public function getTopPosts()
{
return Post::orderBy('rating', 'desc')
->limit(/*some limit*/)
->get();
}
/**
* @param int $userId
* @return Post[] | Collection
*/
public function getUserPosts($userId)
{
return Post::whereUserId($userId)
->orderBy('created_at', 'desc')
->get();
}
}
接口必须与实现相关联,例如 应用服务提供者:
<?php
final class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(PostQueries::class,
EloquentPostQueries::class);
}
}
这个类已经很有用了。 他通过减轻控制器或实体类的负担来实现自己的责任。 在控制器中可以这样使用:
<?php
final class PostsController extends Controller
{
public function lastPosts(PostQueries $postQueries)
{
return view('posts.last', [
'posts' => $postQueries->getLastPosts(),
]);
}
}
方法 帖子控制器::lastPosts 只是要求一些实施 帖子查询 并与它一起工作。 在我们链接的提供者中 后置查询 与类 EloquentPost查询 这个类将被替换到控制器中。
让我们想象一下我们的应用程序已经变得非常流行。 每分钟有数千名用户打开包含最新出版物的页面。 最受欢迎的出版物也被经常阅读。 数据库不能很好地处理此类负载,因此它们使用标准解决方案 - 缓存。 除了数据库之外,某些数据快照还存储在针对某些操作优化的存储中 - memcached или Redis的.
缓存逻辑通常没有那么复杂,但是在 EloquentPostQueries 中实现它并不是很正确(如果只是因为 单一责任原则)。 使用模板更自然 装饰者 并实现缓存作为主要操作的装饰:
<?php
use IlluminateContractsCacheRepository;
final class CachedPostQueries implements PostQueries
{
const LASTS_DURATION = 10;
/** @var PostQueries */
private $base;
/** @var Repository */
private $cache;
public function __construct(
PostQueries $base, Repository $cache)
{
$this->base = $base;
$this->cache = $cache;
}
/**
* @return Post[] | Collection
*/
public function getLastPosts()
{
return $this->cache->remember('last_posts',
self::LASTS_DURATION,
function(){
return $this->base->getLastPosts();
});
}
// другие методы практически такие же
}
忽略界面 存储库 在构造函数中。 由于某种未知的原因,他们决定以这种方式命名 Laravel 中的缓存接口。
类 缓存后查询 仅实现缓存。 $this->缓存->记住 检查该条目是否在缓存中,如果不在,则调用回调并将返回值写入缓存。 剩下的就是将此类实现到应用程序中。 我们需要应用程序中的所有类来请求接口的实现 后置查询 开始接收类的实例 缓存后查询。 然而他本人 缓存后查询 构造函数必须接收一个类作为参数 EloquentPost查询因为如果没有“真正的”实现它就无法工作。 我们改变 应用服务提供者:
<?php
final class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(PostQueries::class,
CachedPostQueries::class);
$this->app->when(CachedPostQueries::class)
->needs(PostQueries::class)
->give(EloquentPostQueries::class);
}
}
我所有的愿望都在提供者中很自然地描述。 因此,我们仅通过编写一个类并更改容器配置来实现对请求的缓存。 应用程序代码的其余部分没有改变。
当然,要完全实现缓存,还需要实现失效,使被删除的文章不会在网站上挂一段时间,而是立即被删除。 但这些都是小事。
底线:我们使用的不是一个模板,而是两个模板。 样本 命令查询职责分离 (CQRS) 提出在接口级别完全分离读写操作。 我通过 接口隔离原则,这表明我熟练地操纵模式和原则,并从另一个推导出另一个作为定理:)当然,并不是每个项目都需要这样的抽象来选择实体,但我会与您分享技巧。开发时,只需创建一个类即可 后置查询 通过 Eloquent 进行通常的实现:
<?php
final class PostQueries
{
public function getById($id): Post
{
return Post::findOrFail($id);
}
// другие методы
}
当需要缓存时,通过一个简单的操作,您可以创建一个接口(或抽象类)来代替此类 后置查询,将其实现复制到类中 EloquentPost查询 并转到我之前描述的方案。 其余的应用程序代码不需要更改。
所有这些关于类、接口的技巧, 依赖注入 и 连续QRS 中详细描述
来源: habr.com