суббота, 29 января 2011 г.

Kohana 3: Работа с контроллерами

В прошлой части мы познакомились с отображениями, в этой части мы будем расширять классы Controller, что позволит нам создавать шаблоны для сайтов. Шаблон - обычное отображение которое более или менее базируется на (X)HTML коде. Это позволит содержать наши отображения в рамках DRY или DIE принципа разработки.


Начнём с шаблона страницы. Создадим в каталоге 'application/views' каталог с именем 'templates', запустим наш любимый редактор или IDE и создадим файл со следующим содержимым:

<!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" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
    <title><?php echo $title;?></title>
    <meta name="keywords" content="<?php echo $meta_keywords;?>" />
    <meta name="description" content="<?php echo $meta_description;?>" />
    <meta name="copyright" content="<?php echo $meta_copywrite;?>" />
    <?php foreach($styles as $file => $type) { echo HTML::style($file,array('media' => $type)), "\n"; }?>
    <?php foreach($scripts as $file) { echo HTML::script($file), "\n"; }?>
  </head>
  <body>
    <div id="container">
     <?php echo $header;?>
     <?php echo $content;?>
     <?php echo $footer;?>
    </div>
  </body>
</html>

Сохраним файл под именем 'default.php' в каталоге 'application/views/templates'.

Как вы можете видеть файл шаблона сильно похож на отображения, которые мы создавали ранее (foreach() мы рассмотрим чуть позже), но в отличии от отображения он, скорее всего, будет использоваться во всём вашем проекте. Поскольку мы будем указывать этот файл в качестве шаблона, это уменьшит количество кода который был бы использован в каждом отдельном отображении, что сильно упрощает поддержку проекта.

Итак, у нас есть шаблон, но фреймворк не собирается ничего с ним делать, пока мы не укажем как его использовать. Вернёмся в нашему редактору или IDE и создадим файл 'defaulttemplate.php' в каталоге 'application/classes/controller/' со следующим содержимым:

<?php
 defined('SYSPATH') or die('No direct script access.');

 class Controller_DefaultTemplate extends Controller_Template
  {
     public $template = 'templates/default';

     /**
      * Инициализируем свойства до запуска методов контроллера (actions),
      */
     public function before()
      {
         // Выполняем всё что нужно выполнить до этого
         parent::before();

         if($this->auto_render)
          {
            // Инициализируем переменные шаблона с пустыми значениями
            $this->template->title            = '';
            $this->template->meta_keywords    = '';
            $this->template->meta_description = '';
            $this->template->meta_copywrite   = '';
            $this->template->header           = '';
            $this->template->content          = '';
            $this->template->footer           = '';
            $this->template->styles           = array();
            $this->template->scripts          = array();
          }
      }

     /**
      * Заполняем значения по умолчанию для наших свойств перед выводом.
      */
     public function after()
      {
         if($this->auto_render)
          {
             // Определяем значения по умолчанию
             $styles                  = array('assets/css/reset.css' => 'screen');
             $scripts                 =array('http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js');

             // Добавляем значения по умолчанию к переменным шаблона
             $this->template->styles  = array_reverse(array_merge($this->template->styles, $styles));
             $this->template->scripts = array_reverse(array_merge($this->template->scripts, $scripts));
           }

         // Выполняем всё что нужно выполнить после этого
         parent::after();
      }
 }

Приведённый выше код расширяет контроллер класс 'Controller_Template' и выполняет три основные вещи: инициализирует свойства (переменные) класса и делает их доступными для наших методов, устанавливает значения по умолчанию, затем связывает значения по умолчанию с переменными шаблона перед формированием и выводом шаблона на экран. Тут foreach() цикл в шаблоне вступает в игру. Цикл foreach() использует статические хэлперы-методы класса 'HTML', один для загрузки CSS стилей и ещё один для загрузки JavaScript файлов. Оба этих хелпера-метода обработают массив и упакуют значения в соответствующие тэги, значениями массива могут выступать локальный путь или URL.

Если вы не знакомы с хэлперами, то вам поможет их краткое определение из документации фреймворка Kohana 2

Хэлперы - простые 'подручные' функции которые помогает вам в разработке.

Хэлперы похожи на библиотеки методов, но есть различие. С библиотекой вы должны создавать экземпляр класса библиотеки и использовать его методы. Хэлперы объявлены как статические методы класса, что позволяет их использовать без создания экземпляра класса. Вы можете представлять их как "глобальные функции"

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

Немного о Controller_Template.

Абстрактный класс для автоматизации работы с шаблонами, является потомком класса Controller и содержит следующие свойства:

$template - строковый параметр, указывающий на текущий шаблон, по умолчанию 'template'.

$auto_render - логическая переменная, указывающая формировать автоматически страницу или нет, по умолчанию 'TRUE'.

Методы:

before() - загружает файл шаблона.

after() - передаёт сформированный шаблон в качестве ответа на запрос.

Вернёмся к нашему шаблону. Как вы могли заметить есть ссылка на 'assets/css/reset.css'. Давайте рассмотрим этот момент. В DocementRoot нашего проекта создадим каталог 'assets' и в нём создадим ещё один каталог 'css'. Сохраним 'reset.css' от Эрика Майера в каталоге 'assets/css'. Вы можете создать в каталоге assets подкаталоги 'images', 'js' или 'files' для упорядочения статических файлов вашего проекта.

На данный момент наше приложение всё ещё не знает, что делать с тем что мы сделали. Нам нужно изменить наш контроллер. Откроем 'application/classes/controller/first.php'. Мы хотим изменить класс предок нашего контроллера, заменим следующие строки:

class Controller_First extends Controller

на:

class Controller_First extends Controller_DefaultTemplate

Также на необходимо изменить наш метод action_index(), приведя его к следующему виду:

public function action_index()
{
 $first_inner   = array();
 $first    = array();
 $this->template->title = 'Kohana 3';

 View::set_global('x', 'Это глобальная переменная');

 $first_inner['content'] = 'У нас есть ещё данные';
 $first['content'] = 'У нас есть данные!';
 $first['first_inner'] = View::factory('blocks/first_inner', $first_inner)
    ->render();

 $this->template->content= View::factory('pages/first', $first);
}

Сохраним его. Вы можете заметить, что у нас появилась новая строка "$this->template->title = 'Kohana 3'", здесь мы присваиваем значение переменной шаблона 'title'. Следующее, на что вы могли обратить внимание - отсутствие 'render()' метода. Теперь в нём нет необходимости, поскольку 'factory()' метода автоматически формирует в нашем шаблоне переменную 'content'.

Есть ещё одна вещь которую нужно сделать перед загрузкой страницы в браузере. Откроем в редакторе наше отображение 'first.php' расположенное в 'application/views/pages/' и удалив лишний код приведём его к следующему виду:

<h1>Это моё первое отображение</h1>
<?php echo $content; ?>
<br/><?php echo $x;?>
<?php echo $first_inner; ?>

Если вы загрузите страницу в браузере, то увидите, что она имеет заголовок 'Kohana 3' и выглядит практически как в прошлой части, но код страницы выглядит совершенно по другому. Возможно вы удивлены, что мы не используем другие переменные нашего шаблона, давайте это исправим. Изменим action_index() ещё раз:

public function action_index()
{
 $first_inner    = array();
 $first     = array();
 $this->template->title  = "Kohana 3";
 $this->template->meta_keywords = 'PHP, Kohana, KO3, Framework';
 $this->template->meta_description = 'A test of of the KO3 framework';
 $this->template->styles  = array('assets/css/red.css' => 'screen');
 $this->template->scripts  = array('assets/js/jqtest.js');
            
 View::set_global('x', 'Это глобальная переменная');
            
 $first_inner['content'] = 'У нас есть ещё данные';
 $first['content'] = 'У нас есть данные!';
 $first['first_inner'] = View::factory('blocks/first_inner', $first_inner)
    ->render();

 $this->template->content= View::factory('pages/first', $first);
}

Всё довольно просто. Вы заметили, что не заполнены header и footer? Я уверен, вы знаете, что с ними делать.

Подсказка: сформировать отображение в эту переменную. :) Ещё вы должны заметить, что я добавил 'assets/css/red.css' и 'assets/css/jqtest.js'. Давайте создадим эти файлы, начнём с 'assets/css/red.css':

h1
{
    color: #FF0000;
}

Следующий 'assets/js/jqtest.js':

$("document").ready(function()
 {
 alert('Привет Kohana!');
 });

Сохраним их и обновим страницу в браузере. Вы должны увидеть всплывающее окно с предупреждением и первая строка текста стала красной.

Сегодня мы создали файл шаблона, расширили контроллер шаблона и наш контроллер использовал этот шаблон. У того что мы сегодня сделали большой потенциал.

Файлы этого руководства находятся здесь. Так же их можно получить с помощью Subversion :

svn checkout http://dev-mark.googlecode.com/svn/trunk/dev-mark/Kohana3-tutorial/tutorial_03 Kohana3-tutorial-03

В следующий раз мы будем работать с моделями.

P.S. За основу взяты материалы с Inside DealTaker и Unofficial Kohana 3.0 Wiki

Другие части руководства:

Похожие по тематике посты:

6 комментариев:

Славик комментирует...

Спасибо за подробное руководство! Как раз разбираюсь с Kohana 3, и пока не прочитал, никак не мог понять, как связать базовый шаблон с контроллерами.
Только один вопрос: вместо того, чтобы создавать Controller_DefaultTemplate разве нельзя "перегрузить" непосредственно контроллер Controller_Template?

snake.nf комментирует...

Всё зависит от задачи которую нужно решить.

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

Но, если у нас имеется общий шаблон для всего сайта и соответственно всех контроллеров, то удобнее создать класс-прослойку который будет подключать шаблон и обрабатывать методы before() и after(), а его потомкам остается только передавать данные в переменные шаблона.

soulfly комментирует...

В этом уроке все получилось за исключением применения CSS и JS. Стиль к заголовку не применяется и предупреждения тоже нет. Подозрения на настройки .htaccess т.к. по ссылкам из сформированного HTML открыть эти файлы тоже не получается.

snake.nf комментирует...

Какую ошибку пишет в логах apache?
Какие ссылки генерируются?
Какой путь к файлам CSS и JS относительно DocumentRoot apache?
Если у вас linux, то имена файлов и каталогов должны быть в нижнем регистре и открыт доступ к файлам на чтение для всех.

Должны сформироваться такие ссылки:
<link type="text/css" href="/assets/css/reset.css" rel="stylesheet" media="screen" />
<link type="text/css" href="/assets/css/red.css" rel="stylesheet" media="screen" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="/assets/js/jqtest.js"></script>

Возможно проблема с размещением файлов.
Если к адресам сформированных ссылок добавить путь из DocumentRoot настроек сайта, то получим путь к файлам в системе.

Например у меня под WinXP у виртуального хоста DocumentRoot c:/xampp/vhosts/kohana/webroot
Соответственно файл /assets/css/red.css расположен c:/xampp/vhosts/kohana/webroot/assets/css/red.css

Проверьте соответствует ли у вас физическое расположение файлов в системе относительному у вашего сайта (DocumentRoot + Link)

Анонимный комментирует...

ошибке в логах нет, только в access.log пишет 404.
физически файл лежит в /www/application/asset/css/red.css
DocumentRoot = c:/apache/front.local/www

Все возможные пути изменял, где может быть ошибка?

snake.nf комментирует...

У вас red.css должен физически распологаться в c:/apache/front.local/www/assets/css/red.css
Путь к нему в браузере http://имя_вашего_хоста/assets/css/red.css