Пользователь
0,0
рейтинг
2 августа 2011 в 18:58

Разработка → Знакомство с Symfony 2 из песочницы

Выход Symfony 2 показался мне достаточно веской причиной чтобы наконец-то найти время и посмотреть что же из себя представляет один из самых популярных PHP-фреймворков в мире. Своё знакомство с ним я и описал в данной заметке. Так как это мой первый опыт работы с Symfony буду рад если заметите какие-то ляпы или недочёты и сообщите мне о них.

1. Установка и настройка


Скачать дистрибутив можно с официального сайта. Думаю лучше скачать версию-standart которая идёт со всеми дополнительными компонентами. Далее я предпологаю что архив был распакован в корневую папку вашего веб-сервера.
Проверить что все корректно работает можно по этой ссылке: http://localhost/Symfony/web/config.php.
Если все нормально, то можно начинать, но для начала нужно закомментировать одну строку в файле Symfony/web/app_dev.php

$kernel->loadClassCache();

Очень странное решение, по умолчанию включать кеш в режиме разработки.

2. Создание нового приложения



Приложения в Symfony 2 имеют модульную структуру, модули называются бандлами (bundle). Наше demo-приложение будет находиться в своём отдельно бандле. В Symfony существует очень много консольных команд, которые помогут вам в работе. Есть соответсвующая команда для генерации структуры нового бандла:

php app/console generate:bundle --namespace=Demos/BlogBundle

Эту команду нужно выполнить из консоли, находясь в папке Symfony/. Если вы работаете в линуксе, то можете присвоить файлу console права на выполнения и не писать перед ним php. Генератор задаст несколько простых вопросов на каждый из которых у него есть ответ по умолчанию. Так как мы создаём своё первое приложение, то согласимся на все варинты. Итак, нажав несколько раз клавишу enter мы получим скелет нашего бандла в папке Symfony/src/Demos/BlogBundle.

3. Контроллер и основы роутинга


Как и большинство современных фреймворков, Symfony придерживается паттерна MVC. Мы начнём своё знакомство с реализацией этого паттерна в Symfony с последней буквы, т.е. контроллера.
Наш маленький бандл уже имеет один контроллер, он называется DefaultController и находися в папке src/Demos/BlogBundle/Controller. Если вы заглянете внутрь него, то увидите что у него реализован экшен indexAction. Сейчас мы увидим что он делает. Откройте в своём браузере адрес http://localhost/Symfony/web/app_dev.php/hello/username.
Вы можете удивиться почему именно такой адрес у этой страницы, но если внимательно посмотрите на PHPDoc (это такие специальные комментарии, которые начинаются с символов /**) перед функцией indexAction, то наверняка всё поймёте. Аннотация Route указывает по какому адресу доступен данный экшен и какой параметр он имеет.
Вообще, можно добиться того же и без аннотаций, но они применяются в Symfony повсеместно, так что лучше сразу к ним привыкать. С одной стороны они более лаконичны, а с другой для них не очень хорошо поддерживается автокомплит в моей любимой IDE.
За настройку того как будут выглядеть урлы вашего приложения отвечает специальный компонент Routing, он очень гибко позволяет всё настроить. Более подробно можно почитать в соответсвующем разделе документации. Мы же кратко рассмотрим почему наше приложение открывается именно по этому адресу и почему от нас не потребовалось для этого ничего настраивать.
Если вы не очень внимательно читали что у вас спрашивал генератор бандла, то вполне могли пропустить его вопрос о том обновить ли настройки роутинга в соответствии с новым бандлом. Если пропустили, то можете открыть файл Symfony\app\config\routing.yml. Там вы увидите такую запись:

DemosBlogBundle:
resource: "@DemosBlogBundle/Controller/"
type: annotation
prefix: /

Первая строка — это название секции с конфигурацией, оно соответсвует имени нашего бандла. Вторая строка — указывает откуда импортировать настройки. В данном случае будут читать все файлы из папки с контроллерами, а следующая строка говорит что все необходимые настройки описаны в аннотациях экшенов. Ну а аннотации мы уже видели. И, наконец, последняя строка указывает какой префикс будет добавлен ко всем урлам из нашего бандла. По умолчанию все урлы доступны напрямую от имени входного скрипта. Давайте поменяем / на /blog чтобы было более логично.
Теперь все что мы будем делать будет доступно по адресу http://localhost/Symfony/web/app_dev.php/blog.

4. Работа с Doctrine: создание модели


Теперь перейдем к следующему шагу — работе с базой данных. Я буду использовать Doctrine, хотя никто не обязывает этого делать, вместо доктрины можете использовать любой удобный вам способ работы с бд. Однако, доктрина хорошо интегрирована с symfony и работать с ней одно удовольствие.
Для начала настроим подключеник к бд. Откройте файл app/config/parameters.ini и опишите свои настройки. Кстати, интересно, почему все настройки по дефолту в yml, а этот в ini-файле. Если в настройках вы указали не сущесвующую базу данных, то выполните следующую команду:
php app/console doctrine:database:create
и доктрина сама создаст её.
Доктрина является полноценной ORM, но построна она по другому принципу нежели ActiveRecord, с которым у многих сейчас однозначно ассоциируется это понятие. Доктрина реализует шаблон Data Mapper который очень подрбно описан здесь.
Для начала нам нужно описать сущность с которой мы будем работать. Это будет класс Post с нужными нам свойствами. Создайте папку src/Demos/BlogBundle/Entity. В ней создайте файл Post.php и заполните его следующеим кодом:

<?php
namespace Demos\BlogBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
* @ORM\Table(name="post")
*/
class Post {

/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;

/**
* @ORM\Column(type="string", length=255)
*/
protected $title;

/**
* @ORM\Column(type="text")
*/
protected $body;

/**
* @ORM\Column(type="datetime")
*/
protected $created_date;

/**
* @ORM\Column(type="datetime")
*/
protected $updated_date;
}


Сам класс никак не связан с базой данных, но в аннотациях содержится описание какие из свойств класса будут колонками таблицы и какого типа они будут. Думаю из аннотаций всё вполне очевидно. Все свойства объявлены защищёнными, поэтому для доступа к ним нужно создать геттеры и сеттеры. Для это используем соответсвующую команду доктрины:

php app/console doctrine:generate:entities Demos/BlogBundle/Entity/Post

Теперь посмотрите в файл Post.php, в нём должны были появиться соответсвующие методы. У нас есть есть описание модели, по ней нужно создать соотвествующую таблицу в базе данных. Для этого выполните:

php app/console doctrine:schema:update --force

Если все прошло без ошибок, то в вашей бд должна было появиться таблица post. Перейдём к непосредсвенной работе с объектами. В классе DefaultController создайте новый экшен с таким кодом:

/**
* @Route("/create")
*/
public function createAction() {
$post = new Post();
$post->setTitle('Demo Blog');
$post->setBody('Hello Symfony 2');
$post->setCreatedDate(new \DateTime("now"));
$post->setUpdatedDate(new \DateTime('now'));

$em = $this->getDoctrine()->getEntityManager();
$em->persist($post);
$em->flush();

return new Response('Created product id ' . $post->getId());
}


А в начало файла нужно добавить импорт нужных пространств имён:

use Demos\BlogBundle\Entity\Post;
use Symfony\Component\HttpFoundation\Response;


Теперь если вы откроете адрес http://localhost/Symfony/web/app_dev.php/blog/create, то в ответ должны получить id созданной записи.
Теперь создадим экшен для вывода сущесвующих записей. Для этого нам понадобится экшен с параметром, который будет принимать id записи.

<?php
/**
     * @Route("/show/{id}")
     */
    public function showAction($id)
    {
        $post = $this->getDoctrine()->getRepository('DemosBlogBundle:Post')->find($id);

        if (!$post) {
            throw $this->createNotFoundException('Страница не найдена!');
        }

        $html = <<<HTML
        <h1>{$post->getTitle()}</h1>

        <p>{$post->getBody()}</p>

        <hr/>
        <small>Запись создана {$post->getCreatedDate()->format("Y-m-d H:i:s")}</small>
HTML;

        return new Response($html);
    }
?>


Теперь можете просмотреть запись пройдя по ссылке http://localhost/Symfony/web/app_dev.php/blog/show/1. На этом с доктриной закончим, подробности в документации.

5. Twig: первые шаблоны


Пора перейти к заключительной части нашего знакомства с Symfony. На прошлом шаге мы поступили не очень хорошо смешав логику работы с выводом записи в showAction. Избавиться от этого недостатка нам поможет View (по русски «вид» звучит как-то не очень хорошо, поэтому я буду называть это вьюхой ;).
В качестве шаблонизатора Symfony использует Twig — пожалуй лучший PHP-шаблонизатор с которым я работал. Как и любой другой компонент симфони можете заменить его на то, что вам больше нравится.
Как вы помните у экшена indexAction есть специальная аннотация Template(), которая говорит что у него есть шаблон.

return array('name' => $name);

Массив который возвращается из экшена передаётся во вьюху, ключи массива буду именами переменных которые будут там доступны.
Давайте изменим экшен show. Нужно добавить соответсвующую аннотацию и вместо того чтобы возвращать объект Response вернуть массив в котором будет просматриваемая запись. Вот что получилось:

/**
* @Route("/show/{id}")
* @Template()
*/
public function showAction($id)
{
$post = $this->getDoctrine()->getRepository('DemosBlogBundle:Post')->find($id);

if (!$post) {
throw $this->createNotFoundException('Страница не найдена!');
}

return array('post' => $post);
}


Согласитесь, так гораздо лучше. Теперь в папке src/Demos/BlogBundle/Resources/views/Default создайте файл show.html.twig в который перенесите html код который был у нас экшене. Синтаксис твига отличается от php поэтому придется кое-что изменить. Конечный вариант смотрите в исходниках. Узнать больше о синтаксисе твига можно в его документации.

6. Заключение


Пожалуй на этом пока остановлюсь. Надеюсь, ещё кому-то кроме меня это будет интересно. В начале с Symfony было немного сложно разобраться, но всё равно интересно. Все исходники доступны на гитхабе.
@ekaragodin
карма
8,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (27)

  • +4
    День symfony 2 на хабре.
    • НЛО прилетело и опубликовало эту надпись здесь
    • +2
      Дай бог не последняя.
  • +2
    Не так давно тоже заинтересовался Symfony2, бегло пробежался по документации и все-равно полез гуглить «symfony2 как начать». Как сегодня уже писали: «очень высокий уровень вхождения». Годная статья получилась. Спасибо!
  • +2
    Жаль, что не затронули валидацию и работу с формами (включая интеграцию валидации, форм и доктрины) — с ними преимущества симфони2 куда заметнее. Да, и зря не упомянули, что Doctrine2 реализует не только (и, пожалуй, не столько) DataMapper, но и UnitOfWork и Reposotory. Прямая работа с мэппингом обычно заканчивается объявлением связей сущностей с БД, ни чтения, ни записи напрямую не вызывается — получаем данные из репозитория, а о их персистентности заботится EntityManager, у которого под капотом UnitOfWork. Кроме описания связи мы по сути вообще не знаем где у нас хранятся сущности.
    • 0
      Со всем этим за один вечер не разберешься, как только появится еще свободное время обязательно продолжу, так все что вы перечислили мне тоже интересно узнать.
      • 0
        Извините, не понял, что за один вечер.
        • 0
          Имеется ввиду — нужно время
          • 0
            Имел в виду, что я не понял что в статье описаны результаты одного вечера :)
    • 0
      интересно сделать динамическую валидацию, мы пока не смогли сделать ее. ограничения кода валидации…
      • 0
        В смысле динамическую? Я пока не разобрался с проблемой уникальных значений в БД при параллельных запросах.
        • 0
          например, есть некий конструктор админки, как у нас :), основанный на yml файлах. т.е. типовой функционал можно определить через 1 файл yml + бандл (контроллер наследуется от контроллера-конструктора(хоть в symfony не советуют так делать :) ))
          так вот, отображение списка, динамических полей, форм добавления и редактирования — все задается в одном файле. и валидация. т.е. это то к чему мы стремимся. сейчас же для форм приходится вручную поля прописывать из-за того что валидатор создает объект для заполнения полей, а потом проверяет сам класс на наличие свойств через property_exist, в итоге динамическая валидация не получается
  • 0
    если честно в обзоре кроме аннотаций, нету никаких особенностей, а их в сф2 значительно больше
    • 0
      Это и не был обзор особенностей симфони, а просто история про то как как начать работать симфони.
  • 0
    Не надо все делать аннотациями. Все делается и через yaml.
    Entity тоже вручную не надо создавать.
    Лучше сформировать из sql yml файлы mapping'a, а потом сгенерировать Entity…
    Entity можно самим править, при перегенерации ваш код генератор не трогает.
    • 0
      да, про аннотации я писал что их можно заменить на более удобный способ описания.
      мне интереснее генерировать sql из yml)), а вообще с генератороми еще толком не разбирался — как генерить миграции, фикстуры и т.д. а вот что генератор не затирает то, что ты руками написал, это конечно удобная крутотень.
    • 0
      А мне аннотации для многого показались удобными. Для маппинга точно. Для роутинга — для простых приложений/бандлов/контроллеров должны быть удобнее, для сложных, наверное, всё же YAML (более читабельно в больших количествах). А ещё можно на PHP и XML — даже не знаю, что хуже :)

      А вот насчёт SQL -> код или код -> SQL холиварить можно долго. Я за второй вариант :)
      • 0
        — для оптимизации базы
        — при множестве таблиц, не завязанных каждая на свой бандл, а более обширной системы… лучше с sql — в yml, имхо. мы так уже месяца 4 работаем. практика показала свои плюсы.
        — к тому же, аннотации менялись за это время, а yml нет :)
        • 0
          — Под оптимизацией вы имеете в виду использование особенностей конкретного движка РСУБД? Не мой случай. Я думаю как скрыть особенности SQL и NoSQL хранилищ.
          — Ну, если система на PHP создаётся как ещё один фронтенд к существующей базе, то альтернатив просто нет (хотя можно ручками писать :) ), но вот если два бандла работают с одной таблицей, то, по-моему, лучше импортировать модель из одного в другой, чем её дублировать — или не вижу каких-то подводных камней?
          — хороший аргумент :)
          • 0
            база да, существующая. пример сложности в бд — в одной таблице одного типа товаров — более 5 миллионов… а есть еще другие нюансы…
            импортировать модель… мы все в один сложили. очень удобно
  • 0
    Насчиет пункта 1
    symfony.com/doc/2.0/cookbook/debugging.html
    все-таки dev используется не только для разработки но и отладки приложений(время выполнения, запросы. ну вы и так знаете), по-этому, я думаю, $kernel->loadClassCache(); по дефолту и включен, чтобы не обманывать статистику генерации.
  • 0
    Жаль, что не затронуты вопросы деплоя…
    • 0
      А что не так с деплоем? Можно обычным php-way слить каталог с приложением и ручками схему БД поправить, можно миграции заюзать, можно capistrano настроить, чтоб он всё сам делал.
      • 0
        Ну, вот например.

        Во время разработки я решаю, что надо посмотреть, как выглядит сайт «вне девелоперского окружения» и пытаюсь открыть не localhost/Symfony/web/app_dev.php/blog, но localhost/Symfony/web/app.php/blog

        /*Конкретного ругательства Symfony я уже не помню, но*/ фреймворк ругался, что не может что-то найти.

        Хотелось бы, чтоб это и было освещено. На сайте документации этого я не нашел; как и в этой статье, которая очень походит на перевод первой части книги, представленной на сайте symfony 2.
        • 0
          /*Конкретного ругательства Symfony я уже не помню, но*/ фреймворк ругался, что не может что-то найти.

          Ну я выкладывал на удаленный сервер в продакшен окружении типа такого блога — проблем не встретил. Что в голову сразу приходит — соединение с БД настроили, схему создали?
  • 0
    Можно добавить ещё, что тем, кто хочет начать работать с Symfony2 более легко, не обязательно использовать сразу весь фреймворк.
    Можно использовать компоненты, как отдельные самодостаточные части.
    Либо есть какой-то старый проект, который не использует фреймворка.
    Сразу весь проект переделывать с нуля бывает нецелесообразно, но в нём можно
    начать использовать какие-то компоненты.
    К примеру, для работы с формами и т.д.
  • 0
    Если в config.yml не прописать

    mappings: ... DemosBlogBundle: ~

    то команда
    php app/console doctrine:generate:entities Demos/BlogBundle/Entity/Post
    не сработает.
    Поправьте пожалуйста.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.