Может войдёшь?
Черновики Написать статью Профиль

Шаблоны в контроллерах и маршрутах

перевод

  1. 1. Шаблоны контроллеров
  2. 2. Шаблоны маршрутов
    1. 2.1. Именные шаблы
    2. 2.2. Фильтры
  3. 3. Заключение

Большинство вёб-приложений используют стандартный шаблон дизайна для большинства или даже для всех своих страниц. Бо(')льшую часть времени их авторы просто пишут PHPView::make(), а затем привязывают нужные данные к этому шаблону:

PHP
return View::make('layouts.default')->nest('content''application.home', array('data' => $data));

Если вы поступаете так же, то учитывайте, что такой подход не слишком гибок. Если у вас множество мест в коде, где вы используете основной шаблон («layout»), то вам придётся потратить много времени на их изменение.

Здесь на сцену выходит Laravel — посмотрим же на него в деле. И, пожалуйста, придержите аплодисменты до конца лекции...

Шаблоны контроллеров

По умолчанию класс PHPHome_Controller наследует от PHPBase_Controller, который, в свою очередь, наследует от PHPController. Посмотрим на конструктор последнего класса:

PHP
// Файл: laravel/routing/controller.php

public function __construct()
{
  
// Если контроллер содержит указание на шаблон, использующийся при
  // отрисовке шаблонов мы создадим класс этого шаблона и присвоим
  // его свойству $this->layout, заменив строковое имя.
  
if (!is_null($this->layout)) {
    
$this->layout $this->layout();
  }
}

Как вы видите, если класс вашего контроллера содержит указание на имя шаблона, Laravel создаст его объект. Чем это полезно для нас? Давайте посмотрим на определение PHPBase_Controller:

PHP
// Файл: application/controllers/base.php

// метод, срабатывающий, если контроллер не может обработать текущий запрос.
class Base_Controller extends Controller {
  public function 
__call($method$parameters) {
    return 
Response::error('404');
  }
}

Кроме «ловящего всё» («catch-all») метода в этом классе больше ничего нет. Но это именно то место, где мы можем сделать красивый трюк: мы можем определить свойство PHP$layoutтаким образом, все контроллеры, наследующие от PHPBase_Controller, получат его автоматически:

PHP
public $layout 'layouts.default';

Отлично! Теперь когда наш контроллер будет загружен, шаблон будет создан и мы сможем привязать к нему данные:

PHP
public function action_index() {
  
$this->layout->nest('content''application.home', array('data' => $data));
}

Это выглядит куда красивее. Заметьте, что наш метод действия не возвращает никакого значения — на самом деле это просто не нужно! Laravel понимает, что мы использовали наш шаблон и всё остальное сделает за нас. Конечно, если мы хотим переадресовать пользователя мы всё равно можем это сделать с помощью return:

PHP
return Redirect::home();

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

PHP
// Файл: application/controllers/home.php

class Home_Controller extends Base_Controller {
  public function 
__construct() {
    
parent::__construct();  // $this->layout теперь определён.

    
$this->filter('before''auth')->only('logout');
  }
}

Насколько просто теперь обращаться с шаблонами в контроллерах! Перейдём к маршрутам.

Шаблоны маршрутов

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

Именные шаблы

Регистрация именного шаблона делается с помощью PHPView::name():

PHP
// Файл: application/start.php или application/routes.php

View::name('layouts.default''layout');

Не имеет особого значения, куда именно вы поместите этот вызов, хотя я сам обычно использую start.php.

Теперь, имея именной шаблон, мы можем создать его экземпляр в начале applications/routes.php:

PHP
$layout View::of('layout');

Теперь при форматировании этого шаблона нам нужно использовать переменную PHP$layout — в анонимных функциях («closure») маршрутов это делается через use:

PHP
// Файл: application/routes.php

Route::get('/', function () use ($layout)
{
    return 
$layout->nest('content''application.home', array('data' => $data));
});

Недостаток этого подхода в том, что нужно писать PHPuse ($layout) в методе каждого маршрута, который использует этот шаблон.

Фильтры

Второй вариант решения задачи использования общего шаблона в маршрутах был подсказан мне Филом Спарксом в одной из тем на форуме Laravel.com. Этот подход немного более элегантный и требует меньшего вмешательства в код маршрутов. Вам нужно определить фильтр after в файле application/routes.php:

PHP
Route::filter('layout', function ($response$type 'html') {
  
// переадресации не содержат содержимого, а ошибки должны форматироваться отдельным шаблоном.
  
if ($response->status 300) return;

  switch (
$type) {
    case 
'html':
      
// ответ уже был подготовлен, поэтому нам просто нужно "обернуть" его в наш шаблон:
      
$response->content View::make('layout', array(
        
'content' => $response->content,
      ))->
render();
      break;
  }
});

Теперь ваши маршруты должны использовать этот фильтр:

PHP
// Файл: application/routes.php

Route::get('/', array('after' => 'layout:html', function () {
  return 
View::make('application.home', array('data' => $data));
});

Вы можете задаваться вопросом, чем же этот подход лучше использования именных шаблонов — ведь нам всё равно нужно привязывать к маршрутам фильтр. Хорошая новость — в Laravel вы можете группировать маршруты с одинаковыми свойствами. Например:

PHP
// Файл: application/routes.php

Route::group(array('after' => 'layout'), function () {
  
Route::get('/', function () {
    return 
View::make('application.home', array('data' => $data));
  });

  
Route::get('about', function () {
    return 
View::make('application.about');
  });
});

Выглядит хорошо. Как вы могли заметить, я не определил type (тип) — параметр, передаваемый в фильтр. Это сделано из-за того, что по умолчанию он установлен в PHP'html', но вы можете сделать это для нужного маршрута следующим образом:

PHP
// Файл: application/routes.php

Route::get('api/posts.json', array('after' => 'layout:json', function () {
  
// получить из БД сообщения, которые нужно вернуть и вернём их в виде массива:
  
return $posts;
});

Я использую фильтр layout, которому передаётся тип PHP'json' (после двоеточия в имени фильтра — прим. пер.), а сам маршрут возвращает обычный массив сообщений. Теперь мы изменим наш фильтр:

PHP
// Файл: application/routes.php

Route::filter('layout', function ($response$type 'html') {
  
// переадресации не содержат содержимого, а ошибки должны форматироваться отдельным шаблоном.
  
if ($response->status 300) return;

  switch (
$type) {
    case 
'html':
      
// ответ уже был подготовлен, поэтому нам просто нужно "обернуть" его в наш шаблон:
      
$response->content View::make('layout', array(
          
'content' => $response->content,
      ))->
render();
      break;

    case 
'json':
      
// установим заголовок Content-Type в нужное значение и закодируем ответ:
      
$response->header('content-type'File::mime('json'));
      
$response->content json_encode($response->content);
      break;
  }
});

Замечательно! Теперь мы возвращаем верный Content-Type при JSON-запросах.

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

Заключение

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

Как вы считаете, полезен ли этот материал? Да Нет

Написать комментарий

Разметка: ? ?

Авторизуйся, чтобы прокомментировать.