Быстрое создание Web-сайтов с помощью CakePHP, Часть 5: Добавление кэширования

Повышение производительности приложений

CakePHP - это надёжное, готовое к использованию средство для быстрой разработки Web-сайтов на PHP. Данная серия "Быстрое создание Web-сайтов с помощью CakePHP" рассказывает о том, как разработать онлайновый каталог товаров с помощью CakePHP. Первая часть данного руководства рассказывает о том, как установить и запустить CakePHP, а вторая часть показывает использование scaffolding и Bake. Третья часть повествует об использовании компонентов Sanitize и Security для защиты информации, передаваемой пользователями, а четвертая часть знакомит с CakePHP-компонентом Session. Пятая часть занимается кэшированием, в частности кэшированием представления и макета, которое может помочь снизить потребление ресурсов сервера и повысить производительность вашего приложения.

Дуэйн О'Брайен, разработчик PHP, внештатный писатель

Дуэйн О'Брайен (Duane O'Brien) был разносторонним специалистом еще в те времена, когда у игры Oregon Trail был только текстовый интерфейс. Его любимый цвет - sushi (суши). Он никогда не бывал на луне.



22.05.2007

Введение

Данная серия руководств предназначена для тех разработчиков приложений на PHP, кто хочет начать использовать CakePHP, чтобы облегчить себе жизнь. Прочитав до конца, вы научитесь устанавливать и настраивать конфигурацию CakePHP, изучите основы проектирования Модель-Представление-Контроллер (MVC), узнаете, как проводить валидацию пользовательских данных в CakePHP, как применять помощников CakePHP, и как, используя CakePHP, можно быстро написать и запустить приложение. Может показаться, что нужно изучить очень много, но не переживайте - большую часть CakePHP сделает за вас.

Предполагается, что вы уже прочитали первую, вторую, третью, и четвертую части данной серии, и у вас есть рабочая среда, которая была установлена для выполнения примеров. Если у вас не установлен CakePHP, то прежде чем идти дальше, необходимо обратиться к первой и второй частям данной серии.

Предполагается, что вы знаете язык программирования PHP, обладаете фундаментальным пониманием проектирования баз данных, и не боитесь замарать рук.

Системные требования

Для того чтобы начать работу, у вас должна быть среда, в которой можно работать. CakePHP предъявляет минимальные разумные требования к серверу:

  1. Сервер HTTP с поддержкой сессий (и желательно mod_rewrite). Данное руководство было написано при использовании Apache V1.3 с mod_rewrite.
  2. PHP версии 4.3.2 или выше (включая PHP версии 5). Данное руководство создавалось на основе PHP версии 5.0.4
  3. Поддерживаемое ядро базы данных (на сегодняшний день - MySQL, PostgreSQL или используя надстройку над ADODB). Руководство было написано при использовании MySQL V4.1.15.

Также необходима база данных и пользователь базы данных, готовые к использованию приложением. В руководстве представлены синтаксические структуры для создания всех необходимых таблиц в MySQL.

Самый простой способ получить CakePHP - это зайти на сайт CakeForge.org и загрузить самую последнюю стабильную версию. Данное руководство создавалось на основе версии 1.1.8. (Полные сборки и копии Subversion также доступны для загрузки. Подробное описание приведено в Справочнике по CakePHP - см. Ресурсы).


Итак, Tor

В конце четвертой части, вам было задано усовершенствовать Tor. Справились?

Добавление представления и функции Favorites

Для просмотра списка Favorites (Избранное), необходимо добавить функцию favorites в контроллер users, как показано в листинге 1.

Листинг 1. Добавление функции favorites в контроллер users
function favorites () {
  $username = $this->Session->read('user');
  $favorites = array();
  if ($username)
  {
    $this->User->recursive = 2;
    $results = $this->User->findByUsername($username);
    foreach($results['Product'] as $product) 
    {
      $favorites[] = array('Product' => $product, 'Dealer' => $product['Dealer']);
    }
    $this->set('products', $favorites);
  } else {
    $this->redirect('/users/login');
  }
}

Не забудьте про переадресацию пользователя, если он не вошел в систему. Это позволит избежать появления ошибки, если пользователь просматривает страницу, не войдя в систему.

Также необходимо добавить файл favorites.html в директорию app/views/users/, который может выглядеть так, как показано в листинге 2.

Листинг 2. Favorites.html
<h2>Your Favorite Products</h2>
<?php
echo $this->renderElement('products_table', array('products' => $products) );
?>

Фактически, все, что делает представление, это показывает таблицу products. Однако сейчас таблица products отображается вместе с ссылкой Add To Favorites (добавить в Избранное). Это неправильно, так как просматривается именно список товаров в favorites (Избранном). Должна быть ссылка Remove From Favorites (удалить из Избранного).


Добавление ссылки Remove From Favorites (удалить из Избранного)

Итак, ваше задание заключалось в добавлении ссылки Remove From Favorites (удалить из Избранного) в таблицу products, настроив ее таким образом, чтобы пользователь видел ссылку Remove (удалить), если товар присутствует в его списке favorites (Избранное), и ссылку Add (добавить), если данного товара нет в списке favorites (Избранное) пользователя. Давайте посмотрим на таблицу products еще раз, так как это очень важно.

Листинг 3. Таблица рroducts
<?php 
if ( isset($ajax) ) {
 echo $ajax->link('Add to Favorites', '/products/add_to_favorites/' . 
  $product['Product']['id'], array('update'=>'updated', 'loading' => 
  "Element.show('loading')",'complete' => "Element.hide('loading')"));
}
?>

Ссылка Remove From Favorites (удалить из Избранного) аналогична тому, что показано в листинге 4.

Листинг 4. Ссылка Remove from Favorites (удалить из Избранного)
echo $ajax->link('Remove from Favorites', '/products/remove_from_favorites/' . 
  $product['Product']['id'], array('update'=>'updated', 'loading' =>  
  "Element.show('loading')",'complete' => "Element.hide('loading')"));

Также в контроллере products должна быть функция removeFromFavorites.

Листинг 5. Функция removeFromFavorites
function removeFromFavorites($id) {
       $username = $this->Session->read('user');
       $success = false;
       $user = $this->Product->User->findByUsername($username, 'User.id');
       $this->Product->User->id = $user['User']['id'];
       $success = $this->Product->User->removeFavorite($id);
       if ( $this->RequestHandler->isAjax() ) {
         $this->set('products', $this->Product->findAll());
       } else {
           if ( $success ) {
             $this->Session->setFlash('Removed product from favorites');
             $this->redirect('/users/favorites');
           } else {
             $this->Session->setFlash('Access denied');
             $this->redirect('/products/index');
           }
       }
   }

Аналогично, необходимо создать метод removeFavorite в контроллере users.

Листинг 6. Создание метода removeFavorite
function removeFavorite($product_id) {
     if($this->getID()) {
       $user = $this->read();
       $fav = array();
       foreach($user['Product'] as $product) {
         if ($product_id != $product['id'])
         {
           $fav[] = $product['id'];
         }
       }
       $user['Product'] = array('Product'=> $fav);
       if($this->save($user)) {
         return true;
       } else {
         return false;
       }
     }
     return false;
   }

Наверно, вы обратили внимание, что при просмотре списка favorites, отображается ссылка Add to Favorites (добавить в Избранное). Вместо нее пользователь должен видеть кнопку Remove from Favorites (удалить из Избранного) для тех товаров, которые были добавлены и находятся в списке favorites. Как это сделать?


Кэширование

По своей сути, кэширование иногда может быть очень сложным. Существуют различные типы кэширования, и у каждого есть свои преимущества и недостатки. Крайне важно понимать, что имеется в виду под словом "кэширование" в данном случае.

Понятие кэширования

Как правило, кэширование происходит при каждом запросе, когда отвечающее приложение говорит "Мне не нужно никуда идти за информацией. Она уже есть у меня". Чаще всего, если пользователь компьютера слышит слово "кэш", то он думает о кэше браузера. Обычно, для того чтобы ускорить работу пользователя, браузер сохраняет копии просмотренных статических файлов - рисунки, таблицы стилей, статические HTML-файлы и файлы сценариев. Хотя данный тип кэширования иногда доставляет неприятности разработчикам Web-приложений, в данном руководстве он рассматриваться не будет.

Еще один пример кэширования - запрос браузером контента у приложения. Если Web-приложение применяет кэширование, то оно может ответить на запрос предварительно созданной копией контента, что снижает излишние затраты ресурсов на вторичное создание контента. Данный тип кэширования и будет описан в данном приложении.

Зачем кэшировать?

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

Как кэшировать?

Итак, вас уговорили. Теперь вы готовы к тому, чтобы кэшировать все и всегда. Как это сделать? Как CakePHP может упростить данную задачу?

Для начала, необходимо включить кэширование. По умолчанию оно отключено. Включить его можно в app/config/core.php - найдите запись: define('CACHE_CHECK', false); и измените ее на define('CACHE_CHECK', true);.

Задавая значение true (истина), вы, тем самым, указываете CakePHP, что кэширование включено. Давайте пойдем дальше, так как настроить кэширование можно позднее. Мы еще не закончили: необходимо указать CakePHP, что конкретно необходимо кэшировать и на какой промежуток времени.


Что кэшировать?

Включив кэширование, необходимо указать, что нужно кэшировать. Это можно реализовать в контроллере для представлений, которые необходимо кэшировать, добавив кэширование в массив helpers. Например, если необходимо кэшировать представления products, то в массиве helpers контроллера products должно присутствовать кэширование. Когда вы создавали контроллер products в Tor, то указали, что используются helpers (помощниики) форм и HTML. При добавлении кэширования в этот список массив helpers будет выглядеть следующим образом: var $helpers = array('Html', 'Form', 'Cache' );.

Теперь, когда используется помощник кэширования, необходимо указать, что конкретно следует кэшировать. Это можно выполнить различными способами, однако все они зависят от массива $cacheAction.

Кэширование конкретного запроса

Предположим, что необходимо кэшировать конкретный запрос. Представим, что есть три или четыре товара, которые часто просматриваются, и вы хотите кэшировать только представления "просмотра" ("view") данных товаров. В этом случае, необходимо описать запросы, которые вы хотите кэшировать, в качестве ключей массива для $cacheAction и указать временной срок в качестве значений ключа. Массив $cacheAction является переменной класса так же, как и массив $helpers. Для кэширования этих конкретных представлений, $cacheAction должен выглядеть так, как показано в листинге 7.

Листинг 7. $cacheAction
var $cacheAction = array (
  'view/1/' => 3600,
  'view/2/' => 3600,
  'view/3/' => 3600
);

Данный $cacheAction указывает CakePHP, что необходимо кэшировать представления "просмотра" для товаров 1-3 в течение 3,600 секунд (один час). Указанный временной срок может задаваться в любом формате, который интерпретируется strtotime(). Можно просто задать 1 hour (1 час).

Кэширование целой функции

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

var $cacheAction = array (
  'edit/' => '+1 hour'
);

Можно даже смешать и согласовать.

Листинг 8. Смешивание и согласование
var $cacheAction = array (
  'view/1/' => 3600,
  'view/2/' => 3600,
  'view/3/' => 3600,
  'edit/' => '+1 hour'
);

Данный прием позволяет экономить время. Теперь вы кэшировали представления "просмотра" наиболее часто просматриваемых товаров и все представления редактирования, но может появиться желание сделать больше.

Кэширование всех функций контроллера

Возможно, необходимо кэшировать все, что делает контроллер. В этом случае, нет никакой необходимости описывать каждую функцию в массиве $cacheAction. Можно просто в качестве значения $cacheAction задать промежуток времени хранения кэшированной информации: var $cacheAction = "+1 hour";.

Значением должна быть строка, которую может интерпретировать strtotime(). Если задать $cacheAction только одно значение, то это укажет CakePHP, что нужно кэшировать все представления контроллера.

Кэширование из функции

Так как $cacheAction является переменной класса, то можно обращаться к переменной непосредственно из функции. Предположим, что необходимо изменить $cacheAction из функции. В этом случае, будет тот же самый синтаксис, который используется для изменения любой переменной класса.

function foo() {
  $this->cacheAction = array()...
}

Как правило, в этом нет никакой необходимости, но может возникнуть ситуация, когда понадобится и такое кэширование. Если вы столкнетесь с такой ситуацией, то теперь знаете, как это можно сделать. В CakePHP есть различные способы кэширования представлений. Это легко. Более сложной задачей, является определение когда нужно, а точнее, когда не нужно кэшировать.


Когда использовать кэширование?

Возможно, у вас появилось ощущение, что кэширование - это самое замечательное изобретение человечества. Более того, очень часто так и есть. Однако возникает вопрос, в каком случае не следует кэшировать представления?

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

Однако это вовсе не означает, что кэширование не нужно использовать вообще. Просто необходимо уточнить CakePHP что следует кэшировать.

Использование разметки <cake:nocache></cake:nocache> в представлении

В представлении или макете, CakePHP позволяет исключить некоторый контент из кэширования, если заключить его в теги <cake:nocache></cake:nocache>. Корректное использование этой разметки позволит CakePHP кэшировать статические части представления или макета, и гарантировать, что динамические составляющие страницы считываются при каждом запросе.

Не всю информацию можно заключать в теги <cake:nocache></cake:nocache>. А именно, нельзя просто заключить переменные в теги <cake:nocache></cake:nocache> для того, чтобы сделать их динамическими. В сущности говоря, в теги <cake:nocache></cake:nocache> можно заключать конструкции CakePHP, например, помощников и вызовы элементов.

Например, в Tor, используемый по умолчанию макет, созданный в четвертой части должен выглядеть примерно так :

Листинг 9. Макет, используемый по умолчанию
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Tor : <?php echo $title_for_layout;?></title>          
<link rel="icon" href="<?php echo $this->webroot . 'favicon.ico';?>" 
type="image/x-icon" />
<link rel="shortcut icon" href="<?php echo $this->webroot . 
'favicon.ico';?>" type="image/x-icon" />
<?php
if ( isset($javascript) ) {
  echo $javascript->link('prototype.js');
  echo $javascript->link('scriptaculous.js?load=effects');
  echo $javascript->link('controls.js');
}
?>
<?php echo $html->css('cake.generic');?>
</head>
<body>
 <div id="container">
   <div id="header">
     <h1><?php echo $html->link('Tor', '/') ?> : Welcome <?php echo 
$session->read('user') ?></h1>
     <div id="menu">
        <?php echo $html->link('Products', '/products') ?> |
        <?php echo $html->link('View Favorites', '/users/favorites') ?> |
        <?php echo $html->link('Login', '/users/login') ?> |
        <?php echo $html->link('Logout', '/users/logout') ?> |
     </div>
   </div>
   <div id="content">
     <?php if ($session->check('Message.flash'))
         {
           $session->flash();
         }
         echo $content_for_layout;
     ?>
   </div>
 </div>
 <?php echo $cakeDebug?>
</body>
</html>

Если кэширование включено, то при первой загрузке любой страницы, которая использует макет по умолчанию, Welcome <?php echo $session->read('user') ?> будет заменено на Welcome или Welcome wrestler - то есть, неважно какой пользователь вошел в систему, он увидит кэшированное имя пользователя.

Чтобы указать, что не следует кэшировать имя пользователя, необходимо заключить строку <?php echo $session->read('user') ?> в теги<cake:nocache></cake:nocache>. Окончательный результат будет выглядеть следующим образом:

Листинг 10. Описание того, что имя пользователя не должно кэшироваться
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Tor : <?php echo $title_for_layout;?></title>          
<link rel="icon" href="<?php echo $this->webroot . 'favicon.ico';?>" 
type="image/x-icon" />
<link rel="shortcut icon" href="<?php echo $this->webroot . 
'favicon.ico';?>" type="image/x-icon" />
<?php
if ( isset($javascript) ) {
  echo $javascript->link('prototype.js');
  echo $javascript->link('scriptaculous.js?load=effects');
  echo $javascript->link('controls.js');
}
?>
<?php echo $html->css('cake.generic');?>
</head>
<body>
 <div id="container">
   <div id="header">
     <h1><?php echo $html->link('Tor', '/') ?> : Welcome 
<cake:nocache><?php echo $session->read('user') 
?></cake:nocache></h1>
     <div id="menu">
        <?php echo $html->link('Products', '/products') ?> |
        <?php echo $html->link('View Favorites', '/users/favorites') ?> |
        <?php echo $html->link('Login', '/users/login') ?> |
        <?php echo $html->link('Logout', '/users/logout') ?> |
     </div>
   </div>
   <div id="content">
     <?php if ($session->check('Message.flash'))
         {
           $session->flash();
         }
         echo $content_for_layout;
     ?>
   </div>
 </div>
 <?php echo $cakeDebug?>
</body>
</html>

Можно выполнить проверку, указав, что контроллер products использует кэширование функции представления (view). Добавьте кэширование в список помощников (helpers) и укажите представление в $cacheAction.

Листинг 11. Описание того, что контроллер products использует кэширование функции представления
<?php
class ProductsController extends AppController
{
  var $name = 'Products';
  var $helpers = array('Html', 'Form', 'Javascript', 'Ajax', 'Cache' );
  var $components = array('Acl', 'RequestHandler');
  var $cacheAction = array(
    'view/' => '+1 hour'
  );
...

Теперь, посмотрите товар. Вы должны увидеть файл, созданный в app/tmp/cache/views, который соответствует тому товару, который вы смотрели. Зайдите в систему под другим пользователем и посмотрите тот же самый товар. Вы увидите, что имя пользователя не было кэшированно. Отредактируйте этот товар. Теперь CakePHP известно, что нужно удалить кэшированное представление. Разве не изящно?

Использование тегов <cake:nocache></cake:nocache> позволяет найти изящное решение кэширования представлений и сохранения актуального контента. Случается, что и этого недостаточно. Иногда приходится чистить кэш вручную.


Когда очищать кэш

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

Но вам не придется делать это самостоятельно, так как за вас это сделает CakePHP. CakePHP знает, что при добавлении, изменении или удалении данных, и если эти действия оказывают влияние на кэшированное представление, необходимо удалить кэшированное представление. Это позволяет сэкономить огромное количество усилий.

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

Как очищать кэш

Аналогично тому, как CakePHP предоставляет различные способы кэширования данных, существует несколько способов удаления информации из кэша:

  • Для очистки кэша только от кэшированного представления "просмотра" товара 1 необходимо использовать следующую структуру: clearCache('products_view_1');.
  • Для очистки кэша от всех представлений "просмотра" контроллера products необходимо использовать следующую структуру: clearCache('products_view');.
  • Для очистки кэша от всех представлений контроллера products необходимо использовать следующую структуру: clearCache('products');.
  • Для очистки кэша от различных типов представлений, необходимо передать массив в clearCache:
    clearCache('products', 'users_view');

    Если необходимо очистить весь кэш, то вызовите функцию без параметров: clearCache();. Эту функцию можно вызвать, создав контроллер emptycache и поместив вызов функции в функцию index. Данная функция должна вызываться вручную, или через что-либо в событии, которое должно быть автоматизировано процессом.

Заключение

Поздравляю! Вы прочитали всю серию до конца. Вероятно, вы в приподнятом настроении и готовы создавать свое собственное приложение.

Но прежде, в app/config/core.php, задайте 2 в качестве значения DEBUG. Это укажет CakePHP, что необходимо отображать отладочную информацию SQL в нижней части окна представлений. Кэшированные страницы хранятся в app/tmp/cache/views. Откройте одну из них, чтобы иметь представление о том, как выглядят кэшированные страницы. Удалите кэшированные файлы и представление товара. Обратите внимание на то, сколько запросов было сделано и как долго они выполнялись. Теперь перезагрузите. Представление было кэшировано. Сколько запросов было запущено на этот раз? Узнайте больше о кэшировании. Рассмотрите различные способы его использования в Tor. Когда разберетесь со всем окончательно, удалите его и займитесь разработкой своего собственного приложения.

Итак, вы много узнали из серии "Быстрое создание Web-сайтов с помощью CakePHP", но только создание своего собственного приложения с помощью CakePHP позволит применить свои знания на практике.


Загрузка

ОписаниеИмяРазмер
Part 5 source codeos-php-cake5.source.zip158KB

Ресурсы

Научиться

Получить продукты и технологии

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source
ArticleID=224842
ArticleTitle=Быстрое создание Web-сайтов с помощью CakePHP, Часть 5: Добавление кэширования
publish-date=05222007