Development

Documentation/ru_RU/book/1.0/02-Exploring-Symfony-s-Code

You must first sign up to be able to contribute.

Version 6 (modified by postman, 10 years ago)
--

Глава 2 - Исследуем код symfony

Перевод второй главы "Exploring Symfony's Code" онлайн книги "Definitive guide to Symfony".

В главе присутствует разнобой в терминах — одни переведены на русский, а другие так и остались на английском. Я не переводил термин, если не удавалось найти общепринятого перевода, или же перевод казался слишком громоздким. Если я использовал английский термин, то приводил его объяснение.

Алексей Гоголев

На первый взгляд приложение cделанное на symfony может выглядеть отпугивающим. Много директорий, скриптов, классов, HTML-файлов, или даже смеси PHP с HTML. В коде используются какие-то классы, которых не содержит папка с приложением, а дерево директорий жутко глубокое, порой доходит до шести уровней. Но как только вы поймете причины этой кажущейся сложности, все вышеописанное покажется вам таким естественным, что вы не обменяйте структуру symfony ни на какую другую. Эта глава призвана убрать возможно возникшее у вас чувство испуга.

MVC Pattern

Symfony основан на классическом шаблоне проектирования MVC (Model-View-Controller). Архитектура MVC состоит из трех уровней: * Model — представляет данные, которыми оперирует приложение * View — отвечает за представление (отображение) информации * Controller — логика приложения; соответственно действиям пользователя, оперирует Model и View

NOTE Читать о MVC pattern на wikipedia.org

Схема 2-1 иллюстрирует работу приложения, построенного на идее MVC.

Благодаря разделению на три составляющие, приложение приобретает структурированность. Код становится более понятным, его легче изменять. Разработка становится гибкой. Например, если разрабатываемое приложение должно работать не только на обычных браузерах, но и на портативных компьютерах, вам нужно создать еще один view (для отображения данных на портативных компьютерах). Логика приложения (controller) и работа с данными (model) будут одинаковыми для обоих задач.

Controller прячет от model и view детали протокола используемого для запроса (HTTP, консольный режим, почта, и пр.). Model скрывает в себе операции с данными, что позволяет сделать view и controller независимыми от типа базы данных, которую использует приложение.

Схема 2-1 иллюстрирует работу приложения, построенного на идее MVC

Схема работы приложения, построенного на идее MVC

Части MVC

Разберемся в достоинствах шаблона проектирования MVC на примере. Сделаем из обычного PHP приложения приложение с MVC архитектурой. Для этого отлично подойдет список сообщений (постов) для блога.

Список Сообщений без Архитектуры MVC

Вот как примерно выглядит PHP файл (листинг 2-1), отвечающий за вывод списка сообщений, если не использовать шаблон MVC.

Листинг 2-1 - Вывод списка сообщений без использования MVC архитектуры

[php]
<?php

// Подключение, и выбор базы данных
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);

// выполнение SQL запроса
$result = mysql_query('SELECT date, title FROM post', $link);

?>

<html>
  <head>
    <title>List of Posts</title>
  </head>
  <body>
   <h1>List of Posts</h1>
   <table>
     <tr><th>Date</th><th>Title</th></tr>
<?php
// Вывод результата 
while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
{
echo "t<tr>n";
printf("tt<td> %s </td>n", $row['date']);
printf("tt<td> %s </td>n", $row['title']);
echo "t</tr>n";
}
?>
    </table>
  </body>
</html>

<?php

// Закрытие соединения
mysql_close($link);

?>

Этот код быстро пишется и быстро выполняется. Но у этого подхода есть недостатки:

  • Отсутствует обработка ошибок (что если не выйдет соединится (connect) с базой?)
  • В коде смешаны HTML и PHP
  • Код привязан к базе MySQL (для другой базы он не будет работать)

Отделяем представление данных от логики

Echo и printf (листинг 2-1) затрудняют чтение кода. Разумно будет разделить код на две части. Первая часть, чистый (без HTML) PHP код содержащий логику и работу с данными, приведен в листинге 2-2.

Листинг 2-2 - Controller содержится в index.php

[php]
<?php

 // Соединение и выбор базы данных
 $link = mysql_connect('localhost', 'myuser', 'mypassword');
 mysql_select_db('blog_db', $link);

 // Выполнение SQL запроса
 $result = mysql_query('SELECT date, title FROM post', $link);

 // Заполнение массива для view
 $posts = array();
 while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
 {
$posts[] = $row;
 }

 // завершение соединения
 mysql_close($link);

 // вызов view
 require('view.php');
?>

HTML и PHP код предназначенные для вывода данных, содержатся во втором скрипте (листинг 2-3), и в нашем случае являются частью view, MVC архитектуры.

Листинг 2-3 - Часть view в файле view.php

[php]
<html>
  <head>
    <title>List of Posts</title>
  </head>
  <body>
    <h1>List of Posts</h1>
    <table>
      <tr><th>Date</th><th>Title</th></tr>
    <?php foreach ($posts as $post): ?>
      <tr>
        <td><?php echo $post['date'] ?></td>
        <td><?php echo $post['title'] ?></td>
      </tr>
    <?php endforeach; ?>
    </table>
  </body>
</html>

Хорошим тоном считается минимум PHP кода во view. Таким образом, код view будет понятен даже без знания PHP, что удобно для верстальщиков и дизайнеров. Также недопустим вывод HTML кода через PHP (например echo ‘< p > это плохо< /p >’). Наиболее частые выражения в шаблонах это echo, if/endif и foreach/endforeach.

Всю логику мы перенесли в первый скрипт (листинг 2-2), он содержит только чистый PHP код (никакого HTML!). Отметим, что теперь вы можете использовать этот же скрипт для совершенно другого представления данных. Например, через PDF файл или XML.

Отделяем работу с данными

Большая часть скрипта, содержащего controller, представляет собой работу с данными. Но что, если список сообщений понадобится для другой цели, например для RSS? Если вы захотите держать всю работу с данными в одном месте, и избежать повторений своего же кода? Если надо будет поменять название таблицы с “post” на “weblog_post”? Если неожиданно понадобится перейти с MySQL на PostgreSQL? Для того, чтоб все эти проблемы решались легко, надо ввести третью составляющую — model. Вынесем всю работу с данными из controller, и поместим ее в model (см. листинг 2-4).

Листинг 2-4 - Model содержится в model.php

[php]
<?php

function getAllPosts()
{
  // Соединение с базой данных
  $link = mysql_connect('localhost', 'myuser', 'mypassword');
  mysql_select_db('blog_db', $link);

  // Выполнение SQL запроса
  $result = mysql_query('SELECT date, title FROM post', $link);

  // Заполнение массива 
  $posts = array();
  while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
  {
     $posts[] = $row;
  }

  // Прерывание соединения с базой данных
  mysql_close($link);

  return $posts;
}

?>

Так как работа с данными была отделена, controller тоже нужно изменить. Исправленный controller представлен в листинге 2-5.

Листинг 2-5 - Controller после корректировки (вынос работы с данными), в файле index.php

[php]
<?php

// вызов model
require_once('model.php');

// получаем список сообщений (постов)
$posts = getAllPosts();

// вызов view
require('view.php');

?>

Код стал еще проще. Теперь, единственное что делает controller — получает данные от model и передает их во view. В более сложных приложениях, controller также работает с запросом (request), сессиями пользователей (user session), аутентификацией (authentication) и т. д. Название функции getAllPosts() (getAllPosts — ''англ.'' получить все сообщения) говорит само за себя и разработчику даже не нужно писать комментарии к коду.

Model полностью посвящен доступу к данным и организован следующим образом: все параметры, которые не зависят от базы данных (например параметры запроса (request parameters)) должны быть переданы controller-ом, и недоступны напрямую из model. Тогда функции model могут быть легко использованы повторно, для других задач.

Дробление частей MVC

Идея MVC архитектуры — разделить код на три части, согласно их природе. Работа с данными происходит в model, view выводит результат, и логика приложения содержится в controller.

Другие шаблоны проектирования могут сделать разработку еще проще. Тройка model, view, controller может дробиться и разделяться на части.

Database Abstraction

Model можно разделить на две части — data access layer и database abstraction layer.

Приведу здесь объяснения терминов:

data access layer — (англ.) слой доступа к данным
database abstraction layer — (англ.) абстракция базы данных, т.е. часть, благодаря которой, мы абстрагируемся от типа базы данных

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

переводчик

Функции доступа к данным (data access functions) не используют прямые запросы к базе данных, а вызывают другие функции, которые делают эту работу. Таким образом, если понадобится сменить тип базы данных по ходу разработки, нужно будет переделать только database abstraction layer.

Примеры database abstraction layer и data access layer приведены в листингах 2-6 и 2-7 соответственно.

Листинг 2-6 - Database Abstraction Layer

[php]
<?php

function open_connection($host, $user, $password)
{
  return mysql_connect($host, $user, $password);
}

function close_connection($link)
{
  mysql_close($link);
}

function query_database($query, $database, $link)
{
  mysql_select_db($database, $link);

  return mysql_query($query, $link);
}

function fetch_results($result)
{
  return mysql_fetch_array($result, MYSQL_ASSOC);
}

Листинг 2-7 - Data Access Layer

[php]
<?php
function getAllPosts()
{
  // Подключение к базе данных
  $link = open_connection('localhost', 'myuser', 'mypassword');

  // Выполнение SQL запроса
  $result = query_database('SELECT date, title FROM post', 'blog_db', $link);

  // заполнение массива
  $posts = array();
  while ($row = fetch_results($result))
  {
     $posts[] = $row;
  }

  // Завершение соединения с базой данных
  close_connection($link);

  return $posts;
}

?>

Вы можете убедиться что в data access layer отсутствуют функции, зависящие от типа базы данных. Также, функции из database abstraction layer могут быть повторно использованы во многих других функциях model, которым нужен доступ к базе.

NOTE Код в листингах 2-6 и 2-7 все равно не удовлетворителен. Можно добиться большей независимости от типа базы данных (убрать SQL запросы, используя независящий от типа базы данных генератор запросов, и реализовать все функции в рамках класса). Но цель этой книги не научить тому, как писать такой код. В Главе 8 вы увидите, что symfony справляется с этими задачами самостоятельно.

Элементы View

View также пойдет на пользу разделение на части. Любая веб страничка обычно содержит элементы, неизменные для всего приложения: заголовки страницы (page headers), графическая разметка страницы (graphical layout), нижний колонтитул (footer), элементы навигации. Только внутренняя часть страницы меняется, при переходе по ссылкам. Вот почему view разделен на главный шаблон (layout) и шаблон (template). Главный шаблон (layout) обычно является общим для всего приложения или для группы страничек. Шаблон (template) оформляет полученные от controller переменные. Логика, которая требуется для того чтоб эти компоненты работали вместе, также является частью view. Согласно этим принципам, view из листинга 2-3 может быть разделен на три части, представленные в листингах 2-8, 2-9, 2-10.

Листинг 2-8 - Шаблон (Template) в файле mytemplate.php

[php]
<h1>List of Posts</h1>
<table>
<tr><th>Date</th><th>Title</th></tr>
<?php foreach ($posts as $post): ?>
  <tr>
    <td><?php echo $post['date'] ?></td>
    <td><?php echo $post['title'] ?></td>
  </tr>
<?php endforeach; ?>
</table>

Листинг 2-9 – Логика view

[php]
<?php

$title = 'List of Posts';
$content = include('mytemplate.php');

?>

Листинг 2-10 – Главный шаблон (Layout)

[php]
<html>
  <head>
    <title><?php echo $title ?></title>
  </head>
  <body>
    <?php echo $content ?>
  </body>
</html>

Действие (Action) и Фронт-Контроллер(Front Controller)

В приведенном примере controller содержит мало действий, но в реальных веб приложениях на него возлагается очень много работы. Наиболее важные задачи, типичные для любого controller: обработка запроса (request handling), обеспечение безопасности (security handling), загрузка конфигурации приложения и прочая рутина. Вот почему controller обычно разделен на front controller, единственный для всего приложения, и действия (actions), которые содержат только индивидуальную для каждой страницы логику (по одному действию на одну страницу).

Одно из замечательных достоинств front controller — он предлагает единственную точку входа в приложение. Если вы решите закрыть доступ к приложению, понадобится изменить только скрипт front controller. В приложении с отключенным front controller, никакое из действий (action) не сработает.

Объектная Ориентированность

Все приведенные примеры используют процедурное программирование. Мощности современных объектно-ориентированных языков программирования могут сделать разработку еще легче благодаря инкапсуляции, наследованию, и прозрачным названиям классов, полей и методов.

Построение MVC архитектуры на базе процедурного языка вынуждает использовать пространства имен (namespace) и повторять свой код несколько раз. Как следствие, код становится менее понятным.

Объектная ориентированность позволяет разработчику пользоваться объектом view, объектом controller, классами model, а также оформить все функции как методы (функции из нашего примера тоже должны быть преобразованы в методы). Все это необходимо для MVC архитектуры.

TIP Если вы хотите узнать больше о шаблонах проектирования для веб приложений в контексте обьектно-ориентированного программирования, почитайте книгу “Patterns of Enterprise Application Architecture”, автор — Martin Fowler (Addison-Wesley, ISBN: 0-32112-742-0). Несмотря на то что в книге приводятся примеры кода на Java и C#, PHP разработчку тоже будет интересно.

Реализация MVC архитектуры в Symfony

Остановимся на минутку. Сколько компонентов требуется для одного только списка сообщений в блоге? Как показано в схеме 2-2, мы используем следующие части:

  • Model
    • Database abstraction
    • Data access
  • View
    • Логика view
    • Шаблон (template)
    • Главный шаблон (layout)
  • Controller
    • Front controller
    • Действие (action)

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

Прежде всего, front controller и главный шаблон (layout) — общие для всего приложения. В вашем приложении может быть много font controller-ов и главых шаблонов (layout), но как минимум необходимо иметь один front controller и один главный шаблон (layout). Front controller это логическая часть MVC, и вам никогда не потребуется писать его, потому что symfony сгенерирует его самостоятельно.

Еще одна хорошая новость — классы model также генерируются автоматически, основываясь на заданной вами структуре данных. Эту работу делает библиотека Propel, она обеспечивает базовую структуру данных и генерирование кода. Если в таблице есть foreign key или поле содержащее дату, Propel обеспечит специальные методы, которые значительно упростят работу с такими структурами данных. Database abstraction layer совершенно не волнует разработчика, потому что основан на компоненте Creole. В любой момент вы можете сменить базу данных и вам не потребуется писать ни одной строчки кода. Все что нужно это поменять один параметр в настройках.

И последнее — логика view заменена несложным конфигурационным файлом без единой строчки кода.

Схема 2-2 - Схема работы symfony

Схема работы symfony

Итого, в symfony для списка сообщений блога требуется только три файла. Они показаны в листингах 2-11, 2-12, 2-13.

Листинг 2-11 - Действие (action), myproject/apps/myapp/modules/weblog/actions/actions.class.php

[php]
<?php
class weblogActions extends sfActions
{
  public function executeList()
  {
    $this->posts = PostPeer::doSelect(new Criteria());
  }
}

?>

Листинг 2-12 - Шаблон (template), myproject/apps/myapp/modules/weblog/templates/listSuccess.php

[php]
<h1>List of Posts</h1>
<table>
<tr><th>Date</th><th>Title</th></tr>
<?php foreach ($posts as $post): ?>
  <tr>
    <td><?php echo $post->getDate() ?></td>
    <td><?php echo $post->getTitle() ?></td>
  </tr>
<?php endforeach; ?>
</table>

Листинг 2-13 - конфигурационный файл view, myproject/apps/myapp/modules/weblog/config/view.yml

[php]
listSuccess:
  metas: { title: List of Posts }

Также нужно создать главный шаблон (layout), он приведен в листинге 2-14 и будет использован в приложении не только для списка сообщений, но и для других задач.

Листинг 2-14 - Главный шаблон (layout), in myproject/apps/myapp/templates/layout.php

[php]
<html>
  <head>
    <?php echo include_title() ?>
  </head>
  <body>
    <?php echo $sf_data->getRaw('sf_content') ?>
  </body>
</html>

Это все что требуется. Этот код реализует точно такую же страничку, что и код из листинга 2-1. Все остальное (слаженная работа всех компонентов вместе) обеспечивает symfony. Если подсчитать строчки кода, станет ясно, что решение задачи (создание списка сообщений для блога) в рамках MVC архитектуры symfony забирает не больше времени чем написание эквивалентного кода в одном файле (листинг 2-1). Однако, это дает огромные преимущества: четкая и структурированная организация кода, возможность повторного использования кода, гибкость, и гораздо больше удовольствия от работы. Как бонус, у вас есть панель отладки (debug toolbar), XHTML-совместимая верстка, возможность легко менять настройки, независимость от базы данных, красивые URL, несколько режимов (environment) и много других инструментов разработки.

Классы Ядра Symfony

Для построения MVC архитектуры в symfony задействованы несколько классов, которые часто будут встречаться в этой книге:

  • sfController — класс посвященный controller. Расшифровывает запрос (request) и передает его действию (action).
  • sfRequest — хранит все элементы запроса (параметры, куки (cookies), заголовки пакетов (headers), и пр.).
  • sfResponse — содержит контент (contents) и заголовки пакетов ответа (response headers). Этот объект в итоге преобразовывается в HTML ответ и высылается пользователю.
  • [http://developer.co.ua/posts/view/singleton_pattern_v_php Singleton класс]) содержит ссылки на все объекты ядра и текущие настройки, доступен всюду.

Вы узнаете больше об этих объектах в Главе 6.

Как вы видите, все классы symfony используют префикс “sf”, также как и переменные ядра symfony в шаблонах. Это поможет избежать конфликтов с именами ваших собственных классов и переменных, и сделает ядро фреймворка более доступным и легкоузнаваемым.

NOTE В стандартах кодирования, используемых в symfony, ТакойВерхнийРегистр используется для имен классов и переменных. Но есть два исключения: классы ядра начинающейся на “sf” (нижний регистр), и переменные встречающейся в шаблонах используют подчеркивание_для_разделения_слов.

Организация кода

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

Структура Проекта: Приложения, Модули, и Действия

Приведу список терминов:

Project — проект
Application — приложение
Module — модуль
Action — действие


переводчик

В symfony проект это набор служб и операций, доступных под одним доменным именем, которые пользуются одной объектной моделью.

Внутри проекта операции сгруппированы в приложения (application). Приложение обычно может быть запущено независимо от других приложений проекта. В большинстве случаев, проект содержит два приложения: пользовательская часть (front-office) и админпанель (back-office). Обе части используют одну базу данных. Тем не менее, один проект может содержать несколько мини-сайтов, каждый из которых является отдельным приложением. Отметим, что ссылки между приложениями одного проекта должны задаваться в абсолютной форме. В свою очередь приложение состоит из модулей (modules). Модуль обычно обеспечивает работу одной веб странички, или группы страничек одной тематики. Пример названия модулей: home, articles, help, shoppingCart, account и т. д.

Модули содержат действия (action). Действия соответствуют операциям, которые можно выполнить в данном модуле. Например, модуль shoppingCart (shopping cart — ''англ.'' тележка для покупок) может содержать действия add, show, и update (''англ.'' добавить, показать и обновить). Собственно любое действие (action) характеризуется глаголом. Каждому действию соответствует результирующая веб страничка. Но два действия могут выдавать одну и ту же страничку (например, действие добавления комментария будет заново выводить сообщение и все комментарии к нему).

NOTE Если работа только началась и не хочется усложнять структуру проекта, очень удобно поместить все действия в один-единственный модуль. Когда же приложение станет более сложным, можно перенести действия (actions) в другие модули. Как упоминалось в Главе 1, такая реорганизация кода, с целью улучшения структуры и читаемости, называется refactoring. Если следовать принципам RAD, то делать refactoring вы будете часто.

Схема 2-3 показывает как, в терминах проект/приложение/модуль/действие (project/application/module/action), организован код проекта «блог». Но примите во внимание, что это '''не''' файловая структура проекта, а логическая организация кода.

Схема 2-3 - Пример организации кода

Пример организации кода

Файловая Структура

Все веб проекты состоят из следующих элементов:

  • База данных, например MySQL или PostgreSQL
  • Статические файлы (HTML, изображения, [http://ru.wikipedia.org/wiki/Javascript JavaScript] файлы, таблицы стилей, и пр.)
  • Файлы закачанные пользователями сайта или администраторами
  • PHP классы и библиотеки
  • Сторонние библиотеки (скрипты сторонних разработчиков)
  • Batch файлы (скрипты которые могут быть запущены через командную строку или посредством cron таблиц)
  • Журналы событий (или логи; отчеты которые ведутся сервером или приложением)
  • Файлы конфигурации

Для организованного хранения этого содержимого symfony предоставляет стандартную файловую структуру, построенную согласно идее MVC и иерархии проект/приложение/модуль/действие. При создании проекта, приложения, или модуля файловая структура для них строится автоматически. Конечно же, вы можете изменить ее под свои требования или требования заказчика.

Директории Проекта

Ниже приведен список директорий находящихся в корне проекта:

apps/
  frontend/
  backend/
batch/
cache/
config/
data/
  sql/
doc/
lib/
  model/
log/
plugins/
test/
  unit/
  functional/
web/
  css/
  images/
  js/
  uploads/

Таблица 2-1 разъясняет предназначение директорий проекта.

Таблица 2-1 - Директории проекта

Директория | Описание ----------- | --------- apps/ | Содержит по одной директории на каждое приложение (обычно, frontend и backend — пользовательская часть и админпанель соответственно). batch/ | Содержит batch скрипты для вызова из командной строки, или программу планировщик для запуска batch процессов. cache/ | Хранит кэшированные версии настроек, и (если вы активируйте эту возможность) кэшированную версию действий (actions) и шаблонов (templates) проекта. Механизм кэширования (подробно описан в Главе 12) использует эти файлы для того чтоб ускорить ответ на запрос к серверу. Любое приложение будет иметь тут свою поддиректорию содержащую предварительно обработанные PHP и HTML файлы. config/ | Содержит базовую конфигурацию проекта. data/ | Тут можно хранить файлы с данными проекта. Например, схема базы данных, дамп базы, или даже базу SQLite. doc/ | Содержит документацию проекта, вашу документацию и документацию созданную с помощью PHPdoc. lib/ | Директория предназначена для сторонних классов и библиотек. Сюда можно добавить свои библиотеки необходимые для нескольких приложений вашего проекта. Поддиректория model/ содержит объектную модель проекта (подробнее в Главе 8). log/ | Содержит журналы событий (логи), которые создает symfony. Также может содержать логи веб сервера, базы данных, или другие журналы любой части проекта. Symfony создает по одному журналу событий на каждое приложение и на каждый режим (environment). Подробней о журналах событий в Главе 16. plugins/ | Содержит установленные плагины (plug-in). Подробней о плагинах в главе 17. test/ | Содержит юнит тесты и функциональные тесты, написанные на PHP и совместимые с тестирующим фреймворком symfony (подробней в Главе 15). Во время создания проекта, symfony автоматически добавляет заглушки с несколькими базовыми тестами. web/ | На эту директорию должен указывать DocumentRoot веб сервера. Только файлы размещенные в web/ доступны из Интернета.

В описании директории test/ есть термин «заглушка». На английском языке это stub. Stub — некая заготовка, базовая структура, скелет. Например, функция которая ничего не делает это stub.

переводчик

Директории Приложения

Все приложения имеют одинаковую файловую структуру:

apps/
  [application name]/
    config/
    i18n/
    lib/
    modules/
    templates/
      layout.php
      error.php
      error.txt

Таблица 2-2 - описывает предназначение поддиректорий приложения

Директория | Описание ----------- | --------- config/ | Содержит множество конфигурационных YAML файлов. Тут содержатся все настройки приложения, не считая параметров заданных по умолчанию, которые находятся в настойках фреймворка. Отметим, что используя YAML файлы директории config/, можно заменить значения параметров заданных по умолчанию. Вы узнайте больше о настройке приложения в Главе 5. i18n/ | Содержит файлы для локализации (internationalization) приложения — в основном файлы интерфейсного перевода (interface translation files). Эта директория не понадобится, если для локализации вы используйте базу данных. Глава 13 посвящена локализации. lib/ | Здесь хранятся классы и библиотеки необходимые только данному приложению. modules/ | Содержит модули (modules) приложения. templates/ | Содержит главные шаблоны приложения, которые являются общими для всех модулей. По умолчанию в директории находится файл layout.php — основной главный шаблон (layout), в который вставляются шаблоны модулей.

NOTE В свежесозданном приложении директоии i18n/, lib/, и modules/ — пустые.

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

Директории Модуля

Каждое приложение содержит один или несколько модулей. Каждому модулю соответствует папка в директории modules/. Имя этой папки и есть имя модуля.

Файловая структура модуля:

apps/
  [application name]/
    modules/
      [module name]/
          actions/
            actions.class.php
          config/
          lib/
          templates/
            indexSuccess.php
          validate/

Таблица 2-3 описывает директории модуля.

Table 2-3 – Директории модуля

Директория | Описание ----------- | --------- actions/ | Обычно содержит единственный файл action.class.php, в котором хранятся все действия (actions) данного модуля. Можно хранить разные действия в разных файлах. config/ | Содержит конфигурационные файлы модуля. lib/ | Здесь хранятся классы и библиотеки индивидуальные для модуля. templates/ | Содержит шаблоны, каждый из них соответствует какому-то действию (action). При инициализации модуля автоматически создается шаблон по умолчанию (indexSuccess.php). validate/ | В директории содержатся конфигурационные файлы, используемые для валидации форм (подробней в Главе 10).

NOTE В новом модуле директории config/, lib/, и validate/ — пустые.

Директории папки web/

Начинка директории со свободным доступом web/ простая. Несколько соглашений об именах (naming conventions) поддиректорий, обеспечивают поведение по умолчанию и удобные сокращения (shortcut) в шаблонах. Пример содержимого web/:

web/
  css/
  images/
  js/
  uploads/

Статические файлы рассортированы по директориям, которые описаны в Таблице 2-4.

Таблица 2-4 – Типичные поддиректории web/

Директория | Описание ----------- | --------- css/ | Содержит .css таблицы стилей. images/ | Здесь хранятся изображения в форматах .jpg, .png, и .gif. js/ | Директория для .js файлов. uploads/ | Папка предназначена для загружаемых пользователями файлов. Зачастую в директории uploads/ хранятся изображения. Но не стоит путать ее с images/. Благодаря разделению на uploads/ и images/, синхронизация рабочего сервера (development server) и сервера на котором реально стоит проект (production server) не затрагивает изображения загруженные пользователями.

NOTE Настоятельно рекомендуется пользоваться родной файловой структурой, но ее можно изменить под ваши требования, например для запуска проекта на сервере не допускающем такое дерево директорий и организацию кода. Подробней об изменении файловой структуры проекта рассказывается в Главе 19.

Популярные приемы

Несколько приемов используются в symfony очень часто. В этой книге и ваших проектах вы встретите их не раз. Это касается контейнеров параметров (parameter holder) , констант, и автоматического подключения классов (class autoloading).

Контейнеры Параметров (Parameter Holders)

У многих классов symfony есть контейнеры параметров. Это удобный способ инкапсулировать атрибуты, используя для доступа getter и setter методы. Например, у класса sfResponse есть контейнер параметров, который можно получить с помощью метода getParameterHolder(). Работа с контейнером параметров одинакова для любого класса, и выглядит так:

Листинг 2-15 – Использование контейнера параметров класса sfResponse

[php]
<?php

$response->getParameterHolder()->set('foo', 'bar');
echo $response->getParameterHolder()->get('foo');
 => 'bar'

?>

Большинство классов, имеющих контейнер параметров, позволяют использовать методы-посредники (proxy method) для уменьшения количества кода. В укороченном варианте код из Листинга 2-15 будет выглядеть так:

Листинг 2-16 - Использование методов-посредников (proxy methods) для операций с контейнером параметров класса sfResponse

[php]
<?php

$response->setParameter('foo', 'bar');
echo $response->getParameter('foo');
 => 'bar'

?>

Вторым параметром в метод get можно передать значение параметра по умолчанию. Это намного удобнее, чем самостоятельно писать код для присваивания параметру значения по умолчанию, используя if и else. Взгляните на листинг 2-17 для сравнения.

Листинг 2-17 - Присвоение параметру значения по умолчанию

[php]
<?php

// Параметр 'foobar' не определен, соответственно метод getParameter() вернет пустое значение
echo $response->getParameter('foobar');
 => null

// Можно присвоить значение по умолчанию рассмотрев два случая с помощью if и else
if ($response->hasParameter('foobar'))
{
  echo $response->getParameter('foobar');
}
else
{
  echo 'default';
}
 => default

// Но гораздо удобнее передать значение по умолчанию вторым параметром в метод getParameter()
echo $response->getParameter('foobar', 'default');
 => default

?>

Контейнеры параметров поддерживают пространства имен (namespace). Если задать третий параметр для методов getParameter() или setParameter(), он будет использован как пространство имен. Соответственно параметр будет определен только внутри этого пространства. Листинг 2-18 содержит пример.

Листинг 2-18 – Использование пространства имен в контейнере параметров класса sfResponse

[php]
<?php

$response->setParameter('foo', 'bar1');
$response->setParameter('foo', 'bar2', 'my/name/space');
echo $response->getParameter('foo');
 => 'bar1'
echo $response->getParameter('foo', null, 'my/name/space');
 => 'bar2'

?>

Вы также можете определить контейнер параметров в своем собственном классе, и пользоваться его возможностями. Листинг 2-19 показывает как это сделать.

Листинг 2-19 – Добавление контейнера параметров в класс

[php]
<?php

class MyClass
{
  protected $parameter_holder = null;

  public function initialize ($parameters = array())
  {
    $this->parameter_holder = new sfParameterHolder();
    $this->parameter_holder->add($parameters);
  }

  public function getParameterHolder()
  {
    return $this->parameter_holder;
  }
}

?>

Константы

Сюрпризом будет малое количество констант в symfony. Это обусловлено тем, что константы в PHP имеют один большой недостаток — вы задаете их один раз и потом не сможете их изменить. Поэтому symfony использует собственный объект sfConfig для конфигурации (configuration object) заменяющий константы. Он предоставляет статические методы для доступа к параметрам, которые могут быть вызваны откуда угодно. Листинг 2-20 показывает sfConfig в действии.

Листинг 2-20 – Использование методов класса sfConfig вместо констант

[php]
<?php

// Вместо констант PHP,
define('SF_FOO', 'bar');
echo SF_FOO;
// Symfony использует обьект sfConfig 
sfConfig::set('sf_foo', 'bar');
echo sfConfig::get('sf_foo');

?>

В методах класса sfConfig также можно задать значения по умолчанию. С помощью метода sfConfig::set() можно несколько раз переопределить значение одного параметра. В Главе 5 методы класса sfConfig описаны более подробно.

Автоматическое Подключение Классов (Class Autoloading)

Обычно, при использовании метода или создания объекта в PHP, необходимо предварительно подключить описание класса.

[php]
<?php

include 'classes/MyClass.php';
$myObject = new MyClass();

?>

Но в больших проектах со множеством классов и глубокой структурой директорий, подключение классов и написание путей к нужным файлам занимает много времени. Благодаря функции {{{__autoload()}}} (или же функции spl_autoload_register()), symfony делает команду include() ненужной. Можно писать просто:

[php]
<?php

$myObject = new MyClass();

?>

Symfony будет искать класс {{{MyClass}}} во всех .php файлах, которые находятся в директориях lib/. Если класс будет найден, он будет подключен автоматически. Так что если ваши классы хранятся в папках lib/, вам не потребуется использовать команды include или require.

NOTE С целью увеличения быстродействия, механизм автоматического подключения сканирует список директорий (которые определены во внутреннем конфигурационном файле) во время первого запроса (request). Найденные классы сохраняются в .php файле как ассоциативный массив, в виде соответствия класс/файл. Таким образом, во время следующих запросов не потребуется искать классы заново. Вот почему необходимо очищать кэш (cache) проекта командой symfony clear-cache, если вы добавили или переместили файл содержащий класс.

Вы узнаете больше о кэше (cache) в Главе 12, и о настойках автоматического подключения в Главе 19.

Итого

Использование MVC фреймворка подразумевает разделение и организацию кода согласно правилам фреймворка. Код отвечающий за презентацию (отображение информации) содержится во view, операции с данными в model, и логика приложения в controller. Это делает применение MVC шаблона одновременно полезным и ограничивающим.

Symfony это MVC фреймворк написанный на PHP. Его структура вбирает лучшее от идеи MVC, в то же время сохраняя простоту в использовании. Благодаря гибкости и способности к изменению конфигурации (configurability), symfony подходит для всех веб проектов.

Теперь, когда идеи на которых построен symfony стали понятны, вы почти готовы к разработке вашего первого приложения. Но прежде необходимо установить symfony.

Перевел Алексей Гоголев postman [at] dev [dot] co [dot] ua

Статья также доступна по адресу http://developer.co.ua/posts/view/glava_2_issleduem_kod_symfony