Laravel缓存:最佳实践

13

PHP 同事们:

这个问题涉及使用 Laravel 缓存的最佳实践。

中心目标是为了减少所有通常与性能有关的原因而对数据库的访问次数。该应用程序是一个读取密集型新闻网站,最多可能有一打控制器,主要是资源类型。

是否有关于应用程序设计的任何记录的最佳实践?我认为很明显,由于 Cache:: 是一个单行语句,将其放入控制器很容易——返回缓存数据或调用模型并缓存结果。当请求更新模型时,无效化缓存(可能会进行急切的重新加载)。但这是一个好习惯吗?

以下是在控制器中执行此操作的第一个示例:

/**
 * Retrieve listing of the gallery resource.
 *
 * @uses GET /gallery to return all image_collections.
 *
 * @param int $id The gallery id
 *
 * @return Response - Contains a HTTP code and a list of articles.
 */
public function index()
{
    $response_data = array();
    $response_code = 200;

    // TRY TO RETURN A CACHED RESPONSE
    $cache_key = "gallery_index";
    $response_data = Cache::get($cache_key, null);

    // IF NO CACHED RESPONSE, QUERY THE DATABASE
    if (!$response_data) {
        try {
            $response_data['items'] = $this->gallery->all();
            Cache::put($cache_key, $response_data, Config::get('app.gallery_cache_minutes'));
        } catch (PDOException $ex) {
            $response_code = 500;
            $response_data['error'] = ErrorReporter::raiseError($ex->getCode());
        }
    }

    return Response::json($response_data, $response_code);
}

我听说过可以使用Laravel路由过滤器来缓存响应,但我还无法完全理解这个想法。

有什么想法?参考资料?示例吗?

感谢大家,
Ray


1
缓存管理很难,这完全取决于你缓存的内容。如果你正在呈现一个近似值,那么即使基础数据有点过时,你也可能不在意。相反,如果你在缓存像ALCs这样的东西,它们可能始终需要正确(然后在底层数据存储更新时刷新)。 - Matthew
你应该在控制器中解耦逻辑,例如,你可以将类型提示缓存传递给控制器构造函数。我还建议为缓存创建存储库,这样你就可以利用 Laravel 的缓存能力。 - Potato Science
2个回答

32

许多人的做法都不一样,但是如果最佳实践是一个问题,我认为在您的存储库中进行缓存是最佳选择。

你的控制器不应该过多地了解数据源,我是说它是来自缓存还是来自数据库。

在控制器中进行缓存不支持DRY(不要重复自己)方法,因为你会发现自己需要在几个控制器和方法中重复编写代码,从而使脚本难以维护。

所以对于我来说,这就是我在Laravel 5中的做法,在使用laravel 4时也不会有太大的区别:

1. 在App/Repositories中创建GalleryEloquentRepository.php

namespace App\Repositories;
use App\Models\Gallery;
use Cache;
    
/**
 * Class GalleryEloquentRepository
 * 
 * @package App\Repositories
 */
class GalleryEloquentRepository implements GalleryRepositoryInterface
{
    /**
     * Fetch all the galleries
     * 
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public function all()
    {
        return Cache::remember('gallerys', $minutes='60', function()
        {
            return Gallery::all();
        });
    }

    /**
     * Fetch a gallery
     * 
     * @return \App\Models\Gallery
     */
    public function find($id)
    {
        return Cache::remember("gallerys.{$id}", $minutes='60', function() use($id)
        {
            return Gallery::find($id);
        });
    }
}

2. 在App/Repositories中创建GalleryRepositoryInterface.php

namespace App\Repositories;
    
/**
 * Interface GalleryRepositoryInterface
 * 
 * @package App\Repositories
 */
interface GalleryRepositoryInterface
{
    public function find($id);

    public function all();
}

3. 在 App/Providers 中创建 RepositoryServiceProvider.php 文件

namespace App\Providers;
use Illuminate\Support\ServiceProvider;

/**
 * Class RepositoryServiceProvider
 * 
 * @package App\Providers
 */
class RepositoryServiceProvider extends ServiceProvider
{
    /**
     * Indicates if loading of the provider is deferred.
     *
     * @var bool
     */
    //protected $defer = true;

    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(
            'App\Repositories\GalleryRepositoryInterface',
            'App\Repositories\GalleryEloquentRepository'
        );
    }
}

4. 在控制器中,您可以这样做

namespace App\Http\Controllers;
    
use View;
use use Illuminate\Support\Facades\Response;
use App\Repositories\GalleryRepositoryInterface;

class GalleryController extends Controller {

    public function __construct(GalleryRepositoryInterface $galleryInterface)
    {
        $this->galleryInterface = $galleryInterface;

    }

    public function index()
    {
        $gallery = $this->galleryInterface->all();

        return $gallery ? Response::json($gallery->toArray()) : Response::json($gallery,500);
    }
}

4
好的回答,@Digilimit。 - Moz Morris
1
模型也是一个不错的地方,但是让我们不要在模型中堆积大量的代码。 - Emeka Mbah
1
我使用Laracogs作为脚手架工具。它生成的结构如下:控制器->服务->存储库->模型。 我的疑问是:在哪里放置我的代码:验证、缓存...?服务还是存储库?为什么不是控制器?(抱歉,我只是试图熟悉设计模式,除了MVC,我并没有实际掌握它的应用,尽管我可以理解它的理论:()。谢谢。 - Thang Nguyen
1
不错的方法!只有一个小问题...如果你使用Cache::remember(),我认为你不需要用Cache::has()检查键是否存在。remember()会处理它。 - Sas Sam
1
@EmekaMbah,我可以指出find($id)方法正在缓存已经被all()方法缓存的数据,并且如果在缓存中找不到,则运行新的查询从数据库中获取它。我可以建议您在运行新的查询搜索数据库之前,首先在gallerys缓存(在all()方法中创建的缓存)中搜索记录。 - yaddly
显示剩余3条评论

-1

当然,有一些技巧可以避免在控制器中放置缓存逻辑,并将其交给某些第三方包来管理缓存。

我建议您阅读这篇文章。

https://github.com/imanghafoori1/laravel-widgetize

通过这种方式,您可以将页面部分组织成定义良好且自我缓存的小部件类。因此,您可以对缓存配置进行细粒度控制,并且只需设置“配置”(而不是缓存逻辑),例如“缓存标签”,“过期时间”等。

缓存逻辑由其他人为您编写并提取到经过良好单元测试的软件包中。因此,它不会污染您的代码。

另一个优点是,通过这种方式不仅可以节省数据库查询,还可以节省大量重复运行的php代码。例如,在小部件位于缓存中时,Laravel Blade背后的php代码不需要运行,也不会运行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接