在 Laravel 4 中缓存视图输出


Caching View Output in Laravel 4

我知道 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'));
});

不幸的是,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 4 中的每个包/库都在 App 容器中设置,因此您实际上可以将 View 库替换为自己的库。

我要采取的步骤是:

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

编辑 - 我玩了一下这个。虽然我不一定同意缓存视图结果,而不是缓存sql查询或"更重的提升",但这是我在Laravel 4中执行此操作的方法

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

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

首先,创建一个包并设置其自动加载。我将使用命名空间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类作为立面。我有一篇关于创建拉拉维尔外墙的博客文章。以下是在这种情况下实现此目的的方法:

为我们的新环境创建立面。我们将在代码中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加载我们的服务提供商,并将Illuminate的View立面替换为我们自己的外观。编辑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将控制器定义为服务器。Zend有一个(曾经?(动作堆栈的概念,它让你

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

也许您想在 Laravel 中探索这种可能性,并缓存这些"操作"的结果(而不是直接缓存视图(。

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

Laravel 中有一个用于缓存视图/部分的库(不仅如此( - 展平。

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

通过 artisan flatten:build 命令缓存应用程序中的所有授权页面。它将抓取您的应用程序并从一个页面转到另一个页面,缓存您允许他访问的所有页面。

冲洗

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

// 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');

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