在Laravel 4中缓存视图输出

Bai*_*ker 9 php caching view laravel blade

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

在Laravel 3中我们可以这样做(信用Laravel论坛):

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

不幸的是,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);
Run Code Online (Sandbox Code Playgroud)

以上内容并未规避正常视图,包括渲染过程.

那么如何绕过正常的视图渲染并从这个组合事件中返回缓存的内容呢?目前在Laravel中是否有可能没有一些丑陋的hackery?

fid*_*per 34

快速而肮脏

好吧,我确定你知道,一个选项是在渲染视图时将项目缓存在控制器内部.我怀疑你不想这样做,因为从长远来看它的可维护性较差.

更易于维护(?)的方法

但是,如果View加载器/渲染器未触发您想要的事件,则可以创建一个.因为Laravel 4中的每个包/库都在App容器中设置,所以您实际上可以用自己的库替换View库.

我要采取的步骤是:

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

编辑 - 我玩了一下这个.虽然我不一定同意缓存View结果,缓存sql查询或"更重的提升",但这是我在Laravel 4中如何做到这一点:

Laravel 4中的View渲染不会触发让我们缓存视图结果的事件.以下是我添加该功能以缓存视图结果的方法.

您可能想要考虑缓存视图结果的后果.例如,这并不是在与数据库交谈以获取视图所需的数据的艰苦工作.无论如何,这可以很好地概述扩展或替换核心项目.

首先,创建一个包并设置其自动加载.我将使用命名空间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/"
    }
},
Run Code Online (Sandbox Code Playgroud)

接下来,创建一个类来替换ViewFacade.在我们的例子中,这意味着我们将扩展Illuminate\View\Environment.

在这个类中,我们将获取View渲染的结果,并添加一些逻辑来缓存(或不缓存)它.这是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;
    }

}
Run Code Online (Sandbox Code Playgroud)

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

接下来,我们需要设置我们的新Environment类作为Facade.我有一篇关于创建Laravel外墙的博客文章.以下是在这种情况下如何实现:

为我们的新环境创建外观.我们将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'; }

}
Run Code Online (Sandbox Code Playgroud)

然后,创建服务提供程序,它将告诉Laravel在fideloper.view调用时要创建什么.请注意,这需要模仿Illuminate\View\ViewServiceProvider用于创建扩展Environment类的功能.

<?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;
        });
    }

}
Run Code Online (Sandbox Code Playgroud)

最后,我们需要将这一切联系在一起并告诉Laravel加载我们的服务提供商并用我们自己的替换Illuminate的View外观.编辑app/config/app.php:

添加服务提供商:

'providers' => array(

    // Other providers

    'Fideloper\View\ViewServiceProvider',

),
Run Code Online (Sandbox Code Playgroud)

用我们自己的替换View外观:

'aliases' => array(

    // Other Aliases

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

),
Run Code Online (Sandbox Code Playgroud)

然后,您就可以在View::make()方法中使用您想要的任何逻辑!

最后

值得注意的是,每个Web请求的多个"请求"中都会加载一些模式.例如,Symfony让你将控制器定义为服务器.Zend有(有?)Action Stacks的概念,它可以让你

...有效地帮助您创建在请求期间执行的[控制器]动作队列.

也许你想在Laravel中探索这种可能性,并缓存那些"行动"的结果(直接缓存视图).

只是一个想法,而不是一个推荐.