Views в Kohana3

Просмотров: 7290Комментарии: 6
Web frameworks

В прошлом туториале мы установили Kohana на сборку XAMPP и запустили простейший трехстраничный сайт. Фактически, он представлял собой «голый» контроллер, выводящий в браузер запрошенную страницу. Бесспорно, такой подход имеет право на существование, особенно в веб-приложениях без пользовательского интерфейса. Однако при наличии достаточно сложной верстки отдаваемых посетителю страниц очень неудобно делать все в контроллере. Хочется выделить разметку в отдельный файл (или файлы; хотя это менее удобно для верстальщика, многие современные CMS делают именно так) и только «натягивать» их на данные по мере необходимости. Тут нам на помощь приходит реализованный в фреймворке Kohana архитектурный паттерн Model-View-Controller (MVC) в той своей части, которая обозначается как View-Controller. View как раз представляет собой шаблоны разметки для вывода данных, сами же данные предоставляются контроллером (хотя можно и напрямую из модели брать), который компонует Views в отдаваемую страницу. Впрочем, теория лучше понимается в сочетании с практикой. Давайте добавим к уже написанному нами контроллеру шаблоны отображения (Views).

Однако сначала обновимся до второго релиз-кандидата Kohana3. Не обновляем только наш контроллер и вручную (либо с помощью системы управления версиями) перезаписываем все, что нужно, в application/bootstrap.php, потому как некоторые приятные изменения там произошли. После обновления все должно заработать как раньше.

Итак, прикрутим к нашему котроллеру какой-нибудь View. Например, к нашей супер-авто-навигации. Создадим папку application/views/elements/, куда и закинем файл navigation.php. Шаблон будет очень простой (я не дизайнер):

<ul id="site_nav">
<li style="display: inline; font-weight: bold;">Navigation: </li>
<?php
if (is_array($items) AND ! empty($items))
{
    foreach ($items as $item)
    {
?>
    <li style="display: inline;"><?php echo $item; ?></li>
<?php
    }
} else {
    echo 'No navigation presented';
}
?>
</ul>

Сейчас нужно подключить его в контроллере, попутно передав нужные данные. Думаю, что логичнее всего будет сделать это в нашем методе Controller_Welcome::_simple_nav() отвечающем за формирование навигационных элементов. Да, предварительно надо переделать метод так, чтобы он формировал массив навигационных элементов. Это просто: вместо конкатенации строк будет добавление элемента массива

$slugs[] = HTML::anchor($slug, url::title($slug));

Ну а дальше классически присоединим шаблон, передав в него данные

$view = new View('elements/navigation');
$view->items = $slugs;

Кстати, можно присоединить шаблон более элегантно, с использованием «фабричной» генерации объекта и метода View::set('template_var', $controller_var), позволяющего присвоить переменной шаблона $template_var значение переменной $controller_var:

$view = View::factory('elements/navigation')
            ->set('items',$slugs);

Все работает!

Есть способ несколько упростить формирование отдаваемой страницы, избавившись от рутинной необходимости явно цеплять основной шаблон и выводить его в $this->request->response. Для этого достаточно унаследовать наш контроллер не от стандартного класса Controller, а от не менее стандартного Controller_Template:

class Controller_Welcome extends Controller_Template {

Главное, не забыть, что по умолчанию для основного шаблона ищется и используется views/template.php. Если у вас он другой, нужно это явно декларировать, например, так

public $template = 'custom_template';

Создадим простой базовый шаблон custom_template.php и поместим его в views/, сверстав как-то так:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.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"/>
<meta name="keywords" content="" />
<meta name="description" content="" />
<title><?php if ( ! empty($title)) echo $title; ?></title>
</head>
<body>
<div class="container">
    <div id="navigation">
        <?php echo $navigation; ?>
    </div>
    <div id="content">
        <?php echo $content; ?>
    </div>
</div>
</body>
</html>

Теперь из методов контроллера заполним его данными. Вот, например, как я это сделал в методе Controller_Welcome::action_index():

public function action_index()
{
    $this->template->title = 'Main page';
    $this->template->content = 'hello, world!!';
    $this->template->navigation = $this->_simple_nav();
}

Заметьте: уже нет необходимости явно присваивать свойству $this->request->response сгенерированный вид. Присвоение сейчас осуществляется в унаследованном методе Controller_Welcome::after().

Давайте добавим еще немного удобства в рутинную процедуру заполнения шаблонов данными. В этом нам поможет метод View::bind(), позволяющий на самой ранней стадии «привязать» к переменной шаблона какую-либо переменную контроллера и потом уже не следить за тем, где ее инициализировать. Чтобы наглядно показать простоту такого способа работы, создадим новый вид для отображения области контента: application/views/elements/content.php

<p>
<?php echo $p1; ?>
</p>
<p>
<?php echo $p2; ?>
</p>

Назначив его, мы должны заполнить $p1 и $p2, однако не можем сделать это раньше, чем эти  переменные определены:

 

$this->template->content = View::factory('elements/content')
                                        ->set('p1',$par1)
                                        ->set('p2',$par2);
                                       
$par1 = 'news 1';
$par2 = 'news 2';

выдаст ошибку.

Но здесь можно поступить так:

$this->template->content = View::factory('elements/content')
                                        ->bind('p1',$par1)
                                        ->bind('p2',$par2);
                                       
$par1 = 'news 1';
$par2 = 'news 2';

Нетрудно убедиться на практике, что сейчас все проходит как надо. В чем преимущество View::bind()? На практике в контроллере зачастую приходится переопределять значения переменных, выводимых в шаблон. Данный метод позволяет не отслеживать все такие переопределения, назначив «ответственные» переменные на ранней стадии обработки и делая дальше все, что душа пожелает.

Удачи!

ЗЫ. Кому нужно, можно скачать архив папки application/

Комментариев: 6 RSS

1 Sezarin 12-08-2009 12:57

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


И, если да, то как при этом будет осуществляться вызов соответствующих контролеров.

2 Александр Купреев 12-08-2009 17:36

Конечно, возможна

Об этом будут следующие главы smile

3 Броткин Иван 19-08-2009 13:43

По поводу bind() - очень удобно при цикличном выводе блоков (комментарии к примеру):

$subview = View::factory('comment')->bind('comment', $comment);
foreach($comments as $comment) 
  echo $subview;

Вроде так smile

4 Александр Купреев 19-08-2009 14:47

Спасибо, Иван, очень классная иллюстрация удобства View::bind()! Действительно, в этом случае отпадает необходимость в цикле переназначать $subview, достаточно это сделать однажды перед циклом.

5 Ахмадишин Ренат 07-09-2009 00:37

Было просто великолепно узнать как все же вставить свои виджеты.

6 Александр Купреев 07-09-2009 10:16

хорошо, подождите несколько дней -- напишу продолжение

Оставьте комментарий!


Используйте нормальные имена.

     

  

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

MaxSiteAuth. Войти через loginza

(обязательно)