在Laravel 4中缓存视图输出 [英] Caching View Output in Laravel 4

查看:59
本文介绍了在Laravel 4中缓存视图输出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道Blade已经为所有刀片视图缓存了已编译的PHP,但是我想更进一步.我正在工作的一个网站被模块化为组件视图,然后在默认控制器中组合在一起.每个小部件"都有其自己的视图,该视图很少更改内容(少数几个经常更新的视图除外).因此,我想缓存这些很少更改的视图的HTML输出,以防止在每次页面加载时对其进行评估.

I know that Blade already caches the compiled PHP for all blade views, but I would like to take this a step further. A website that I'm working on is modularized into component views and then pieced together in the default controller. Each of the "widgets" has its own view, which rarely changes content (with the exception of a few frequently updating ones). So, I'd like to cache the HTML output of these rarely-changing views to prevent them from being evaluated on every page load.

在Laravel 3中,我们可以做类似的事情(在Laravel论坛中积分) :

In Laravel 3 we could do something like so (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中的效果不同.

Unfortunately, View::loader has entirely disappeared in Laravel 4. When digging through \Illuminate\View\View and \Illuminate\View\Environment, I discovered that each view dispatches an event named "composing: {view_name}". Listening for this event provides the view name and data being passed to it on each view render, however returning from the callback does not have the same effect as it did in 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);

以上内容并未绕过普通视图包括渲染过程.

The above does not circumvent the normal view including and rendering process.

那么您如何规避常规视图渲染并从此合成事件中返回缓存的内容?目前在Laravel中可能没有一些丑陋的骇客吗?

So how can you circumvent normal view rendering and return cached content from this composing event? Is it possible currently in Laravel without some ugly hackery?

推荐答案

又快又脏

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

Well, one option, as I'm sure you know, is to cache the items inside of controllers as the View is being rendered. I suspect you don't want to do that, as it's less maintainable in the long-run.

更具可维护性(?)方法

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

However, if the View loader/renderer doesn't fire an event where you want, you can create one. Because every package/library in Laravel 4 is set in the App container, you can actually replace the View library with your own.

我要采取的步骤是:

  1. 创建一个库/程序包.目标是创建一个扩展Laravel视图逻辑的类.看一看之后,您可能想扩展这一个-这是View门面
  2. 如果您使用自己的扩展View外观(也就是如果我对步骤1中的文件的假设是正确的),则只需替换
  1. Create a library/package. The goal is to create a class which extends Laravel's view logic. After taking a look, you might want to extend this one - This is the View facade
  2. If you extended the View facade with your own (aka if my assumption on the file in step 1 is correct), you'll then just need to replace the alias for View in app/config/app.php with your own.

编辑-我玩了一点.尽管我不一定同意缓存View结果,而不是缓存sql查询或重载",但这是我在Laravel 4中要做的事情:

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

The View rendering in Laravel 4 doesn't fire an event that let's us cache the result of a view. Here's how I've added in that functionality to cache a view's result.

您可能要考虑缓存视图结果的后果.例如,这并没有完成与数据库的对话以获取视图所需数据的艰苦工作.无论如何,这都很好地概述了扩展或替换核心项目的情况.

You may want to consider the ramifications of caching a view's result. For instance, this doesn't get around doing the hard work of talking to a datbase to get the data needed for the view. In any case, this gives a good overview on extending or replacing core items.

首先,创建一个程序包并设置其自动加载功能.我将使用命名空间Fideloper\View.它在composer.json中的自动加载如下:

First, create a package and set up its autoloading. I'll use the namespace Fideloper\View. It's autoloading in composer.json will looks like this:

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

接下来,创建一个类来替换View外观.就我们而言,这意味着我们将扩展 Illuminate \查看\环境.

Next, create a class to replace the View facade. In our case, that means we'll be extending Illuminate\View\Environment.

在此类中,我们将获取呈现View的结果,并添加一些逻辑以对其进行缓存(或不缓存).这是Fideloper/View/Environment.php:

In this class, we'll take the result of the View being rendered and add some logic to cache (or not cache) it. Here is 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.但是,我们还有一些事要做.

So, that's where the bulk of your work will be - filling out that // Cache Logic Here. However, we have some plumbing left to do.

接下来,我们需要设置新的Environment类以用作Facade.我有一篇有关创建Laravel外墙的博客文章.在这种情况下,以下是完成此操作的方法:

Next, we need to set up our new Environment class to work as a Facade. I have a blog post about creating Laravel facades. Here's how to accomplish that in this case:

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

Create the facade for our new Environment. We'll name it fideloper.view in code.

<?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时要创建的内容.请注意,这需要模仿Illuminate\View\ViewServiceProvider的功能来创建扩展的Environment类.

Then, create the Service Provider which will tell Laravel what to create when fideloper.view is called. Note that this needs to mimic functionality of the Illuminate\View\ViewServiceProvider for creating the extended Environment class.

<?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加载我们的服务提供商,并用我们自己的Illuminate的View门面替换.编辑app/config/app.php:

Lastly, we need to hook this all together and tell Laravel to load our Service Provider and replace Illuminate's View facade with our own. Edit app/config/app.php:

添加服务提供商:

'providers' => array(

    // Other providers

    'Fideloper\View\ViewServiceProvider',

),

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

Replace the View facade with our own:

'aliases' => array(

    // Other Aliases

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

),

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

You'll then be able to use whatever logic you wish in the View::make() method!

最后

值得注意的是,每个Web请求中的多个请求"中都需要加载一些模式.例如,Symfony让您将控制器定义为服务器. Zend有(有吗?)操作堆栈的概念,可以让您

It's worth noting that there are some patterns to load in multiple "requests" per web request. Symfony, for instance, let's you define controllers as servers. Zend has (had?) a concept of Action Stacks, which let you

...有效地帮助您创建要在请求期间执行的[controller]操作队列.

... effectively help you create a queue of [controller] actions to execute during the request.

也许您想在Laravel中探索这种可能性,并缓存这些操作"的结果(与直接缓存视图相对).

Perhaps you'd like to explore that possibility within Laravel, and cache the results of those "actions" (vs caching a view directly).

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

Just a thought, not a recommendation.

这篇关于在Laravel 4中缓存视图输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆