{{TOC}} Большинство вёб-приложений используют стандартный ((док3:views шаблон)) дизайна для большинства или даже для всех своих страниц. Бо(')льшую часть времени их авторы просто пишут %%View::make()%%, а затем привязывают нужные данные к этому шаблону: %% return View::make('layouts.default')->nest('content', 'application.home', array('data' => $data)); %% Если вы поступаете так же, то учитывайте, что такой подход не слишком гибок. Если у вас множество мест в коде, где вы используете основной шаблон ("layout"), то вам придётся потратить много времени на их изменение. Здесь на сцену выходит Laravel - посмотрим же на него в деле. И, пожалуйста, придержите аплодисменты до конца лекции... == ((#контроллеры)) Шаблоны контроллеров == По умолчанию класс %%Home_Controller%% наследует от %%Base_Controller%%, который, в свою очередь, наследует от %%Controller%%. Посмотрим на конструктор последнего класса: %% // Файл: laravel/routing/controller.php public function __construct() { // Если контроллер содержит указание на шаблон, использующийся при // отрисовке шаблонов мы создадим класс этого шаблона и присвоим // его свойству $this->layout, заменив строковое имя. if (!is_null($this->layout)) { $this->layout = $this->layout(); } } %% Как вы видите, если класс вашего ((док3:controllers контроллера)) содержит указание на имя ((док3:views шаблона)), Laravel создаст его объект. Чем это полезно для нас? Давайте посмотрим на определение %%Base_Controller%%: %% // Файл: application/controllers/base.php // метод, срабатывающий, если контроллер не может обработать текущий запрос. class Base_Controller extends Controller { public function __call($method, $parameters) { return Response::error('404'); } } %% Кроме "ловящего всё" ("catch-all") метода в этом классе больше ничего нет. Но это именно то место, где мы можем сделать красивый трюк: мы можем определить свойство %%$layout%% - таким образом, **все контроллеры, наследующие от %%Base_Controller%%, получат его автоматически**: %% public $layout = 'layouts.default'; %% Отлично! Теперь когда наш контроллер будет загружен, шаблон будет создан и мы сможем ((док3:views#привязка привязать)) к нему данные: %% public function action_index() { $this->layout->nest('content', 'application.home', array('data' => $data)); } %% Это выглядит куда красивее. Заметьте, что наш метод ((док3:controllers#действия)) **не возвращает никакого значения** - на самом деле это просто не нужно! Laravel понимает, что мы использовали наш шаблон и всё остальное сделает за нас. Конечно, если мы хотим переадресовать пользователя мы всё равно можем это сделать с помощью **return**: %% return Redirect::home(); %% ##(alert) **Внимание:** запомните, что для добавления ((док3:routing#фильтр+ы))ов к контроллеру вам нужно зарегистрировать их в конструкторе. Кроме этого вам нужно не забыть вызвать родительский конструктор. Например, ваш %%%% может быть таким: %% // Файл: application/controllers/home.php class Home_Controller extends Base_Controller { public function __construct() { parent::__construct(); // $this->layout теперь определён. $this->filter('before', 'auth')->only('logout'); } } %% ## Насколько просто теперь обращаться с ((док3:views шаблонами)) в ((док3:controllers контроллерах))! Перейдём к ((док3:routing маршрутам)). == ((#маршруты)) Шаблоны маршрутов == К сожалению, в ((док3:routing маршрутах)) шаблоны не так хороши, как в ((#контроллер+ы))ах - но не пугайтесь, мы решим и эту задачу. Есть два способа: //((#именные регистрация именного шаблона))// и //((#фильтры использование фильтров))//. === Именные шаблы === Регистрация именного ((док3:views шаблона)) делается с помощью %%View::name()%%: %% // Файл: application/start.php или application/routes.php View::name('layouts.default', 'layout'); %% Не имеет особого значения, куда именно вы поместите этот вызов, хотя я сам обычно использую **start.php**. Теперь, имея //именной шаблон//, мы можем создать его экземпляр в начале **applications/routes.php**: %% $layout = View::of('layout'); %% Теперь при форматировании этого шаблона нам нужно использовать переменную %%$layout%% - в //анонимных функциях// ("closure") ((док3:routing маршрутов)) это делается через **((http://php.net/manual/ru/functions.anonymous.php#example-162 use))**: %% // Файл: application/routes.php Route::get('/', function () use ($layout) { return $layout->nest('content', 'application.home', array('data' => $data)); }); %% Недостаток этого подхода в том, что нужно писать %%use ($layout)%% в методе каждого маршрута, который использует этот шаблон. === Фильтры === Второй вариант решения задачи использования общего шаблона в ((док3:routing маршрутах)) был подсказан мне ((https://twitter.com/PhillSparks Филом Спарксом)) в одной из тем на форуме //Laravel.com//. Этот подход немного более элегантный и требует меньшего вмешательства в код //маршрутов//. Вам нужно определить //((док3:routing#фильтр+ы))// **after** в файле **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; } }); %% Теперь ваши //маршруты// должны использовать этот фильтр: %% // Файл: application/routes.php Route::get('/', array('after' => 'layout:html', function () { return View::make('application.home', array('data' => $data)); }); %% Вы можете задаваться вопросом, чем же этот подход лучше использования ((#именные именных шаблонов)) - ведь нам всё равно нужно привязывать к //маршрутам// ((док3:routing#фильтр+ы)). Хорошая новость - в Laravel вы можете //группировать маршруты// с одинаковыми свойствами. Например: %% // Файл: 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** (тип) - параметр, передаваемый в //фильтр//. Это сделано из-за того, что по умолчанию он установлен в %%'html'%%, но вы можете сделать это для нужного маршрута следующим образом: %% // Файл: application/routes.php Route::get('api/posts.json', array('after' => 'layout:json', function () { // получить из БД сообщения, которые нужно вернуть и вернём их в виде массива: return $posts; }); %% Я использую фильтр **layout**, которому передаётся тип %%'json'%% (!!(tl_note) после двоеточия в имени фильтра - //прим. пер.//!!), а сам //маршрут// возвращает обычный массив сообщений. Теперь мы изменим ((#фильтры наш фильтр)): %% // Файл: 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; } }); %% Замечательно! Теперь мы возвращаем верный %%(t)Content-Type%% при JSON-запросах. Итак, как вы видите, в зависимости от вашей задачи любое из ((#маршруты вышеперечисленных решений)) может быть подходящим. == Заключение == Лично я предпочитаю использовать //((док3:controllers контроллеры))//, когда дело идёт о работе с //((док3:views шаблонами))// просто потому, что это намного проще. Поймите меня правильно - //((док3:routing маршруты))// по прежнему отлично применимы и я пользуюсь их гибкостью в сочетании с //контроллерами// для создания мощных приложений.