"修饰"(Decorate)Laravel里面的Repositories

本文来自pilishen.com----原文连接; 欢迎来和pilishen一块儿学习php&Laravel;学习群:109256050php

该篇属于《Laravel底层核心技术实战揭秘》这一课程《laravel底层核心概念解析》这一章的扩展阅读。考虑到学员们的基础差别,为了不视频当中过于详细而连篇累牍,故将一些laravel底层实现相关的PHP知识点以文章形式呈现,供你们预习和随时查阅。laravel

像咱们以前在课程里提到的,当开发任何正式的laravel项目时,将controller与咱们的Eloquent ORM(或者其余的数据来源)进行解耦,向来是老练明智之举。一般咱们会建立一个interface,而后再写一个实现了这个interface的repository,而后再经过laravel服务容器将对该interface的依赖解析到这个具体的repository上,具体的数据操做咱们都是在repository当中进行。web

一样的道理也适用于cache。咱们都知道,数据查询每每是咱们一个web应用里的主要性能瓶颈,因此不免要为数据查询建立相应的缓存(cache)。一样的,在你的controller里面具体地实现数据cache,也是很是糟糕的作法——这样呢,你就将controller与某一种特定的cache实现给强行绑到一块了,后期若是你想着换一种实现方式,好比从memcache换成redis,那么你就不得不大量修改你controller里的逻辑。并且,当某一个数据查询在多个地方出现时,你就不得不写重复的cache实现的代码。redis

或许呢,你能够在你的repository里写cache相关的实现逻辑,对相对较小的项目,这样倒也行得通。可是呢,这也意味着你的repository就既依赖于ORm,又依赖于你所选择的cache。这个时候若是你想着更换其中任何一种依赖形式,都极可能意味着你得从新写一整个新的repository,这期间又要重复不少以前的代码。缓存

固然,确定有一个更优雅的方式——经过使用“修饰者模式”(decorator pattern),咱们能够再建立一个repository,让其实现同一个interface,同时呢,把以前那个repository里的实现给“包裹”起来,或者说“修饰”一下。在这个cache相关的repository里,每个方法内都相应调取以前那个数据repository里的逻辑,而后相应地返回cache事后的response。这样的话,咱们cache的逻辑,就跟咱们数据操做的逻辑,相对分离开了,假设后期咱们想换一种数据来源,那么咱们的cache实现也就不会受到影响。bash

假设这是对应于咱们Usermodel的interface:app

<?php
namespace App\Repositories\Interfaces;

interface UserRepositoryInterface {
    public function all();
    public function findOrFail($id);
    public function create($input);
}
复制代码

接下来呢是其对应的数据操做的repository:ide

<?php
namespace App\Repositories;

use App\User;
use App\Repositories\Interfaces\UserRepositoryInterface;
use Hash;

class EloquentUserRepository implements UserRepositoryInterface {
    private $model;
    public function __construct(User $model)
    {
        $this->model = $model;
    }
    public function all()
    {
        return $this->model->all();
    }
    public function findOrFail($id)
    {
        return $this->model->findOrFail($id);
    }
    public function create($input)
    {
        $user = new $this->model;
        $user->email = $input['email'];
        $user->name = $input['name'];
        $user->password = Hash::make($input['password']);
        $user->save();
        return $user;
    }
}
复制代码

咱们能够这样来定义一个cache repository:post

<?php
namespace App\Repositories\Decorators;

use App\Repositories\Interfaces\UserRepositoryInterface;
use Illuminate\Contracts\Cache\Repository as Cache;

class CachingUserRepository implements UserRepositoryInterface {
    protected $repository;
    protected $cache;
    public function __construct(UserRepositoryInterface $repository, Cache $cache)
    {
        $this->repository = $repository;
        $this->cache = $cache;
    }
    public function all()
    {
        return $this->cache->tags('users')->remember('all', 60, function () {
            return $this->repository->all();
        });
    }
    public function findOrFail($id)
    {
        return $this->cache->tags('users')->remember($id, 60, function () use ($id) {
            return $this->repository->findOrFail($id);
        });
    }
    public function create($input)
    {
        $this->cache->tags('users')->flush();
        return $this->repository->create($input);
    }
}
复制代码

能够看到咱们的这个cache repository里每一个方法,实际上并不负责任何的数据查询操做。咱们经过constructor接收一个一样实现了该interface的具体实现类,也即以前的数据查询的repository,同时接收cache服务,这样呢,咱们不须要触碰任何的数据查询逻辑,直接调取数据相关的repository里相应的方法,而后给它相应地“包裹”上cache便可,或者说用cache实现“修饰”了一下以前的数据查询结果。性能

那么要实际地调用到咱们的这个cache实现,咱们还须要更新一下咱们的service provider,好让它把这个“修饰者”,也即咱们的cache repository,给相应地解析出来:

<?php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{ 
	 public function register()
    {
        $this->app->singleton('App\Repositories\Interfaces\UserRepositoryInterface', function () {
            $baseRepo = new \App\Repositories\EloquentUserRepository(new \App\User);
            $cachingRepo = new \App\Repositories\Decorators\CachingUserRepository($baseRepo, $this->app['cache.store']);
            return $cachingRepo;
        });
    }
}
复制代码

能够看到咱们实例化了数据查询的repository,也即$baseRepo,而后实例化cache repository,将数据repository和cache服务传递进去,而后返回这个cache repository的实例。这样了之后,咱们就能够在controller里实际调用了。

注意的是,用cache repository来“装饰”本来的数据repository,这只是一个例子、一种用法而已,但愿经过这个,你能学会的是如何“装饰、修饰”你的已有的数据repository,不止是cache实现,好比也能够在装饰者中触发特定事件。

相关文章
相关标签/搜索