2013-07-04 71 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