Русская документация Symfony2 на SymfonyGuru

Дата последнего обновления: 2012-05-20.

Контроллер

Всё ещё с нами после первых двух частей? Вы становитесь ярым приверженцем Symfony2! Давайте, без лишней суеты, узнаем что контроллеры могут сделать для вас.

Использование форматов

В наши дни, web приложение должно уметь выдавать не только HTML страницы. Начиная с XML для RSS каналов и Web служб, заканчивая JSON для Ajax запросов, существует множество различных форматов. Поддержка этих форматов в Symfony2 проста. Измените routing.yml, добавив _format со значением xml:

  • YAML
    # src/Application/HelloBundle/Resources/config/routing.yml
    hello:
        pattern:  /hello/{name}
        defaults: { _controller: HelloBundle:Hello:index, _format: xml }
    
  • XML
    <!-- src/Application/HelloBundle/Resources/config/routing.xml -->
    <route id="hello" pattern="/hello/{name}">
        <default key="_controller">HelloBundle:Hello:index</default>
        <default key="_format">xml</default>
    </route>
    
  • PHP
    // src/Application/HelloBundle/Resources/config/routing.php
    $collection->add('hello', new Route('/hello/{name}', array(
        '_controller' => 'HelloBundle:Hello:index',
        '_format'     => 'xml',
    )));
    

Затем, наряду с index.twig.html добавьте шаблон index.twig.xml:

<!-- src/Application/HelloBundle/Resources/views/Hello/index.twig.xml -->
<hello>
    <name>{{ name }}</name>
</hello>

И наконец, т.к. шаблон должен быть выбран в соответствии с форматом, внесите следующие изменения в контроллер:

// src/Application/HelloBundle/Controller/HelloController.php
public function indexAction($name, $_format)
{
    return $this->render(
        'HelloBundle:Hello:index.twig.'.$_format,
        array('name' => $name)
    );
}

Вот и всё что для этого нужно. Нет нужды изменять контроллер. Для стандартных форматов Symfony2 автоматически подбирает заголовок Content-Type для ответа. Если хотите поддержку форматов лишь для одного действия, тогда используйте заполнитель {_format} в паттерне:

  • YAML
    # src/Application/HelloBundle/Resources/config/routing.yml
    hello:
        pattern:      /hello/{name}.{_format}
        defaults:     { _controller: HelloBundle:Hello:index, _format: html }
        requirements: { _format: (html|xml|json) }
    
  • XML
    <!-- src/Application/HelloBundle/Resources/config/routing.xml -->
    <route id="hello" pattern="/hello/{name}.{_format}">
        <default key="_controller">HelloBundle:Hello:index</default>
        <default key="_format">html</default>
        <requirement key="_format">(html|xml|json)</requirement>
    </route>
    
  • PHP
    // src/Application/HelloBundle/Resources/config/routing.php
    $collection->add('hello', new Route('/hello/{name}.{_format}', array(
        '_controller' => 'HelloBundle:Hello:index',
        '_format'     => 'html',
    ), array(
        '_format' => '(html|xml|json)',
    )));
    

Таким образом контроллер будет вызыван для следующих URL:: /hello/Fabien.xml или /hello/Fabien.json

Запись requirements устанавилвает регулярные выражения, которым должны соотвествовать заполнители. Если в этом примере запросить ресурс /hello/Fabien.js вы получите ошибку 404 HTTP, потому что он не удовлетворяет тербованию для _format.

Объект Response

Теперь, давайте вернёмся к контроллеру Hello:

// src/Application/HelloBundle/Controller/HelloController.php

public function indexAction($name)
{
    return $this->render('HelloBundle:Hello:index.twig.html', array('name' => $name));
}

Метод render() заполняет шаблон и возвращает объект Response. Ответ может быть оптимизирован, перед тем как отправится в браузер, допустим, чтобы изменить Content-Type:

public function indexAction($name)
{
    $response = $this->render('HelloBundle:Hello:index.twig.html', array('name' => $name));
    $response->headers->set('Content-Type', 'text/plain');

    return $response;
}

Для простейших шаблонов, вы даже можете создать объект Response вручную и сэкономить этим несколько миллисекунд:

public function indexAction($name)
{
    return new Response('Hello '.$name);
}

Это действительно полезно, когда контроллер должен отправить JSON ответ на Ajax запрос.

Управление ошибками

Когда что-нибудь не найдено, вы должны вести честную игру с протоколом HTTP и вернуть ответ 404. Это легко сделать выдав встроенное исключение для HTTP:

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

public function indexAction()
{
    $product = // retrieve the object from database
    if (!$product) {
        throw new NotFoundHttpException('The product does not exist.');
    }

    return $this->render(...);
}

NotFoundHttpException вернёт в браузер ответ 404 HTTP.

Перемещения и перенаправления

Если вы хотите переместить пользователя на другую страницу, используйте метод redirect():

return $this->redirect($this->generateUrl('hello', array('name' => 'Lucas')));

generateUrl() такой же метод как и generate(), который мы применяли ранее в хелпере router. Он получает имя маршрута и массив параметров как аргументы и возвращает ассоциированный дружественный URL.

Также вы можете легко переместить одно действие на другое с помощью метода forward(). Как и для хелпера actions, он применяет внутренний подзапрос, но возвращает объект Response, что позволяет в дальнейшем его изменить:

$response = $this->forward('HelloBundle:Hello:fancy', array('name' => $name, 'color' => 'green'));

// do something with the response or return it directly

Объект Request

Помимо значений заполнителей для маршрутизации, контроллер имеет доступ к объекту Request:

$request = $this->get('request');

$request->isXmlHttpRequest(); // is it an Ajax request?

$request->getPreferredLanguage(array('en', 'fr'));

$request->query->get('page'); // get a $_GET parameter

$request->request->get('page'); // get a $_POST parameter

В шаблоне получить доступ к объекту Request можно через хелпер app.request:

.. code-block:: html+php

{{ app.request.query.get(‘page’) }}

{{ app.request.parameter(‘page’) }}

Сессия

Протокол HTTP не имеет состояний, но Symfony2 предоставляет удобный объект сиссии, который представляет клиента (будь он человеком, использующим браузер, ботом или web службой). Между двумя запросами Symfony2 хранит атрибуты в cookie, используя родные сессии из PHP.

Сохранение и получение информации из сессии легко выполняется из любого контроллера:

$session = $this->get('request')->getSession();

// store an attribute for reuse during a later user request
$session->set('foo', 'bar');

// in another controller for another request
$foo = $session->get('foo');

// set the user locale
$session->setLocale('fr');

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

// store a message for the very next request (in a controller)
$session->setFlash('notice', 'Congratulations, your action succeeded!');

// display the message back in the next request (in a template)
{{ app.session.flash('notice') }}

Заключительное слово

Вот и всё что хотелось рассказать, и я даже уверен, что мы не использовали все отведённые 10 минут. Мы коротко рассмотрели бандлы в первой части, и все особенности о которых мы узнали являются частью бандлов ядра фреймворка. Но благодаря бандлам, в Symfony2 всё может быть расширено или заменено. Это и есть тема следующей части руководства.