<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>hudson@work &#187; best practices</title>
	<atom:link href="http://hudson.su/tag/best-practices/feed/" rel="self" type="application/rss+xml" />
	<link>http://hudson.su</link>
	<description>статьи о web-разработке, менеджменте IT проектов и контроле качества</description>
	<lastBuildDate>Fri, 20 Jan 2012 13:15:39 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>Symfony 2: пакеты, практические рекомендации</title>
		<link>http://hudson.su/2011/02/15/symfony-2-bundles-best-practices/</link>
		<comments>http://hudson.su/2011/02/15/symfony-2-bundles-best-practices/#comments</comments>
		<pubDate>Tue, 15 Feb 2011 07:58:29 +0000</pubDate>
		<dc:creator>hudson</dc:creator>
				<category><![CDATA[Профессиональное]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[bundles]]></category>
		<category><![CDATA[symfony]]></category>
		<category><![CDATA[Symfony2]]></category>
		<category><![CDATA[web разработка]]></category>

		<guid isPermaLink="false">http://hudson.su/?p=1747</guid>
		<description><![CDATA[Пакет &#8211; прежде всего это директория, которая имеет строго определенную структуру и может содержать все что угодно (от классов, до контроллеров и web-ресурсов). Не смотря на то что пакеты очень гибки, вы должны следовать некоторым рекомендациям (best practices), если вы хотите выложить его в общий доступ. Об этих практиках мы и поговорим ниже: Наименование пакета [...]]]></description>
			<content:encoded><![CDATA[<p>Пакет &#8211; прежде всего это директория, которая имеет строго определенную структуру и может содержать все что угодно (от классов, до контроллеров и web-ресурсов). Не смотря на то что пакеты очень гибки, вы должны следовать некоторым рекомендациям (best practices), если вы хотите выложить его в общий доступ. Об этих практиках мы и поговорим ниже:</p>
<p><span id="more-1747"></span></p>
<div>
<h2>Наименование пакета<a title="Permalink to this headline" href="http://docs.symfony-reloaded.org/guides/bundles/best_practices.html#bundle-name"></a></h2>
<p>Пакет &#8211; это, помимо всего прочего, также пространство имен. Пространство имен должно соответствовать <a href="http://groups.google.com/group/php-standards/web/psr-0-final-proposal" target="_blank">стандартам совместимости</a> PHP 5.3 для пространств имен и наименований классов: они должны начинаться с сегмента &#8220;поставщика&#8221; (вендора), содержать несколько сегментов категорий (в том числе может не содержать ни одного) и оканчиваться коротким наименованием пространства имен, которое должно, в свою очередь, завершаться суффиксом <tt>Bundle</tt>.</p>
<p>Пространство имен становится пакетом как только вы добавите в него класс пакета (bundle class). Наименование класса пакета должно соответствовать нескольким нехитрым правилам:</p>
<ul>
<li>Использовать только буквы, цифры и подчерк;</li>
<li>Использовать <em>ВерблюжийРегистр (aka CamelCase)</em> для имени</li>
<li>Использовать описательные и короткие наименования (не менее 2х слов)</li>
<li>Использовать в качестве префикса наименование вендора (и опционально &#8211; категории пространства имен)</li>
<li>Использовать суффикс <tt>Bundle</tt>.</li>
</ul>
<p>В таблице ниже приведены несколько примеров правильных пространств имен и наименований классов для пакетов:</p>
<table style="height: 106px" border="1" width="525">
<col width="54%"></col>
<col width="46%"></col>
<thead>
<tr>
<th>Пространство имен</th>
<th>Наименование класса</th>
</tr>
</thead>
<tbody>
<tr>
<td><tt>SensioBundleBlogBundle</tt></td>
<td><tt>SensioBlogBundle</tt></td>
</tr>
<tr>
<td><tt>SensioBundleSocialBlogBundle</tt></td>
<td><tt>SensioSocialBlogBundle</tt></td>
</tr>
<tr>
<td><tt>SensioBlogBundle</tt></td>
<td><tt>SensioBlogBundle</tt></td>
</tr>
</tbody>
</table>
<p>По договоренности, метод <tt>getName()</tt> класса в пакете должен возвращать имя класса.</p>
</div>
<div>
<h2>Структура директорий</h2>
<p>Базовую структуру директорий пакета <tt>HelloBundle</tt> можно представить следующим образом:</p>
<pre>XXX/...
    HelloBundle/
        HelloBundle.php
        Controller/
        Resources/
            meta/
                LICENSE
            config/
            doc/
                index.rst
            translations/
            views/
            public/
        Tests/</pre>
<p>Структура директорий XXX отражает структуру пространства имен пакета.</p>
<p>Следующие файлы являются обязательными:</p>
<ul>
<li><tt>HelloBundle.php</tt>;</li>
<li><tt>Resources/meta/LICENSE</tt>: текст лицензии на код пакета;</li>
<li><tt>Resources/doc/index.rst</tt>: корневой файл документации пакета.</li>
</ul>
<div>
<p>Замечание: следование этим соглашениям обеспечивает работу средств автоматизации.</p>
</div>
<p>Глубина под-директорий должна быть минимально необходимой для используемых классов и файлов (максимум 2 уровня). Больший уровень вложенности может быть использован для малозначимых или же редкоиспользуемых файлов.</p>
<p>Дирктория пакета предназначается только для чтения. Если вам необходимо писать временные файлы &#8211; вы можете сохранять их в директории <tt>cache/ </tt>или <tt>log/</tt>. Вы можете генерировать файлы в директории пакета при помощи различных инструментов, но только в том случае, если генерированные файлы будут частью репозитория.</p>
<p>Следующие классы и файлы имеют определенное расположение:</p>
<table style="height: 168px" border="1" width="520">
<col width="48%"></col>
<col width="52%"></col>
<thead>
<tr>
<th>Тип</th>
<th>Директория</th>
</tr>
</thead>
<tbody>
<tr>
<td>Контроллеры</td>
<td><tt>Controller/</tt></td>
</tr>
<tr>
<td>Файлы с переводами</td>
<td><tt>Resources/translations/</tt></td>
</tr>
<tr>
<td>Шаблоны</td>
<td><tt>Resources/views/</tt></td>
</tr>
<tr>
<td>Модульные и функциональные тесты</td>
<td><tt>Tests/</tt></td>
</tr>
<tr>
<td>Web-ресурсы (доступные публично)</td>
<td><tt>Resources/public/</tt></td>
</tr>
<tr>
<td>Конфигурация</td>
<td><tt>Resources/config/</tt></td>
</tr>
<tr>
<td>Команды</td>
<td><tt>Command/</tt></td>
</tr>
</tbody>
</table>
</div>
<div>
<h2>Классы</h2>
<p>Структура директорий пакета используется как иерархия пространства имен Например, <tt>HelloController </tt>контроллер находится по пути <tt>Bundle/HelloBundle/Controller/HelloController.php и </tt>полное имя класса будет <tt>BundleHelloBundleControllerHelloController</tt>.</p>
<p>Все классы и файлы должны соответствовать <a href="http://docs.symfony-reloaded.org/contributing/code/standards.html" target="_blank">стандартам кодирования Symfony2</a>.</p>
</div>
<div>
<p>Некоторые классы следует рассматривать как фасады (facades) и стараться делать их как можно более короткими, например Commands, Helpers, Listeners и Controllers.</p>
<p>Классы, которые подключаются к Event dispatcher&#8217;у должны иметь суффикс <tt>Listener</tt>.</p>
<p>Классы исключений должны быть расположены во сложенном пространстве имен <tt>Exception</tt>.</p>
</div>
<div>
<h2>Вендоры<a title="Permalink to this headline" href="http://docs.symfony-reloaded.org/guides/bundles/best_practices.html#vendors"></a></h2>
<p>Пакет не должен включать сторонних PHP-библиотек. Вместо этого они должны располагаться в стандартной директории Symfony2, доступной автозагрузчику. Также пакет не должен включать сторонних библиотек на JavaScript, CSS или на других языках.</p>
</div>
<div>
<h2>Тесты<a title="Permalink to this headline" href="http://docs.symfony-reloaded.org/guides/bundles/best_practices.html#tests"></a></h2>
<p>Пакеты должны комплектоваться набором тестов на PHPUnit и расположенными в директории <tt>Tests/</tt>. Тесты должны следовать следующим принципам:</p>
<ul>
<li>Тестовый набор должен выполняться простой командой <tt>phpunit </tt>из демо-приложения;</li>
<li>Функциональные тесты должны быть использованы только для тестирования ответов (response output) и профильных данных, если таковые имеются;</li>
<li>Покрытие кода должно составлять не менее 95% кода вашего пакета.</li>
</ul>
<div>
<p>Замечание: тестовый набор не должен содержать скрипта <tt>AllTests.php</tt>, но должен полагаться на наличие файла <tt>phpunit.xml.dist</tt>.</p>
</div>
</div>
<div>
<h2>Документация</h2>
<p>Все классы и функции должны быть документированы в стиле PHPDoc.</p>
<p>Полная документация также должна быть представлена в формате <a href="http://docs.symfony-reloaded.org/contributing/documentation/format.html"><em>reStructuredText</em></a>, и располагаться в директории <tt>Resources/doc/</tt>; наличие файла <tt>Resources/doc/index.rst</tt> &#8211; <strong>обязательно</strong>!</p>
</div>
<div>
<h2>Контроллеры<a title="Permalink to this headline" href="http://docs.symfony-reloaded.org/guides/bundles/best_practices.html#controllers"></a></h2>
<p>Контроллеры в пакете не должны наследоваться от класса <tt><a title="SymfonyBundleFrameworkBundleControllerController" href="http://api.symfony-reloaded.org/PR6/Symfony/Bundle/FrameworkBundle/Controller/Controller.html">Controller</a></tt>. Они должны реализовывать интерфейс <tt><a title="SymfonyFoundationDependencyInjectionContainerAwareInterface" href="http://api.symfony-reloaded.org/PR6/Symfony/Foundation/DependencyInjection/ContainerAwareInterface.html">ContainerAwareInterface</a></tt> или наследоваться от класса <tt><a title="SymfonyFoundationDependencyInjectionContainerAware" href="http://api.symfony-reloaded.org/PR6/Symfony/Foundation/DependencyInjection/ContainerAware.html">ContainerAware</a></tt>.</p>
<div>
<p>Замечание: Если вы посмотрите на методы класса <tt><a title="SymfonyBundleFrameworkBundleControllerController" href="http://api.symfony-reloaded.org/PR6/Symfony/Bundle/FrameworkBundle/Controller/Controller.html">Controller</a></tt>, вы заметите что они представляют собой &#8220;ярлычки&#8221; для упрощения изучения.</p>
</div>
</div>
<div>
<h2>Шаблоны</h2>
<p>Если пакет включает в себя шаблоны, они должны использовать Twig. Пакет не должен включать в себя главный layout, за исключением случаев, если он представляет собой полноценное рабочее приложение.</p>
</div>
<div>
<h2>Файлы переводов</h2>
<p>Если пакет содержит переводы сообщений, они должны быть определены в формате XLIFF;</p>
<p>If a bundle provides message translations, they must be defined in the XLIFF format; домен должен располагаться после имени пакета (<tt>bundle.hello</tt>).</p>
<p>Пакет не должен переопределять уже существующие сообщения из других пакетов.</p>
</div>
<div>
<h2>Конфигурация</h2>
<p>Конфигурация пакета должна использовать строенный <a href="http://docs.symfony-reloaded.org/guides/bundles/configuration.html" target="_blank">механизм</a> Symfony2. Пакет должен предоставлять все его базовые настройки в виде XML.</p>
<blockquote><p>Оригинал тут <a href="http://docs.symfony-reloaded.org/guides/bundles/best_practices.html" target="_blank">http://docs.symfony-reloaded.org/guides/bundles/best_practices.html</a></p>
<p>Перевод как обычно авторский )</p></blockquote>
</div>
]]></content:encoded>
			<wfw:commentRss>http://hudson.su/2011/02/15/symfony-2-bundles-best-practices/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>О «symfony best practices»</title>
		<link>http://hudson.su/2010/03/07/about-symfony-best-practices/</link>
		<comments>http://hudson.su/2010/03/07/about-symfony-best-practices/#comments</comments>
		<pubDate>Sun, 07 Mar 2010 04:38:30 +0000</pubDate>
		<dc:creator>hudson</dc:creator>
				<category><![CDATA[Профессиональное]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://hudson.su/?p=1069</guid>
		<description><![CDATA[Кросспост моего хабратопика. Nicolas Perriault представлял на SymfonyDay’09 презентацию &#8220;30 Symfony Best Practices&#8221;. Кое-что из его практик очевидно (было мне известно), кое-что не было. Но есть две практики, которые мне совершенно не видятся как &#8220;best&#8221;. О них я и хочу поговорить. Итак: #4 Никаких экземпляров Criteria (Propel) или Doctrine_Query, SQL запросов и любой ORM/RDBMS специфики [...]]]></description>
			<content:encoded><![CDATA[<p>Кросспост моего <a href="http://hudson.habrahabr.ru/blog/86607/" target="_blank">хабратопика</a>.</p>
<p><strong>Nicolas Perriault</strong> представлял на <strong>SymfonyDay’09</strong> презентацию <a href="http://www.slideshare.net/nperriault/30-symfony-best-practices" target="_blank">&#8220;30 Symfony Best Practices&#8221;</a>. Кое-что из его практик очевидно (было мне известно), кое-что не было. Но есть две практики, которые мне совершенно не видятся как &#8220;best&#8221;. О них я и хочу поговорить.</p>
<p>Итак:</p>
<p><span id="more-1069"></span></p>
<p><em><strong>#4</strong><br />
Никаких экземпляров Criteria (Propel) или Doctrine_Query, SQL запросов и любой ORM/RDBMS специфики не должно быть ни в шаблонах ни в экшенах (actions);<br />
Чем сильнее вы привяжете модель к контроллерам и видам, тем более инертным к изменениям будет ваш бэкэнд.</em></p>
<p>Далее следует несколько примеров:</p>
<pre>// ПЛОХО
public function executeRecentProducts( sfWebRequest $request )
{
    $this-&gt;products = Doctrine::getTable( 'Product' )
        -&gt;createQuery( 'p' )
        -&gt;where( 'p.is_published = 1' )
        -&gt;orderBy( 'published_at DESC' )
        -&gt;limit(10)
        -&gt;execute();
    // Плохо, получаем Doctrine_Collection
}

// ЧУТЬ ЛУЧШЕ
public function executeRecentProducts( sfWebRequest $request )
{
    $this-&gt;products = Doctrine::getTable( 'Product' )
        -&gt;createQuery( 'p' )
        -&gt;where( 'p.is_published = 1' )
        -&gt;orderBy( 'published_at DESC' )
        -&gt;limit(10)
        -&gt;fetchArray();
    // Уже лучше, Array
}

// ХОРОШО
public function executeRecentProducts( sfWebRequest $request )
{
    $this-&gt;products = ProductTable::getRecent( $max = 10, $published = true );
    // Array, идеально ))
}</pre>
<p>Итак, что же предлагает Николас? С тем что нужно избегать Doctrine_Query и т.п. в видах и действиях &#8211; с этим я согласен. Но он также предлагает передавать во view только массивы (см. примеры выше). На словах выглядит неплохо, но зачастую может возникнуть проблема с подтягиванием связанных записей (1:n или n:n). Опять же, подтягивать в виде заранее не выбранные при джойне записи &#8211; это можно сказать &#8220;плохой тон&#8221; разработки. Но с другой стороны это происходит совершенно прозрачно для разработчика (и при необходимости отлавливается через webDebug).</p>
<p>Другой неприятный момент в передаче массивов в вид в том, что какое-то из передаваемых значений может потребовать предварительной обработки перед выводом, что придется выполнять явно в действии или в *Table классе. Если же у нас есть объект, то мы можем вызвать например метод getMyField() вместо getField().</p>
<p><em><strong>#25</strong><br />
Избегайте помещения под версионный контроль любые из генерированных классов вида Base*, в особенности, если вы хотите писать плагины для многократного использования или же хотите перераспределять вашу работу. Почему? Возможно что-то захочет расширить ваши классы модели или же добавить к ним behavior другой.</em></p>
<p>Что меня беспокоит здесь? То, что в репозитории не хранится целостное приложение. Т.е. модель конечно есть, схема есть. Но отсутствуют классы, от которых наследуется ваша модель. Таким образом, осуществляя вынос вашего приложения на production сервер, вы всегда должны помнить, что помимо update&#8217;а кода, выполнения миграции (которая еще не факт что выполнится при отсутствии Base* классов), выполнения дополнительных процедур, присущих конкретному релизу, вы должны будете еще пересобрать и модель. Согласитесь, еще один элемент сакральных знаний, который со временем (особенно на больших проектах) переходит в область чего-то почти магического.</p>
<p>В Doctrine 2.0 обещают уйти от наследования Base* классов, так что, возможно, скоро эта практика утратит актуальность. Но пока мы имеем Doctrine 1.2 ))</p>
<p><strong>Ну что, коллеги, а вы что думаете об этих двух практиках? Как поступаете изо дня в день?</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://hudson.su/2010/03/07/about-symfony-best-practices/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

