"修飾"(Decorate)Laravel裏面的Repositories

該篇屬於[《Laravel底層核心技術實戰揭祕》]; 歡迎做客咱們的php&Laravel學習羣:109256050(//study.163.com/course/courseMain.htm?courseId=1003575006)這一課程《laravel底層核心概念解析》這一章的擴展閱讀。考慮到學員們的基礎差別,爲了不視頻當中過於詳細而連篇累牘,故將一些laravel底層實現相關的PHP知識點以文章形式呈現,供你們預習和隨時查閱。
原文連接: http://www.pilishen.com/posts...

像咱們以前在課程裏提到的,當開發任何正式的laravel項目時,將controller與咱們的Eloquent ORM(或者其餘的數據來源)進行解耦,向來是老練明智之舉。一般咱們會建立一個interface,而後再寫一個實現了這個interface的repository,而後再經過laravel服務容器將對該interface的依賴解析到這個具體的repository上,具體的數據操做咱們都是在repository當中進行。php

一樣的道理也適用於cache。咱們都知道,數據查詢每每是咱們一個web應用裏的主要性能瓶頸,因此不免要爲數據查詢建立相應的緩存(cache)。一樣的,在你的controller裏面具體地實現數據cache,也是很是糟糕的作法——這樣呢,你就將controller與某一種特定的cache實現給強行綁到一塊了,後期若是你想着換一種實現方式,好比從memcache換成redis,那麼你就不得不大量修改你controller裏的邏輯。並且,當某一個數據查詢在多個地方出現時,你就不得不寫重複的cache實現的代碼。laravel

或許呢,你能夠在你的repository裏寫cache相關的實現邏輯,對相對較小的項目,這樣倒也行得通。可是呢,這也意味着你的repository就既依賴於ORm,又依賴於你所選擇的cache。這個時候若是你想着更換其中任何一種依賴形式,都極可能意味着你得從新寫一整個新的repository,這期間又要重複不少以前的代碼。web

固然,確定有一個更優雅的方式——經過使用「修飾者模式」(decorator pattern),咱們能夠再建立一個repository,讓其實現同一個interface,同時呢,把以前那個repository裏的實現給「包裹」起來,或者說「修飾」一下。在這個cache相關的repository裏,每個方法內都相應調取以前那個數據repository裏的邏輯,而後相應地返回cache事後的response。這樣的話,咱們cache的邏輯,就跟咱們數據操做的邏輯,相對分離開了,假設後期咱們想換一種數據來源,那麼咱們的cache實現也就不會受到影響。redis

假設這是對應於咱們Usermodel的interface:緩存

<?php
namespace App\Repositories\Interfaces;

interface UserRepositoryInterface {
    public function all();
    public function findOrFail($id);
    public function create($input);
}

接下來呢是其對應的數據操做的repository:app

<?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:ide

<?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實現「修飾」了一下以前的數據查詢結果。post

那麼要實際地調用到咱們的這個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實現,好比也能夠在裝飾者中觸發特定事件。

相關文章
相關標籤/搜索