Русские документы
Ежедневные компьютерные новости RSS rusdoc.ru  Найти :
Новости
Последние поступления
Книжный магазин
  Hardware:
Видеоустройства
Системные платы
Процессоры
Мобильные устройства
Аудиосистема
Охлаждение системы
Накопители информации
КПК и ноутбуки
Телефоны и связь
Периферия
Система
Сети
Разные устройства
 
  Programming:
Web-разработка
Языки программирования
Технологии и теория
Разработка игр
Программная инженерия
 
  Software:
Операционные системы
Windows 7
Базы данных
Обзоры программ
Графика и дизайн
   
  Life:
Компьютерная жизнь
Разные материалы
   
Партнеры
Публикация
Правовая информация
Реклама на сайте
Обратная связь
Экспорт в RSS Экспорт в RSS2.0
    Читать в Яндекс.Ленте



Фрагментарное кэширование в MVC веб-фреймворках

Раздел: Programming / PHP @ 30.03.2008 | Ключевые слова: MVC web framework кэш версия для печати

Автор: dmmd
Источник: habrahabr

Наверняка большинство программистов, работающих с современными веб-фрейворками, реализующими схему MVC, сталкивалось с таким небольшим затруднением: кэширование фрагмента View.

Хорошие фреймворки предлагают инструменты для полного кэширования страниц, фрагментарного, или кэширования экшенов. Недавно я посмотрел 90 выпуск подкаста Railscasts, посвященный именно фрагментарному кэшированию в Ruby on Rails и уважаемый автор решал проблему, как мне показалось, неоптимально.

Опишу ситуацию.
Мы в шаблоне страницы и хотим закэшировать ее часть, например, список новых товаров. Пока все хорошо, мы пользуемся встроенными во фреймворк удобными средствами и в две-три строчки окружаем блок - ура, он кэшируется. Но - чу!, контроллер-то об этом ничего не знает и продолжает выполнять свою работу по подготовке данных для View. Естественно, ведь проверка наличия кэша осуществляется уже из шаблона, а контроллер к тому моменту отработал.

Автор подкаста показывает некрасивое решение - перенос кода для подготовки данных в шаблон и тут же, естественно, отметает его, как "ugly". Что он предлагает - перенести этот код в модель. То есть, в модели товара создается специальный метод, который выбирает новые товары, и этот метод вызывается из шаблона. Это лучше, чем первый вариант, но все же недостаточно хорошо, так как в модели приходится реализовывать вещи, которые могут понадобиться в одном только месте, а при смене интерфейса сайта могут оказаться ненужными и скорее всего останутся болтаться в коде просто так.

Мое решение

Я работаю со своим фреймворком на PHP, и пример буду писать на PHP, но решение простое и реализуется на любом скриптовом язке.

view.php:


...

<? if !(cacher::start(`Cache_Name`)) { ?>

	<ul>

		<? foreach ($latest as $item) { ?>

			<li><?=$item->name();?>: <?=$item->price();?></li>

		<? } ?>

	</ul>

<? cacher::end(); } ?>

...




controller.php:


...

$latest = new model_collection(`product`);

$latest->load_by( $condition, $order, $limit );



$this->export(`latest`, $latest);

...




Метод load_by(...) выполняет один или несколько запросов к базе данных и формирует набор моделей класса Product. То есть, тратятся ресурсы на запрос, да еще и память на экземпляры модели.
Хорошо бы как-то запомнить, что мы хотим сделать, а делать это только если кэша нет.
Напишем это.

utils.php:


...

class prepared extends stdClass // крохотный класс для хранения подготовленной операции

{

	// не буду усложнять пример геттерами и сеттерами

	public $obj, $method, $args;

}



class utils

{

...

	public static function prepare( $obj, $method, $args = null )

	{

		$res = new prepared();



		// метод принимает неограниченное количество параметров

		$args = func_get_args();

		$res->obj = array_shift($args);

		$res->method = array_shift($args);

		

		// запоминаем все остальные параметры

		$res->args = $args;



		return $res;

	}



	public static function run( $prepared )

	{

		// страховка: шаблон не должен думать, пришли ли реальные данные, или заготовка

		if (!($prepared instance_of prepared)) return $prepared;

		

		$obj = $prepared->obj;



		// вариант с заготовленным статическим методом

		if (`::` === mb_substr( $prepared->method, 0, 2)) $method = $obj.$prepared->method;

		else $method = array( $obj, $prepared->method ); // обычный метод



		return call_user_func_array( $method, $prepared->args );

	}



...

}

...




Использование

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

...




view.php:


...



    <? if !(cacher::start(`Cache_Name`)) { ?> <? // только здесь выполняем запланированное, при этом шаблону не нужно знать, что именно делается $latest = utils::run( $latest ); ?> <ul> <? foreach ($latest as $item) { ?> <li><?=$item->name();?>: <?=$item->price();?></li> <? } ?> </ul> <? cacher::end(); } ?> ...

Предположим, в вашем фреймворке товары надо было бы грузить статическим методом. Пожалуйста, можно и так:

controller.php:


...

// ничего не грузим сразу

// $latest = Product::get_latest(...);

// запоминаем, что мы хотим сделать, в самой переменной для шаблона

$latest = utils::prepare( `Product`, `::get_latest`, ... );



$this->export(`latest`, $latest);

...




В шаблоне же даже ничего не нужно менять.
Этот способ я использую во множестве мест и пока он меня не подводил. Недостаток: пока не удается готовить наборы операций, но в таких извращенных случаях уже можно и метод где-нибудь добавить.

Буду рад комментариям.

Апдейт

В комментариях мне указывают на наличие компонентов и возможности кэшировать их целиком. Я вынужден пояснить - заметка не об этом. Приведу другой пример, из реальной жизни.

Страница со списком новостей, экшен `index` контроллера `news`.


...

$news = new model_collection(`news`); // или как у вас

$news->load_by( $conditions, $order, $limit );



$this->export(`news`, $news);

...




Шаблон со списком новостей вкладывается в лэйаут, в котором присутствует еще куча компонент (новые товары, курсы валют и прочее). Компоненты кэшируются целиком, естественно. Но вот именно "основной" экшен же нам надо выполнить, мы страницу целиком закэшировать чаще всего не можем.
Тут-то и пригождается описанный подход - данные не доставать сразу, а только приготовиться. Можно, конечно, вынести непосредственно вывод новостей в еще один экшен, но таким путем мы почти удвоим количество экшенов, а это явно неудобно.

Так должно быть понятней.

Это интересно:








версия для печатиРаспечатать статью


Вернуться в раздел: Programming / PHP


Реклама:
Читать наc на:

Add to Google
Читать в Яндекс.Ленте






Rambler's Top100
© Copyright 1998-2012 Александр Томов. All rights reserved.