【问题标题】:Laravel Repository caching and paginationLaravel 存储库缓存和分页
【发布时间】:2017-07-05 17:49:07
【问题描述】:

我已经创建了一个基础存储库,现在正在扩展它以添加缓存,但我遇到的问题似乎比大多数人都多的是pagination

在我的 all() 方法中,我在不缓存的情况下执行以下操作:

public function all($pagination = 20)
{
    try
    {
        $query = $this->model->newQuery();
        return $this->handlePagination($query, $pagination);
    }
    catch (Exception $e)
    {
        throw $e;
    }
}

protected function handlePagination($query, $pagination)
{
    if (is_null($pagination))
    {
        return $query;
    }
    $collection = $query->paginate($pagination);
    return $collection;
}

这很好,但是当我尝试实现缓存时,我想单独缓存每个模型并存储每个集合的键,因此,如果我对所有条目进行分页,我会将每个分页集合存储在缓存:

cache set.1 [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
cache set.2 [21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40]

等等……

问题是似乎不可能使用实际的分页类来返回结果,因为您只分页id

我可以分别返回数据和分页器,但这看起来很hacky。

有没有办法用模型数据重新填充Paginator 类而不覆盖整个内容?

编辑

我在想这样的事情:

public function all($pagination = 20)
{
    try
    {
        $cache_key = $this->cache_key . '.all';
        $ids = Cache::get($cache_key);
        if (! $ids)
        {
            $query = $this->model->newQuery();
            $ids = $query->pluck('id');
            Cache::put($cache_key, $ids, $this->cache_ttl);
        }

        $ids = $this->handlePagination($ids, $pagination);
        $collection = new Collection();
        foreach ($ids as $id)
        {
            if ($model = $this->find($id))
            {
                $collection->put($id, $model);
            }
        }
        return $collection;
    }
    catch (Exception $e)
    {
        throw $e;
    }
}

/**
 * @var \Illuminate\Pagination\Paginator
 */
protected $paginator;
public function handlePagination($array, $pagination = 20)
{
    if (!is_null($pagination))
    {
        $this->paginator = Paginator::make($array, count($array), $pagination);
        return $this->paginator->getItems();
    }
    return $array;
}

public function getPaginator()
{
    return $this->paginator;
}

【问题讨论】:

    标签: php caching pagination laravel-4


    【解决方案1】:

    我必须在我工作的项目中实现缓存,我遇到了类似的问题,但没有分页。但是方法应该是一样的。

    如果模型被告知这样做,Laravel 默认会在内部处理查询缓存。

    我所做的是创建一个类,我想要缓存的所有对象都应该以某种方式扩展。然后你可以使用分页而不用考虑缓存。

    在下面的代码中,请特别注意以下方法覆盖:

    • 新查询
    • 新的BaseQueryBuilder
    • newFromBuilder

    CacheModel 类如下所示:

    <?php
    
    class CacheModel extends Eloquent
    {
     /**
     * Holds the query builder from which this model was fetched from.
     *
     * @var Illuminate\Database\Query\Builder
     */
     protected $queryBuilder;
    
    
     /**
     * Overrides Illuminate\Database\Eloquent\Model's newQuery().
     * We need to do this to store the query builder in our class for caching purpuses.
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
     public function newQuery($excludeDeleted = true)
     {
        $eloquentBuilder = parent::newQuery($excludeDeleted);
        return $eloquentBuilder->rememberForever();
     }
    
    
     /**
     * Overrides Illuminate\Database\Eloquent\Model's newBaseQueryBuilder().
     * We need to do this to store the query builder in our class for caching purpuses.
     *
     * @return \Illuminate\Database\Query\Builder
     */
     protected function newBaseQueryBuilder()
     {
        $queryBuilder = parent::newBaseQueryBuilder();
        $this->queryBuilder = $queryBuilder;
        return $queryBuilder;
     }
    
    
     /**
     * Overrides Illuminate\Database\Eloquent\Model's newFromBuilder().
     * We need to do this to update the cache.
     *
     * @return an instance of the specified resource
     */
     public function newFromBuilder($attributes = array())
     { 
        $object = parent::newFromBuilder($attributes);
    
        $that = $this; 
    
        $referencedCacheKeysFromObject = Cache::rememberForever($object->getCacheIdKey(), function() use ($that){
              return array( $that->getQueryBuilder()->getCacheKey() => true );
        });
        if ( !isset($referencedCacheKeysFromObject[$this->getQueryBuilder()->getCacheKey()] ))
        {
             # Update the cache entries that hold the object
            $referencedCacheKeysFromObject[$this->getQueryBuilder()->getCacheKey()] = true;
            Cache::forget($object->getCacheIdKey());
            Cache::forever($object->getCacheIdKey(), $referencedCacheKeysFromObject);
        }
    
        $referencedCacheKeysFromObjectTable = Cache::rememberForever($object->getCacheTableKey(), function() use ($that){
              return array( $that->getQueryBuilder()->getCacheKey() => true );
        });
        if ( !isset( $referencedCacheKeysFromObjectTable[$this->getQueryBuilder()->getCacheKey()] ))
        {
            # Udate the cache entries that hold objects from the object's table.
            $referencedCacheKeysFromObjectTable[$this->getQueryBuilder()->getCacheKey()] = true;
            Cache::forget($object->getCacheTableKey());
            Cache::forever($object->getCacheTableKey(), $referencedCacheKeysFromObjectTable);
        }
    
        return $object;
     }
    
    
     /**
     * Overrides Illuminate\Database\Eloquent\Model's save().
     * We need to do this to clean up the cache entries related to this object.
     *
     * @return \Illuminate\Database\Query\Builder
     */
     public function save(array $attributes = array())
     {
        if (!$this->exists)
        {
            # If the object doesn't exists, it means that the object is gonna be created. So refresh all queries involving the object table.
            # This is needed because the new created object might fell within one of the cache entries holding references to objects of this type.
            $this->cleanUpCacheQueriesOfObjectTable();
        }
        $this->cleanUpCacheQueriesOfObject();
        return parent::save();
     }
    
    
     /**
     * Overrides Illuminate\Database\Eloquent\Model's delete().
     * We need to do this to clean up the cache entries related to this object.
     *
     */
     public function delete()
     {
        $this->cleanUpCacheQueriesOfObject();
        return parent::delete();
     }
    
    
     /**
     * Overrides Illuminate\Database\Eloquent\Model's delete().
     * We need to do this to clean up the cache entries related to this object.
     *
     */
     public static function destroy($id)
     {
        $this->find($id)->cleanUpCacheQueriesOfObject();
        Cache::forget($this->getCacheIdKey($id));
        return parent::destroy($id);
     }
    
    
     /**
     * Returns the asociated query builder from which the model was created
     *
     * @return \Illuminate\Database\Query\Builder
     */
     public function getQueryBuilder()
     {
        return $this->queryBuilder;
     }
    
    
     /**
     * Cleans up all the cache queries that involve this object, if any.
     *
     */
     private function cleanUpCacheQueriesOfObject()
     {
        # Clean up the cache entries referencing the object as we need to re-fetch them.
        if ( $referencedCacheKeys = Cache::get($this->getCacheIdKey()) )
        {
            foreach ($referencedCacheKeys as $cacheKey => $dummy)
            {
                Cache::forget($cacheKey);
            }
        }
     }
    
    
     /**
     * Cleans up all the cache queries that involve this object table, if any.
     * Needed when a a new object of this type is created. 
     * The cache needs to be refreshed just in case the object fells into 
     * one of the cache queries holding entries from the same type of the object.
     *
     */
     private function cleanUpCacheQueriesOfObjectTable()
     {
        # Clean up the cache entries referencing the object TABLE as we need to re-fetch them.
        if ( $referencedCacheKeys = Cache::get($this->getCacheTableKey()) )
        {          
            foreach ($referencedCacheKeys as $cacheKey => $dummy)
            {
                Cache::forget($cacheKey);
            }
        }
     }
    
    
     /**
     * Returns a string containing a key for the table cache
     *
     */
     private function getCacheTableKey()
     {
        return '_' . $this->getTable();
     }
    
    
     /**
     * Returns a string containing a key for the object cache
     *
     */
     private function getCacheIdKey($id = null)
     {
        if (!isset($id))
        {
          if (isset($this->id))
            $id = $this->id;
          else
            $id = md5(serialize($this->getAttributes()));
        }
    
        return $this->getCacheTableKey() . '_' . $id;
     }
    
    }
    

    然后您可以创建另一个扩展此 CacheModel 的类,该类称为 PaginateModel,您可以执行任何您想做的分页操作。 当然,其余的对象应该扩展这个 PaginateModel 类。

    编辑:在 newFromBuilder 方法中添加了一些 if 条件,因为我刚刚发现自 3.1.3 以来 APC 中存在一个错误。

    【讨论】:

    • 非常有趣的类——它是否充分利用了 Eloquent 提供的所有功能并成功缓存了所有内容?
    • 到目前为止,我们正在测试这个类。到目前为止,我们没有发现重大问题。但是你可以试一试,看看它是否有效。背后的想法是缓存一个对象和所有将获取该对象的相关查询。一旦对象得到更新,我们就会使所有这些引用失效,因此缓存会随处刷新。
    • 将缓存嵌入 Eloquent 模型可能不是一个好主意。 Questioner 正在使用存储库设计模式,因此处理缓存的正确方法是存储库的缓存装饰器类。
    【解决方案2】:

    同时传递页码以进行缓存。像这样的东西

    $currentPg = Input::get('page') ? Input::get('page') : '1';
    $boards = Cache::remember('boards'.$currentPg, 60, function(){ return WhatEverModel::paginate(15); });

    【讨论】:

      【解决方案3】:

      您需要按照Laravel documentation 中的说明手动创建分页器。所以基本上你需要从你的存储库中返回分页器对象,这些对象可以很容易地被缓存。其他选项是根据缓存/查询的参数动态生成分页器。

      【讨论】:

        猜你喜欢
        • 2020-03-08
        • 2015-07-11
        • 1970-01-01
        • 1970-01-01
        • 2019-03-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-09
        相关资源
        最近更新 更多