MVC Webフレームワークでの断片化されたキャッシング

確かに、MVCスキームを実装する最新のWebフレームワークを使用するほとんどのプログラマーは、Viewフラグメントをキャッシュするという小さな問題に直面しています。



優れたフレームワークは、フルページキャッシュ、フラグメント化、またはアクションキャッシュのためのツールを提供します。 最近、Ruby on Railsの断片化されたキャッシングに特化したRailscastsポッドキャストの第90号を見て、尊敬されている著者が問題を解決しました。



状況を説明します。

ページテンプレート内にあり、その一部(たとえば、新製品のリスト)をキャッシュする必要があります。 これまでのところ、フレームワークに組み込まれた便利なツールを使用して、ブロックを2、3行で囲んでいます。まあ、キャッシュされています。 しかし-chu !、コントローラーはこれについて何も知らず、Viewのデータを準備する仕事を続けます。 当然、結局、キャッシュはテンプレートからすでにチェックされており、コントローラーはそれまでに機能していました。



ポッドキャストの作者はい解決策を示しています-テンプレート内のデータを準備するためにコードを移動し、もちろんすぐに「ugい」とマークします。 彼が提案するのは、このコードをモデルに転送することです。 つまり、新しい製品を選択する特別なメソッドが製品モデルに作成され、このメソッドはテンプレートから呼び出されます。

これは最初のオプションよりも優れていますが、まだ十分ではありません。モデルは1か所で必要なものを実装する必要があるため、サイトインターフェースを変更する場合、それらは不要である可能性があり、ほとんどの場合、そのようにコードにぶらつくことになります。



私の決断



私は自分のPHPフレームワークを使用し、PHPで例を作成しますが、ソリューションは単純であり、任意のスクリプト言語で実装できます。



view.php

 <コード>
 ...
 <?  if!(cacher :: start( 'Cache_Name')){?>
	 <ul>
		 <?  foreach($アイテムとして最新$){?>
			 <li> <?= $ item-> name();?>:<?= $ item-> price();?> </ li>
		 <?  }?>
	 </ ul>
 <? キャッシュ機能:: end();  }?>
 ...
 </ code>




controller.php

 <コード>
 ...
 $ latest = new model_collection( 'product');
 $ latest-> load_by($条件、$注文、$制限);

 $ this-> export( 'latest'、$ latest);
 ...
 </ code>




load_by(...)メソッドは、1つ以上のデータベースクエリを実行し、Productクラスのモデルのセットを形成します。 つまり、リクエストにリソースが費やされ、モデルインスタンスにメモリが費やされます。

何をしたいのかを何らかの形で覚えていて、キャッシュがない場合にのみこれを行うといいでしょう。

これを書いて。



utils.php

 <コード>
 ...
準備されたクラスはstdClassを拡張します//準備された操作を格納するための小さなクラス
 {
	 //ゲッターとセッターで例を複雑にしません
	 public $ obj、$ method、$ args;
 }

クラスutils
 {
 ...
	 public static function prepare($ obj、$ method、$ args = null)
	 {
		 $ res =新規準備済み();

		 //メソッドは無制限の数のパラメーターを受け入れます
		 $ args = func_get_args();
		 $ res-> obj = array_shift($ args);
		 $ res-> method = array_shift($ args);
		
		 //他のすべてのパラメーターを記憶します
		 $ res-> args = $ args;

		 $ resを返します。
	 }

	パブリック静的関数の実行($準備済み)
	 {
		 //保険:テンプレートは、実際のデータが来たか、空白かを考えるべきではありません
		 if(!($ prepared instance_of prepared)))は、$ preparedを返します。
		
		 return call_user_func_array(array($ prepared-> obj、$ prepared-> method)、$ prepared-> args);
	 }

 ...
 }
 ...
 </ code>


run()



メソッドはdavojanプロンプトで単純化されます





使用する





controller.php

 <コード>
 ...
 $ latest = new model_collection( 'product');
 //すぐには何もロードしない
 // $ latest-> load_by($ condition、$ order、$ limit);
 //テンプレートの変数自体で何をしたいのかを覚えている
 $ latest = utils :: prepare($ latest、 'load_by'、$ condition、$ order、$ limit);

 $ this-> export( 'latest'、$ latest);
 ...
 </ code>




view.php

 <コード>
 ...
 <?  if!(cacher :: start( 'Cache_Name')){?>
	 <?
	 //ここでのみ計画を実行しますが、テンプレートは正確に何が行われているかを知る必要はありません
	 $ latest = utils :: run($ latest);
	 ?>
	 <ul>
		 <?  foreach($アイテムとして最新$){?>
			 <li> <?= $ item-> name();?>:<?= $ item-> price();?> </ li>
		 <?  }?>
	 </ ul>

 <? キャッシュ機能:: end();  }?>
 ...
 </ code>




フレームワークで、静的メソッドを使用して商品をロードする必要があるとします。 これを行うことができます:



controller.php

 <コード>
 ...
 //すぐには何もロードしない
 // $ latest = Product :: get_latest(...);
 //テンプレートの変数自体で何をしたいのかを覚えている
 $ latest = utils :: prepare( 'Product'、 'get_latest'、...);

 $ this-> export( 'latest'、$ latest);
 ...
 </ code>




テンプレートでは、何も変更する必要さえありません。

この方法は多くの場所で、失敗するまで使用します。 欠点:一連の操作を準備することはまだできませんが、そのような異常なケースでは、どこかにメソッドを追加することはすでに可能です。



コメントさせていただきます。





更新する



コメントでは、コンポーネントの存在とそれらを完全にキャッシュする機能を示しています。 私は明確にする必要があります-メモはそれについてではありません。 実生活から別の例を挙げましょう。



ニュースリストページ、コントローラー「ニュース」のアクション「インデックス」。

 <コード>
 ...
 $ news = new model_collection( 'news');  //またはあなたが持っているものは何でも
 $ news-> load_by($条件、$注文、$制限);

 $ this-> export( 'news'、$ news);
 ...
 </ code>




ニュースのリストを含むテンプレートがレイアウトに埋め込まれていますが、そこにはまだ多くのコンポーネント(新製品、為替レートなど)があります。 コンポーネントは、当然、完全にキャッシュされます。 しかし、実行する必要があるのはまさに「メイン」アクションであり、ほとんどの場合、ページ全体をキャッシュすることはできません。

ここで、説明したアプローチが役立ちます。データをすぐに取得せずに、準備を整えてください。 もちろん、ニュース出力を別のアクションに直接持ち込むこともできますが、この方法ではアクションの数がほぼ2倍になり、明らかに不便です。



だから、それはより明確でなければなりません。



All Articles