Laravel Repository缓存和分页

时间:2014-02-17 22:10:56

标签: php caching pagination laravel-4

我已经创建了一个基础存储库,现在正在扩展它以添加缓存,但我似乎遇到的问题多于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

进行分页,因此似乎无法使用实际的分页类来返回结果

我可以单独返回数据和分页符,但这看起来非常h​​acky。

有没有办法用模型数据重新填充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;
}

3 个答案:

答案 0 :(得分:1)

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



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




答案 1 :(得分:0)

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

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

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

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

  • newQuery
  • newBaseQueryBuilder
  • 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;
 }

}

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

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

答案 2 :(得分:0)

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