在 Laravel 4 中缓存视图输出

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17460157/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 08:05:11  来源:igfitidea点击:

Caching View Output in Laravel 4

phpcachingviewlaravelblade

提问by Bailey Parker

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.

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

In Laravel 3 we could do something like so (credit Laravel forums):

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

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

Unfortunately, View::loaderhas entirely disappeared in Laravel 4. When digging through \Illuminate\View\Viewand \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:

不幸的是,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);

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

以上并没有绕过正常的视图包含和渲染过程。

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?

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

回答by fideloper

Quick and Dirty

又快又脏

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.

嗯,正如我确定您知道的那样,一种选择是在呈现视图时缓存控制器内的项目。我怀疑您不想这样做,因为从长远来看,它的可维护性较差。

More maintainable(?) method

更易于维护(?)方法

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.

但是,如果视图加载器/渲染器没有在您想要的地方触发事件,您可以创建一个。因为 Laravel 4 中的每个包/库都是在 App 容器中设置的,你实际上可以用你自己的来替换 View 库。

The steps I would take is:

我将采取的步骤是:

  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 Viewfacade
  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 Viewin app/config/app.phpwith your own.
  1. 创建一个库/包。目标是创建一个扩展 Laravel 视图逻辑的类。看看之后,你可能想扩展这个- 这是View门面
  2. 如果您使用自己的外观扩展了 View 外观(也就是说,如果我在第 1 步中对文件的假设是正确的),那么您只需要将Viewin的别名替换app/config/app.php您自己的。

Edit- I played with this a bit. Although I don't necessarily agree with caching a View result, vs caching sql queries or the "heavier lifts", here is how I'd go about doing this in Laravel 4:

编辑 - 我玩了一下。虽然我不一定同意缓存 View 结果,vs 缓存 sql 查询或“更重的提升”,以下是我在 Laravel 4 中执行此操作的方法

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.

Laravel 4 中的视图渲染不会触发让我们缓存视图结果的事件。下面是我如何添加该功能来缓存视图的结果。

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.

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

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

首先,创建一个包并设置其自动加载。我将使用命名空间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/"
    }
},

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

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

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:

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

}

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.

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

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

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

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

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

}

Then, create the Service Provider which will tell Laravel what to create when fideloper.viewis called. Note that this needs to mimic functionality of the Illuminate\View\ViewServiceProviderfor creating the extended Environmentclass.

然后,创建服务提供者,它会告诉 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;
        });
    }

}

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:

最后,我们需要将所有这些连接在一起并告诉 Laravel 加载我们的服务提供者并用我们自己的替换 Illuminate 的 View 外观。编辑app/config/app.php

Add the Service Provider:

添加服务提供者:

'providers' => array(

    // Other providers

    'Fideloper\View\ViewServiceProvider',

),

Replace the View facade with our own:

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

'aliases' => array(

    // Other Aliases

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

),

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

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

Finally

最后

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

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

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

... 有效地帮助您创建一个 [controller] 操作队列以在请求期间执行。

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

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

Just a thought, not a recommendation.

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

回答by Yaroslav

There is a library for caching views/parts in Laravel(and not only) - Flatten.

在 Laravel 中有一个用于缓存视图/部分的库(不仅仅是) - Flatten。

It is a powerful cache system for caching pages at runtime. What it does is quite simple : you tell him which page are to be cached, when the cache is to be flushed, and from there Flatten handles it all. It will quietly flatten your pages to plain HTML and store them. That whay if an user visit a page that has already been flattened, all the PHP is highHymaned to instead display a simple HTML page. This will provide an essential boost to your application's speed, as your page's cache only gets refreshed when a change is made to the data it displays.

它是一个强大的缓存系统,用于在运行时缓存页面。它的作用很简单:你告诉他要缓存哪个页面,何时刷新缓存,然后 Flatten 处理这一切。它会悄悄地将您的页面扁平化为纯 HTML 并存储它们。如果用户访问一个已经扁平化的页面,那么所有的 PHP 都会被劫持以显示一个简单的 HTML 页面。这将为您的应用程序的速度提供重要的提升,因为您的页面缓存仅在对其显示的数据进行更改时才会刷新。

To cache all authorized pages in your application via the artisan flatten:buildcommand. It will crawl your application and go from page to page, caching all the pages you allowed him to.

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

Flushing

冲洗

Sometimes you may want to flush a specific page or pattern. If per example you cache your users's profiles, you may want to flush those when the user edit its informations. You can do so via the following methods :

有时您可能想要刷新特定页面或模式。如果每个示例缓存用户的配置文件,您可能希望在用户编辑其信息时刷新这些配置文件。您可以通过以下方法执行此操作:

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

// Flushing via an UrlGenerator
Flatten::flushRoute('user', 'taylorotwell');
Flatten::flushAction('UsersController@user', 'taylorotwell');

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

link to - https://github.com/Anahkiasen/flatten

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