Pull to refresh

Простейший блог на Symfony 2

Symfony *
Привет, %username%!
symfony — популярный PHP-фреймворк, на котором создано не мало серьезных проектов. Но в русскоязычном сообществе он распространен гораздо меньше. То ли это связано с более высоким порогом входа, то ли с отсутствием русскоязычной документации.
В этом статье я хочу рассказать о создании простейшего блога на symfony2.

Шаг 0. Требования к ПО

Веб-сервер (например, Apache) с 5.3.2 или выше.
Сервер базы данных. Мы будем использовать MySQL.
Git (опционально).
Шаг 1. Установка.

Есть два пути: скачать архив с сайта или использовать git. Мы пойдем по второму, т.к. многие бандлы обновляются вслед за репозитарием.
В корне веб-сервера выполняем команды:
git clone https://github.com/symfony/symfony-standard.git symfony
cd symfony
php bin/vendors.php

Переходим по ссылке http://localhost/symfony/web/config.php. Тут symfony напишет требования и рекомендации по настройке веб-сервера.
В моем случае нужно только указать директиву date.timezone в php.ini.
Там же, внизу страницы написано расположение вашего php.ini.
После того как вы исправите все проблемы, вас ждет самое интересное.
Перейдите по ссылке, с той же страницы, http://localhost/symfony/web/app_dev.php/_configurator/.
Сдесь можно настроить соединение с базой данных и выставить секретный ключ для предотвращение CSRF-аттак.
После этого symfony будет настроена, ну или предложит сама ввести полученую конфигурацию, если файл app/config/parameters.ini не доступен для записи.
Тут стоит упоминуть о бандлах. В данном случае мы будем использовать AcmeDemoBundle — уже созданный бандл, для создания демо-приложений. Углубляться в бандлы я не буду, скажу лишь, что в symfony2 из них, как из кирпичиков, строится ваше приложение.
Так же нам понадобится еще один бандл — FOSUserBundle. Он предоставляет все необходимое для работы с пользователями — вход, регистрация, подтверждение пароля и другое.
Скачиваем:
git submodule add -f git://github.com/FriendsOfSymfony/UserBundle.git vendor/bundles/FOS/UserBundle

Добавляем новое пространство имен(namespace) в app/autoload.php:
$loader->registerNamespaces(array(<br>    'FOS' => __DIR__.'/../vendor/bundles',<br>    // your other namespaces<br>);<br>

Добавляем новый бандл в наше приложение в файле app/AppKernel.php:
public function registerBundles()<br>{<br>    return array(<br>        // ...<br>        new FOS\UserBundle\FOSUserBundle(),<br>        // ...<br>    );<br>}<br>

В app/config/security.yml заменяем параметр providers:
security:<br>    providers:<br>        fos_userbundle:<br>            id: fos_user.user_manager<br>

А так же изменяем firewalls и access_control чтобы разрешить анонимный доступ для всех страниц, кроме страницы создания сообщения.
firewalls:<br>    main:<br>        pattern:     .*<br>        form_login:<br>            provider:      fos_userbundle<br>            login_path:     /login<br>            use_forward:    false<br>            check_path:     /login_check<br>            failure_path:  null<br>        logout:      true<br>        anonymous:    true<br>access_control:<br>    - { path: /post/new, role: ROLE_USER } <br>    - { path: /.*, roles: IS_AUTHENTICATED_ANONYMOUSLY}<br>

В app/config/config.yml удаляем auto_mapping: true и добавляем следующее:
doctrine:<br>    orm:<br>        mappings:<br>            FOSUserBundle: ~<br>            AcmeDemoBundle:  ~<br>            # your other bundles<br>

и в конце файла дописываем:
<br>fos_user:<br>    db_driver: orm<br>    firewall_name: main<br>    class:<br>        model:<br>            user: Acme\DemoBundle\Entity\User<br>

Так же нужно добавить в framework translator, чтобы включить перевод строк. В частности строк FOSUserBundle.
framework:<br>    translator: { fallback: en }<br>

Шаг 2. Создание моделей.

Создадим файлы в директории src/Acme/DemoBundle/Entity. Если такой папки нет — создайте её.
Post.php:
<?php<br><br>namespace Acme\DemoBundle\Entity;<br><br>use Doctrine\ORM\Mapping as ORM;<br><br>/**<br> *<br> * @ORM\Entity<br> */<br>class Post<br>{<br>    /**<br>     * @var integer $id<br>     *<br>     * @ORM\Id<br>     * @ORM\Column(name="id", type="integer")<br>     * @ORM\GeneratedValue(strategy="AUTO")<br>     */<br>    private $id;<br><br>    /**<br>     * @ORM\ManyToOne(targetEntity="User", inversedBy="posts")<br>     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")<br>     */<br>    public $user;<br><br>    /**<br>     * @var string $title<br>     *<br>     * @ORM\Column(name="title", type="string", length=255)<br>     */<br>    public $title;<br><br>    /**<br>     * @var text $description<br>     *<br>     * @ORM\Column(name="description", type="text")<br>     */<br>    public $description;<br>}<br>

Комментарии перед переменными — аннотации. В их использовании не должно возникнуть сложностей, если, конечно, не задаваться вопросом «как оно работает?».
User.php:
<?php<br>namespace Acme\DemoBundle\Entity;<br>use FOS\UserBundle\Entity\User as BaseUser;<br>use Doctrine\ORM\Mapping as ORM;<br><br>/**<br> * @ORM\Entity<br> * @ORM\Table(name="fos_user")<br> */<br>class User extends BaseUser<br>{<br>    /**<br>     * @ORM\Id<br>     * @ORM\Column(type="integer")<br>     * @ORM\generatedValue(strategy="AUTO")<br>     */<br>    protected $id;<br>    <br>    /**<br>     * @ORM\OneToMany(targetEntity="Post", mappedBy="fos_user")<br>     */<br>    public $posts;<br><br>    public function __construct()<br>    {<br>        parent::__construct();<br>        // your own logic<br>    }<br>}<br>

Мы наследуемся от FOS\UserBundle\Entity\User, так что кроме двух заданных нами будут сгенерированы еще несколько полей, включая username, password и email.
После написания моделей мы можем смело создать таблицы:
php app/console doctrine:schema:create

Шаг 3. Создание контроллера.

Создадим файл PostController.php в папке src/Acme/DemoBundle/Controller/:
<?php<br>namespace Acme\DemoBundle\Controller;<br><br>use Symfony\Bundle\FrameworkBundle\Controller\Controller;<br>use Acme\DemoBundle\Entity\Post;<br>use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;<br><br>class PostController extends Controller<br>{<br>    /**<br>     * @Template()<br>     */<br>    public function indexAction()<br>    {<br>        $em = $this->get('doctrine')->getEntityManager();<br>        $posts = $em->getRepository('AcmeDemoBundle:Post')->findAll();<br>        return array('posts' => $posts);<br>    }<br>    <br>    /**<br>     * @Template()<br>     */<br>    public function newAction()<br>    {<br>        $post = new Post();<br>        // Получаем пользователя и присваиваем новому посту<br>        $post->user = $this->get('security.context')->getToken()->getUser();<br>        // Создаем форму, по хорошему следовало бы создать отдельный класс, но можно и так<br>        $form = $this->get('form.factory')<br>            ->createBuilder('form', $post)<br>            ->add('title', 'text')<br>            ->add('description', 'textarea')<br>            ->getForm();<br>        $request = $this->get('request');<br>        if ($request->getMethod() == 'POST') {<br>            $form->bindRequest($request);<br>            if ($form->isValid()) {<br>                $em = $this->get('doctrine')->getEntityManager();<br>                $em->persist($post);<br>                $em->flush();<br>                return $this->redirect($this->generateUrl('post_index'));<br>            }<br>        }<br>        return array('form' => $form->createView());<br>    }<br>}<br>

Зачем нужен Template() перед каждым экшеном? Без него вместо
return array('posts' => $posts);

нам бы пришлось писать
return $this->render('AcmeDemoBundle:Post:index.html.twig', array('posts' => $posts));

Шаг 4. Шаблоны.

В src/Acme/DemoBundle/Resources/views/Post создадим вид для каждого экшена.
index.html.twig:
{% extends 'AcmeDemoBundle::layout.html.twig' %}<br><br>{% block content %}<br><br>{% if is_granted('ROLE_USER') %}<br>    <div><a href="{{ path("post_new") }}">New Post</a></div><br>{% endif %}<br><br>{% for post in posts %}<br>      <h2>{{ post.title}}</h2><br>      <p>{{ post.description }}</p><br>      <div>by {{ post.user.username }}</div><br>{% else %}<br>    Пока в блоге нет статей.<br>{% endfor %}<br><br>{% endblock %}<br>

new.html.twig:
{% extends 'AcmeDemoBundle::layout.html.twig' %}<br><br>{% block content %}<br>    <h1>New Post</h1><br>    <br>    <form action="{{ path("post_new") }}" method="post"><br>        {{ form_widget(form) }}<br><br>        <input type="submit" class="symfony-button-grey" value="Create" /><br>    </form><br>{% endblock %}<br>

О шаблонизаторе Twig уже частично упоминалось на хабре, но есть и отличная официальная документация, правда на английском языке.
Шаг 5. Роуты.

В app/config/routing.yml прописываем наши роуты и не забываем про UserBundle
post_index:<br>    pattern: /post<br>    defaults: { _controller: AcmeDemoBundle:Post:index }<br><br>post_new:<br>    pattern: /post/new<br>    defaults: { _controller: AcmeDemoBundle:Post:new }<br><br>fos_user_security:<br>    resource: "@FOSUserBundle/Resources/config/routing/security.xml"<br><br>fos_user_user:<br>    resource: "@FOSUserBundle/Resources/config/routing/user.xml"<br>    prefix: /user<br><br>* This source code was highlighted with Source Code Highlighter.

Теперь наш блог будет доступен по ссылке http://localhost/symfony/web/app_dev.php/post, а при попытке зайти на http://localhost/symfony/web/app_dev.php/post/new вы будете перенаправлены на страницу входа.
Послесловие

Я не претендую на полное руководство и оригинальность, это скорее заметка по быстрому старту приложения для себя.
Буду рад если кому то пригодится, так же не откажусь от помощи и критики.
У кого будут вопросы — задавайте, думаю на хабре есть люди которые занимаются этим фреймворком.
Кроме того у нас есть jabber-конференция symfony@conference.jabber.ru
Tags:
Hubs:
Total votes 55: ↑48 and ↓7 +41
Views 50K
Comments 102
Comments Comments 102

Posts