2013-07-04 73 views
9

我知道Blade已经为所有刀片视图缓存了已编译的PHP,但我想更进一步。我正在处理的网站被模块化为组件视图,然后在默认控制器中拼凑起来。每个“小部件”都有自己的视图,很少更改内容(除少数频繁更新的内容外)。所以,我想缓存这些变化很小的视图的HTML输出,以防止它们在每次页面加载时被评估。Laravel中的缓存视图输出4

在Laravel 3,我们可以做一些像这样(credit Laravel forums):

Event::listen(View::loader, function($bundle, $view) 
{ 
    return Cache::get($bundle.'::'.$view, View::file($bundle, $view, 
                Bundle::path($bundle).'view')); 
}); 

不幸的是,View::loader已经完全Laravel 4消失在通过\Illuminate\View\View\Illuminate\View\Environment挖,我发现每个视图调度名为事件"composing: {view_name}"。听此事件提供了被传递给它的视图名称和数据在每个视图渲染,但是从回调返回不具有相同的效果,因为它在Laravel 3那样:

Event::listen('composing: *', function($view) { 
    if(!in_array($view->getName(), Config::get('view.alwaysFresh'))) { 
    // Hacky way of removing data that we didn't pass in 
    // that have nasty cyclic references (like __env, app, and errors) 
    $passedData = array_diff_key($view->getData(), $view->getEnvironment() 
                    ->getShared()); 

    return Cache::forever($view->getName() . json_encode($passedData), function() { 
     return 'test view data -- this should appear in the browser'; 
    }); 
}, 99); 

以上不规避包括正常的视图和渲染过程。

那么你如何规避普通视图渲染并从这个组合事件返回缓存的内容呢?现在Laravel可能没有一些丑陋的黑客吗?

+0

也许我问如果你是在一种方式,以避免重新创建的视图数据这样做呢?你是否还需要点击数据库来创建视图,即使视图结果本身保存在缓存中?你可能有更好的运气来缓存数据库命中的结果等。 – fideloper

回答

34

快速和肮脏的

好了,一个选择,因为我敢肯定你知道,就是缓存控制器内的项目,如查看正在呈现。我怀疑你不想这样做,因为从长远来看它不太可维护。

更好的可维护性(?)方法

但是,如果查看加载/渲染器不火,你想一个事件,你可以创建一个。因为Laravel 4中的每个包/库都在App容器中设置,所以实际上可以用您自己的替换View库。

我会采取的步骤是:

  1. 创建库/包。目标是创建一个扩展Laravel的视图逻辑的类。看完后,你可能需要扩展this one - 这是View外观
  2. 如果你用自己的扩展视图外观(也就是说,如果我在步骤1中对文件的假设是正确的),那么你只需要用您自己的app/config/app.php替换alias for View

编辑 - 我玩了这一点。虽然我不一定同意缓存视图结果,缓存sql查询或“较重的升降机”,但这里是我如何去做这件事Laravel 4

Laravel 4中的视图渲染doesn不会发布让我们缓存视图结果的事件。以下是我在该功能中添加的缓存视图结果的方式。

您可能想考虑高速缓存视图结果的后果。例如,这并没有解决与数据库交谈以获取视图所需数据的艰苦工作。无论如何,这对扩大或替代核心项目提供了一个很好的概述。

首先,创建一个包并设置其自动加载。我将使用命名空间Fideloper\View。它自动加载在composer.json会是这样的:

"autoload": { 
    "classmap": [ 
     "app/commands", 
     "app/controllers", 
     "app/models", 
     "app/database/migrations", 
     "app/database/seeds", 
     "app/tests/TestCase.php" 
    ], 
    "psr-0": { 
     "Fideloper": "app/" 
    } 
}, 

接下来,创建一个类来代替View门面。在我们的情况下,这意味着我们将延长Illuminate\View\Environment

在这个类中,我们将看到渲染的结果并添加一些逻辑来缓存(或不缓存)它。这里是Fideloper/View/Environment.php

<?php namespace Fideloper\View; 

use Illuminate\View\Environment as BaseEnvironment; 
use Illuminate\View\View; 

class Environment extends BaseEnvironment { 

    /** 
    * Get a evaluated view contents for the given view. 
    * 
    * @param string $view 
    * @param array $data 
    * @param array $mergeData 
    * @return \Illuminate\View\View 
    */ 
    public function make($view, $data = array(), $mergeData = array()) 
    { 
     $path = $this->finder->find($view); 

     $data = array_merge($mergeData, $this->parseData($data)); 

     $newView = new View($this, $this->getEngineFromPath($path), $view, $path, $data); 

     // Cache Logic Here 

     return $newView; 
    } 

} 

所以,这就是你的工作的大部分将是 - 填写该// Cache Logic Here。但是,我们还有一些工作要做。

接下来,我们需要设置新的Environment类作为Facade。我有一篇关于creating Laravel facades的博客文章。以下是在这种情况下如何完成此项任务:

为我们的新环境创建外观。我们将用代码将其命名为fideloper.view

<?php namespace Fideloper\View; 

use Illuminate\Support\Facades\Facade; 

class ViewFacade extends Facade { 

    /** 
    * Get the registered name of the component. 
    * 
    * @return string 
    */ 
    protected static function getFacadeAccessor() { return 'fideloper.view'; } 

} 

然后,创建服务提供商,这将告诉Laravel创造什么时候fideloper.view被调用。请注意,这需要模拟用于创建扩展Environment类的Illuminate\View\ViewServiceProvider的功能。

<?php namespace Fideloper\View; 

use Illuminate\Support\ServiceProvider; 

class ViewServiceProvider extends ServiceProvider { 

    public function register() 
    { 
     $this->app['fideloper.view'] = $this->app->share(function($app) 
     { 
      // Next we need to grab the engine resolver instance that will be used by the 
      // environment. The resolver will be used by an environment to get each of 
      // the various engine implementations such as plain PHP or Blade engine. 
      $resolver = $app['view.engine.resolver']; 

      $finder = $app['view.finder']; 

      $env = new Environment($resolver, $finder, $app['events']); 

      // We will also set the container instance on this view environment since the 
      // view composers may be classes registered in the container, which allows 
      // for great testable, flexible composers for the application developer. 
      $env->setContainer($app); 

      $env->share('app', $app); 

      return $env; 
     }); 
    } 

} 

最后,我们需要一起挂钩这一点,并告诉Laravel载入我们的服务供应商和我们自己的替代照亮的观点门面。编辑app/config/app.php

添加服务提供商:

'providers' => array(

    // Other providers 

    'Fideloper\View\ViewServiceProvider', 

), 

与我们自己替换的视图门面:

'aliases' => array(

    // Other Aliases 

    //'View'   => 'Illuminate\Support\Facades\View', 
    'View'   => 'Fideloper\View\ViewFacade', 

), 

然后您就可以使用您所希望的任何逻辑在View::make()方法!

最后

,有一些模式在每个Web请求多次“请求”加载值得注意。举例来说,Symfony就是让你define controllers as servers。 Zend拥有(已有?)动作堆栈的概念,它允许您在请求期间创建[控制器]动作的队列,以便在执行时有效地执行队列。也许你想在Laravel中探索这种可能性,并缓存这些“动作”(vs直接缓存视图)的结果。

只是一个想法,而不是一个建议。

+0

+1对于ServiceProviders和Facades(如果我可以的话,我会+2或+3)的一个非常详细的指南+1。我一直在避免阅读关于他们的信息,但是你却以其他方式说服了我。这是使用它们的好例子,也是扩展Laravel功能的一个好的跳跃点。谢谢! –

+0

并回答你的问题是我正在缓存数据库请求,所以缓存视图输出可能是不必要的微观优化。但是,这个过程很适合扩展Laravel的功能。 –

+0

我只是想澄清一些事情。只要您有一种方法检查缓存的HTML的缓存状态和时间戳,视图缓存确实会避免与数据库对话。在您的控制器方法中,您只需在方法顶部检查缓存状态,然后再调用模型中的任何内容。如果缓存状态仍然有效,则跳过所有数据库内容,然后返回HTML。如果缓存状态不再有效,则执行正常的请求并重新缓存结果。 这就是你如何使用Smarty的例子。 – AgmLauncher

4

有一个图书馆缓存意见/部分在Laravel(而不仅仅) - Flatten。

它是一个强大的缓存系统,用于在运行时缓存页面。它的功能很简单:告诉他哪个页面要被缓存,什么时候刷新缓存,从那里Flatten处理它。它会悄悄地将你的页面变成纯HTML并存储它们。如果用户访问一个已经变平的页面,那么所有的PHP都会被劫持,而不是显示一个简单的HTML页面。这将为应用程序的速度提供必要的提升,因为只有在对其显示的数据进行更改时才会刷新页面的缓存。

要通过artisan flatten:build命令缓存应用程序中的所有授权页面。它将抓取您的应用程序并逐页访问,缓存您允许的所有页面。

Flushing

有时您可能需要刷新特定页面或模式。例如,如果您缓存用户的配置文件,则可能需要在用户编辑其信息时将其刷新。您可以通过以下方法做到这一点:

// Manual flushing 
Flatten::flushAll(); 
Flatten::flushPattern('users/.+'); 
Flatten::flushUrl('http://localhost/users/taylorotwell'); 

// Flushing via an UrlGenerator 
Flatten::flushRoute('user', 'taylorotwell'); 
Flatten::flushAction('[email protected]', 'taylorotwell'); 

// Flushing template sections (see below) 
Flatten::flushSection('articles'); 

链接 - https://github.com/Anahkiasen/flatten