<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/atom10full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xml:lang="en" xml:base="http://tigor.com.ua/blog/wp-atom.php">
	<title type="text">Журнал веб разработчика</title>
	<subtitle type="text">Блог о разработке, тестировании веб-приложений</subtitle>

	<updated>2008-11-11T10:01:24Z</updated>
	<generator uri="http://wordpress.org/" version="2.6.2">WordPress</generator>

	<link rel="alternate" type="text/html" href="http://tigor.com.ua/blog" />
	<id>http://tigor.com.ua/blog/feed/atom/</id>
	

			<link rel="self" href="http://feeds.feedburner.com/tigor" type="application/atom+xml" /><entry>
		<author>
			<name>ingvar</name>
					</author>
		<title type="html"><![CDATA[Symfony: интернационализация (I18n) / часть 2 – редактирование данных в административной панели]]></title>
		<link rel="alternate" type="text/html" href="http://tigor.com.ua/blog/2008/11/11/symfony-i18n-part-2-edit-data-in-admin-panel/" />
		<id>http://tigor.com.ua/blog/?p=590</id>
		<updated>2008-11-11T07:05:35Z</updated>
		<published>2008-11-10T22:33:28Z</published>
		<category scheme="http://tigor.com.ua/blog" term="Symfony" /><category scheme="http://tigor.com.ua/blog" term="I18n" />		<summary type="html"><![CDATA[Часть 2: интернационализация (I18n) – редактирование данных в административной панели
Продолжение темы мультиязычности в Symfony: первой части, где рассказывались основы работы с интернационализацией в Symfony. Теперь рассмотрим, как данные редактировать в административной панели для нескольких языков.
Исходный код к статье - demo-i18n_part2.zip (source code). Код написан на Symfony 1.2, Propel 1.3.
Рисунок 2.1: Пример Frontend (Example Frontend)


Рисунок 2.2: [...]]]></summary>
		<content type="html" xml:base="http://tigor.com.ua/blog/2008/11/11/symfony-i18n-part-2-edit-data-in-admin-panel/"><![CDATA[<h2>Часть 2: интернационализация (I18n) – редактирование данных в административной панели</h2>
<p>Продолжение темы мультиязычности в Symfony: <a href="http://tigor.com.ua/blog/2008/10/29/symfony-i18n-part-1-introduction/"  title="Symfony: интернационализация (I18n) / часть 1 - введение">первой части</a>, где рассказывались основы работы с интернационализацией в Symfony. Теперь рассмотрим, как данные редактировать в административной панели для нескольких языков.</p>
<p>Исходный код к статье - <a href="http://www.tigor.com.ua/blog/wp-content/uploads/demo-i18n_part2.zip" onclick="javascript:pageTracker._trackPageview('/downloadstigor./blog/wp-content/uploads/demo-i18n_part2.zip');">demo-i18n_part2.zip</a> (source code). Код написан на Symfony 1.2, Propel 1.3.</p>
<p><strong>Рисунок 2.1</strong>: Пример Frontend (Example Frontend)<br />
<a href="http://tigor.com.ua/blog/wp-content/uploads/2008/11/frontend_main_en.png" ><img src="http://tigor.com.ua/blog/wp-content/uploads/2008/11/frontend_main_en-300x204.png" alt="" title="frontend_main_en" width="300" height="204" class="alignnone size-medium wp-image-597" /></a><br />
<span id="more-590"></span></p>
<p><strong>Рисунок 2.2:</strong> Пример Backend - список записей (Example Backend - List of blog_post)<br />
<a href="http://tigor.com.ua/blog/wp-content/uploads/2008/11/backend_blog_post_list.png" ><img src="http://tigor.com.ua/blog/wp-content/uploads/2008/11/backend_blog_post_list.png" alt="" title="backend_blog_post_list" width="500" height="215" class="alignnone size-full wp-image-598" /></a></p>
<p><strong>Рисунок 2.3:</strong> Пример Backend - редактирование записи (Example Backend - Edit of blog_post) - там не перевод, статьи взяты с Google News отдельно для каждого из языков<br />
<a href="http://tigor.com.ua/blog/wp-content/uploads/2008/11/backend_blog_post_edit.png" ><img src="http://tigor.com.ua/blog/wp-content/uploads/2008/11/backend_blog_post_edit-300x227.png" alt="" title="backend_blog_post_edit" width="300" height="227" class="alignnone size-medium wp-image-599" /></a></p>
<p><br/></p>
<h2>Описание реализации</h2>
<p><br/><br />
Шаги описывают все по примеру, ссылка на который указана выше.</p>
<p>1) Изменить путь к фреймворку Symfony в файле конфигурации:<br />
Листинг 2.1: файл конфигурации <strong>/config/ProjectConfiguration.class.php</strong></p>
<pre name="code" class="php">

&lt;?php
require_once &#039;D:\home\symfony\vendors\symfony-1.2\lib/autoload/sfCoreAutoload.class.php&#039;;
sfCoreAutoload::register();

class ProjectConfiguration extends sfProjectConfiguration
{
  public function setup()
  {
  }
}
</pre>
<p>2) Изменить доступ к БД: propel.ini (user, password, dbname, &#8230;), databases.yml</p>
<p>3) Выполнить ряд команд, чтобы создать таблицы и внести тестовые значения<br />
&bull; php symfony propel:insert-sql<br />
&bull; php symfony propel:data-load</p>
<p>4) Заходим по ссылке <strong>http://demo-i18n/</strong>, предварительно настроив хост в Apache <img src='http://tigor.com.ua/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>С FrontEnd полагаю все понятно. Для реализации в админке возможности редактировать на одной странице данных для всех языков использовались наработки <a href="http://trac.symfony-project.org/wiki/HowToHandlei18nDbFieldsWithAdminGeneratorMethod2" onclick="javascript:pageTracker._trackPageview('/outbound/article/trac.symfony-project.org/trac.wiki_HowToHandlei18nDbFieldsWithAdminGeneratorMethod2');">How To Handle i18n Db Fields With the Admin Generator Method 2</a>. Код немного модифицировался, т.к. были проблемы при использовании plugins c Behaviors (propel.ini: propel.builder.addBehaviors = true), что приводило к тому, что Behaviors не срабатывали.</p>
<p>Листинг 2.2: код для редактирования всех локализированных полей БД в Административной Панели - прекрасно работает с плагинами Behaviors - добавлен в model BlogPost.php</p>
<pre name="code" class="php">

  /**
   * Handle I18n DB fields in Admin Generator
   *
   * @author Igor Brovchenko
   * @param string $method
   * @param mixed $arguments
   * @return mixed
   */
  public function __call($method, $arguments)
  {
    $data = split(&#039;I18n&#039;, $method, 2);

    if( count($data) != 2 )
    {
      // original call for support sfPropelBehavior
      return parent::__call($method, $arguments);
    }

    list( $method, $culture ) = $data;

    if (4 == strlen($culture))
    {
      $culture = strtolower(substr($culture, 0, 2)) . &#039;_&#039; . strtoupper(substr($culture, 2, 2));
    }
    else
    {
      $culture = strtolower($culture);
    }

    $this-&gt;setCulture( $culture );

    return call_user_func_array(array($this, $method), $arguments);
  }
</pre>
<p>Листинг 2.3: пример файла generator.yml для редактирования локализированных данных</p>
<pre name="code" class="php">

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      BlogPost
    theme:            default

    list:
      peer_method:    doSelectWithI18n
      max_per_page:   50
      display:        [ =id, =title ]
      sort:           [ id, desc ]

    edit:
      display:
        &quot;NONE&quot;:       [ created_at ]
        &quot;EN&quot;:
          - title_i18n_en
          - announce_i18n_en
          - body_i18n_en
        &quot;RU&quot;:
          - title_i18n_ru
          - announce_i18n_ru
          - body_i18n_ru

      fields:
        title_i18n_en:
          name:       Title
          params:     disabled=false maxlength=255 style=&quot;width:80%&quot;

        announce_i18n_en:
          name:       Announce
          type:       textarea_tag
          params:     disabled=false size=150x6

        body_i18n_en:
          name:       Body
          type:       textarea_tag
          params:     disabled=false size=150x6

        title_i18n_ru:
          name:       Title
          params:     disabled=false maxlength=255 style=&quot;width:80%&quot;

        announce_i18n_ru:
          name:       Announce
          type:       textarea_tag
          params:     disabled=false size=150x6

        body_i18n_ru:
          name:       Body
          type:       textarea_tag
          params:     disabled=false size=150x6
</pre>
<p>Принцип таков: нужно описать все поля, которые мы хотим редактировать с суффиксами языков (title_i18n_en, title_i18n_ru). Т.е. если хотим ещё редактировать французскую версию, то надо добавить следующий код.</p>
<p>Листинг 2.4: добавляем поддержку французского языка в файл generator.yml</p>
<pre name="code" class="php">

generator:
...

    edit:
      display:
        &quot;NONE&quot;:       [ created_at ]
        &quot;EN&quot;:
          - title_i18n_en
          - announce_i18n_en
          - body_i18n_en
        &quot;RU&quot;:
          - title_i18n_ru
          - announce_i18n_ru
          - body_i18n_ru
        &quot;FR&quot;:
          - title_i18n_fr
          - announce_i18n_fr
          - body_i18n_fr

      fields:
...
        title_i18n_fr:
          name:       Title
          params:     disabled=false maxlength=255 style=&quot;width:80%&quot;

        announce_i18n_fr:
          name:       Announce
          type:       textarea_tag
          params:     disabled=false size=150x6

        body_i18n_fr:
          name:       Body
          type:       textarea_tag
          params:     disabled=false size=150x6
</pre>
<p>Вот в принципе и все. Просто и удобно. Единственное, что плохо это то, что если БД очень большая, то количество полей огромно, и если потребуется отредактировать какие-то поля, а их много, что как следствие приводит к ошибкам (copy past). Поэтому, был написан Task, который позволяет упростить процесс редактирования и главное добавление новых языков, но это уже будет описано в следующей статье.</p>
<p><strong>Ссылки по теме:</strong><br />
&bull; <a href="http://tigor.com.ua/blog/2008/10/29/symfony-i18n-part-1-introduction/" >Symfony: интернационализация (I18n) / часть 1 - введение</a></p>
]]></content>
		<link rel="replies" type="text/html" href="http://tigor.com.ua/blog/2008/11/11/symfony-i18n-part-2-edit-data-in-admin-panel/#comments" thr:count="5" />
		<link rel="replies" type="application/atom+xml" href="http://tigor.com.ua/blog/2008/11/11/symfony-i18n-part-2-edit-data-in-admin-panel/feed/atom/" thr:count="5" />
		<thr:total>5</thr:total>
	</entry>
		<entry>
		<author>
			<name>ingvar</name>
					</author>
		<title type="html"><![CDATA[Symfony: интернационализация (I18n) / часть 1 - введение]]></title>
		<link rel="alternate" type="text/html" href="http://tigor.com.ua/blog/2008/10/29/symfony-i18n-part-1-introduction/" />
		<id>http://tigor.com.ua/blog/?p=569</id>
		<updated>2008-11-11T10:01:24Z</updated>
		<published>2008-10-28T22:32:29Z</published>
		<category scheme="http://tigor.com.ua/blog" term="Symfony" /><category scheme="http://tigor.com.ua/blog" term="I18n" />		<summary type="html"><![CDATA[Начинаю цикл статей по организации мультиязычности в Symfony. Мультиязычность достигается с помощью интернационализации и локализации.
Часть 1.1: интернационализация (I18n) - введение
Немного теории (источник Википедия).
Интернационализа́ция (англ. internationalization) — процесс адаптации продукта, такого как программное или аппаратное обеспечение, к языковым и культурным особенностям региона (регионов), отличного от того, в котором разрабатывался продукт. В английском языке для слова «internationalization» [...]]]></summary>
		<content type="html" xml:base="http://tigor.com.ua/blog/2008/10/29/symfony-i18n-part-1-introduction/"><![CDATA[<p>Начинаю цикл статей по организации мультиязычности в Symfony. Мультиязычность достигается с помощью интернационализации и локализации.</p>
<h2>Часть 1.1: интернационализация (I18n) - введение</h2>
<p>Немного теории (источник Википедия).</p>
<blockquote><p><strong>Интернационализа́ция</strong> (англ. internationalization) — процесс адаптации продукта, такого как программное или аппаратное обеспечение, к языковым и культурным особенностям региона (регионов), отличного от того, в котором разрабатывался продукт. В английском языке для слова «internationalization» принято сокращение «i18n». При этом число 18 означает количество пропущенных между «i» и «n» букв.</p></blockquote>
<blockquote><p><strong>Локализа́ция</strong> (англ. localization) — перевод и адаптация элементов интерфейса, вспомогательных файлов и документации. В английском языке для слова «localization» иногда применяется сокращение «l10n». При этом число 10 означает количество пропущенных между «l» и «n» букв.</p></blockquote>
<p><span id="more-569"></span></p>
<p>Собственно хорошая поддержка интернационализации в Symfony на фоне других систем, а также многие другие возможности помогли мне остановиться на этом замечательном фреймворке :).</p>
<p>Хочу поделиться моим небольшим опытом и достигнутыми результатами упрощения организации работы с I18n в Symfony. Весь материал разбил на несколько частей.</p>
<p>Использовалась Symfony 1.1 и Propel 1.3.</p>
<p><strong>Мои требования к интернационализации (I18n) для фреймворка:</strong><br />
1) Локализация интерфейса сайта<br />
2) Интернационализации данных (новости, статьи, продукция,…)<br />
3) Административная панель: редактирование локализации интерфейса<br />
4) Административная панель: редактирование данных с учетом интернационализации<br />
5) Простота использования, гибкость (возможность легко добавить новый язык), производительность<br />
6) Работать с локализацией через БД (MySQL), в частности через PDO (Propel  1.3), используя открытый коннект к БД</p>
<p>Symfony практически «справляется» со всеми требованиями. Но для некоторых процессов пришлось кое-что изменить, доработать. <strong>Добился ли я выполнения поставленных задач?</strong> ДА!!!!!</p>
<p>По умолчанию доступен один язык, и его можно активировать через настройки: <strong>frontend/config/settings.yml</strong></p>
<p>Листинг 1.1: настройка &#8220;культуры&#8221; по умолчанию</p>
<pre name="code" class="php">

all:
  .settings:
    default_culture: ru_RU  # или uk_UA
</pre>
<p>Не забудьте сбросить кеш.</p>
<p>Формат записи (локали): uk_UA  - язык и код страны.</p>
<p>Таким образом, мы говорим, какой профиль использовать для интернационализации.<br />
Список всех профилей можно найти в ядре Symfony: /symfony-1.1/lib/i18n/data/</p>
<p>Теперь если использовать Helpers, которые работают с I18n (календарь, список стран, дата…) мы на выходе получим тексты не в английском языке по умолчанию, а в украинском.</p>
<p>Листинг 1.2: получение текущей &#8220;культуры&#8221;</p>
<pre name="code" class="php">

$culture = $this-&gt;getUser()-&gt;getCulture();
</pre>
<p> </p>
<h2>Часть 1.2: организация итернационализации (I18n) контента в Symfony</h2>
<p>Сначала расскажу, как организована интернационализация данных, а после вернемся к локализации интерфейса в других частях.</p>
<p>Вначале наши данные без поддержки I18n имеют следующий вид.</p>
<p>Листинг 1.3: схема таблицы &#8220;Статьи&#8221; (schema.yml)</p>
<pre name="code" class="php">

propel:
  _attributes:          { package: lib.model.article }

  article:
    id:                 ~
    slug:               { type: varchar, size: 255, required: true }
    image_url:          { type: varchar, size: 255, required: true }
    title:              { type: varchar, size: 255, required: true }
    announce:           { type: text, required: true }
    full_text:          { type: text, required: true }
    created_at:         ~
</pre>
<p>Листинг 1.4: Маршрутизация (routing.yml):</p>
<pre name="code" class="php">

article:
  url: /articles/:year/:month/:day/:slug
  param: { module: article, action: index }
</pre>
<p>Ссылка на статью имела бы следующий вид: http://tigor.com.ua/articles/2008/10/28/i18n_hello_world</p>
<p>Добавляем интернационализацию.</p>
<p>Листинг 1.4: Маршрутизация с поддержкой I18n (routing.yml)</p>
<pre name="code" class="php">

article:
  url: /:sf_culture/articles/:year/:month/:day/:slug
  param: { module: article, action: index }
  requirements: { sf_culture: (?:ru|uk|en) }
</pre>
<p>Указываем к примеру 3-и языка: русский, украинский, английский. Если ввести что-то другое вместо языка, то страница будет не найдена, и получим код ошибки 404.</p>
<p>Теперь ссылка будет вот такая:<br />
• http://tigor.com.ua/en/articles/2008/10/28/i18n_hello_world<br />
• http://tigor.com.ua/ru/articles/2008/10/28/i18n_hello_world<br />
• http://tigor.com.ua/uk/articles/2008/10/28/i18n_hello_world</p>
<p>Но контент все еще на одном языке. Делаем правки схемы.</p>
<p>Листинг 1.5: схема с поддержкой I18n (schema.yml)</p>
<pre name="code" class="php">

propel:
  _attributes:          { package: lib.model.article }

  article:
    _attributes:        { phpName: Product, isI18N: true, i18nTable: article _i18n }
    id:                 ~
    slug:               { type: varchar, size: 255, required: true }
    image_url:          { type: varchar, size: 255, required: true }
    created_at:         ~

  article _i18n:
    id:                 { type: integer, required: true, primaryKey: true, foreignTable: article,
       foreignReference: id, onDelete: cascade, onUpdate: cascade }
    culture:            { isCulture: true, type: varchar, size: 7, required: true, primaryKey: true }
    title:              { type: varchar, size: 255, required: true }
    announce:           { type: text, required: true }
    full_text:          { type: text, required: true }
</pre>
<p>В Листинге 1.5 для таблицы <strong>article _i18n</strong> сознательно указаны <strong>id</strong> и <strong>culture</strong>, чтобы отобразить, что же Symfony вставляет при указании опции isI18N. Эти поля указывать не стоит, дабы не совершать ошибки. Но в исключительных это может понадобиться.</p>
<p>Листинг 1.6: Небольшие примеры работы с объектом Article</p>
<pre name="code" class="php">

// Чтение данных
$article = ProductPeer::retrieveByPk(1);
$title = $article -&gt;getTitle(); // По умолчанию испольщуется текущая культура, или переопределенная через машрутизацию
$title = $article -&gt;getTitle(‘en’); // Явно указываем язык

// Изменение данных
$article = ProductPeer::retrieveByPk(1);
$article-&gt;setTitle(‘Мир во всем мире!’, ‘ru’);
$article-&gt;save();
</pre>
<p><strong>Ссылки по теме:</strong><br />
&bull; <a href="http://tigor.com.ua/blog/2008/11/11/symfony-i18n-part-2-edit-data-in-admin-panel/" >Symfony: интернационализация (I18n) / часть 2 – редактирование данных в административной панели</a><br />
• <a href="http://ru.wikipedia.org/wiki/I18n" onclick="javascript:pageTracker._trackPageview('/outbound/article/ru.wikipedia.org/ru.wiki_I18n');">Википедия: Интернационализация</a><br />
• <a href="http://ru.wikipedia.org/wiki/L10n" onclick="javascript:pageTracker._trackPageview('/outbound/article/ru.wikipedia.org/ru.wiki_L10n');">Википедия: Локализация</a><br />
• <a href="http://en.wikipedia.org/wiki/Internationalization_and_localization" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org/en.wiki_Internationalization_and_localization');">Wikipedia: Internationalization_and_localization</a><br />
• <a href="http://www.symfony-project.org/book/1_1/13-I18n-and-L10n" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.symfony-project.org/www.book_1_1_13-I18n-and-L10n');">The Definitive Guide to symfony: Chapter 13 - I18n And L10n</a></p>
]]></content>
		<link rel="replies" type="text/html" href="http://tigor.com.ua/blog/2008/10/29/symfony-i18n-part-1-introduction/#comments" thr:count="6" />
		<link rel="replies" type="application/atom+xml" href="http://tigor.com.ua/blog/2008/10/29/symfony-i18n-part-1-introduction/feed/atom/" thr:count="6" />
		<thr:total>6</thr:total>
	</entry>
		<entry>
		<author>
			<name>ingvar</name>
					</author>
		<title type="html"><![CDATA[Первая конференция по Symfony в Украине состоялась]]></title>
		<link rel="alternate" type="text/html" href="http://tigor.com.ua/blog/2008/10/28/pervaya-konferentsiya-po-symfony-v-ukraine-sostoyalas/" />
		<id>http://tigor.com.ua/blog/?p=565</id>
		<updated>2008-10-28T20:25:32Z</updated>
		<published>2008-10-28T20:25:32Z</published>
		<category scheme="http://tigor.com.ua/blog" term="Symfony" />		<summary type="html"><![CDATA[Прошла первая в Украине конференция по фреймворку Symfony. Сие событие происходило в городе Черкассы. Было не так много людей, как предполагалась. Надеюсь на следующую удастся съездить :). 
Основная тема была Symfony 1.1 - на глазах у присутствующих было &#8220;собрано&#8221; небольшое приложение с использованием форм, админки. Осталось только дождаться, когда выложат презентации от всех докладчиков.
Источник - отчет по самой конференции.
]]></summary>
		<content type="html" xml:base="http://tigor.com.ua/blog/2008/10/28/pervaya-konferentsiya-po-symfony-v-ukraine-sostoyalas/"><![CDATA[<p>Прошла первая в Украине конференция по фреймворку Symfony. Сие событие происходило в городе Черкассы. Было не так много людей, как <span lang="RU">предполагалась</span>. Надеюсь на следующую <span lang="RU">удастся </span>съездить :). </p>
<p>Основная тема была Symfony 1.1 - на глазах у <span lang="RU">присутствующи</span><span lang="RU">х</span><span lang="RU"> </span>было &#8220;собрано&#8221; небольшое приложение с использованием форм, админки. Осталось только дождаться, когда выложат презентации от всех докладчиков.</p>
<p><a href="http://symfony.org.ua/2008/10/pervaya-konferentsiya-po-symfony-v-ukraine-sostoyalas/" onclick="javascript:pageTracker._trackPageview('/outbound/article/symfony.org.ua/symfony.2008_10_pervaya-konferentsiya-po-symfony-v-ukraine-sostoyalas');">Источник</a> - отчет по самой конференции.</p>
]]></content>
		<link rel="replies" type="text/html" href="http://tigor.com.ua/blog/2008/10/28/pervaya-konferentsiya-po-symfony-v-ukraine-sostoyalas/#comments" thr:count="0" />
		<link rel="replies" type="application/atom+xml" href="http://tigor.com.ua/blog/2008/10/28/pervaya-konferentsiya-po-symfony-v-ukraine-sostoyalas/feed/atom/" thr:count="0" />
		<thr:total>0</thr:total>
	</entry>
		<entry>
		<author>
			<name>ingvar</name>
					</author>
		<title type="html"><![CDATA[HighLoad++ 2008 / Видео докладов, презентации, тезисы, отчеты]]></title>
		<link rel="alternate" type="text/html" href="http://tigor.com.ua/blog/2008/10/14/highload-2008-presentation-video-reports/" />
		<id>http://tigor.com.ua/blog/?p=560</id>
		<updated>2008-10-14T21:07:05Z</updated>
		<published>2008-10-14T21:03:18Z</published>
		<category scheme="http://tigor.com.ua/blog" term="Новости" />		<summary type="html"><![CDATA[Подборка полезных ссылок по конференции HighLoad 2008:
 http://www.highload.ru/papers2008/ (тезисы и презентации конференции)
 http://ontico.ru/vita/p/2008/hl++presentations.rar (85 метров  презентаций)
 http://www.highload.ru/index-foto.html (1348 фотографий с HL++)
 http://www.highload.ru/news/10488.html (видеорепортаж от О2ТВ)
 http://blogs.yandex.ru/search.xml?text=highload&#38;ft=blog (в блогах встречаются хорошие репортажи о докладах и конференции)
 http://smotri.com/community/video/highload/ (видео докладов)
 http://www.russia.ru/video/high-load/ (видеорепортаж от Russia.ru)
]]></summary>
		<content type="html" xml:base="http://tigor.com.ua/blog/2008/10/14/highload-2008-presentation-video-reports/"><![CDATA[<p>Подборка полезных ссылок по конференции HighLoad 2008:</p>
<p> <a href="http://www.highload.ru/papers2008/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.highload.ru/www.papers2008');" target="_blank">http://www.highload.ru/papers2008/</a> (тезисы и презентации конференции)<br />
 <a href="http://ontico.ru/vita/p/2008/hl++presentations.rar" onclick="javascript:pageTracker._trackPageview('/outbound/article/ontico.ru/vita_p_2008_hl++presentations.rar');" target="_blank">http://ontico.ru/vita/p/2008/hl++presentations.rar</a> (85 метров  презентаций)<br />
 <a href="http://www.highload.ru/index-foto.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.highload.ru/www.index-foto.html');" target="_blank">http://www.highload.ru/index-foto.html</a> (1348 фотографий с HL++)<br />
 <a href="http://www.highload.ru/news/10488.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.highload.ru/www.news_10488.html');" target="_blank">http://www.highload.ru/news/10488.html</a> (видеорепортаж от О2ТВ)<br />
 <a href="http://blogs.yandex.ru/search.xml?text=highload&amp;ft=blog" onclick="javascript:pageTracker._trackPageview('/outbound/article/blogs.yandex.ru/blogs.search.xml?text=highload&amp;ft=blog');" target="_blank">http://blogs.yandex.ru/search.xml?text=highload&amp;ft=blog</a> (в блогах встречаются хорошие репортажи о докладах и конференции)<br />
 <a href="http://smotri.com/community/video/highload/" onclick="javascript:pageTracker._trackPageview('/outbound/article/smotri.com/community_video_highload');" target="_blank">http://smotri.com/community/video/highload/</a> (видео докладов)<br />
 <a href="http://www.russia.ru/video/high-load/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.russia.ru/www.video_high-load');" target="_blank">http://www.russia.ru/video/high-load/</a> (видеорепортаж от Russia.ru)</p>
]]></content>
		<link rel="replies" type="text/html" href="http://tigor.com.ua/blog/2008/10/14/highload-2008-presentation-video-reports/#comments" thr:count="0" />
		<link rel="replies" type="application/atom+xml" href="http://tigor.com.ua/blog/2008/10/14/highload-2008-presentation-video-reports/feed/atom/" thr:count="0" />
		<thr:total>0</thr:total>
	</entry>
		<entry>
		<author>
			<name>ingvar</name>
					</author>
		<title type="html"><![CDATA[Symfony: неправильный символ апострофа в файле uk.dat для украинского языка]]></title>
		<link rel="alternate" type="text/html" href="http://tigor.com.ua/blog/2008/09/21/symfony-bad-symbol-apostrophe-in-i18n-ukdat-file/" />
		<id>http://tigor.com.ua/blog/?p=552</id>
		<updated>2008-09-21T20:34:01Z</updated>
		<published>2008-09-21T17:41:32Z</published>
		<category scheme="http://tigor.com.ua/blog" term="Symfony" />		<summary type="html"><![CDATA[Необходимо было реализовать украинскую версию на Symfony. И какое же было разочарование когда возникли проблемы со словами, где встречается апостроф, например: П’ятниця, и др&#8230; В браузерах IE, Opera слова просто &#8220;рвало&#8221; или, что ещё хуже был просто квадрат, как будто такого символа в таблице UTF-8 нет. При изучения файла uk.xml стало ясно, что это символ [...]]]></summary>
		<content type="html" xml:base="http://tigor.com.ua/blog/2008/09/21/symfony-bad-symbol-apostrophe-in-i18n-ukdat-file/"><![CDATA[<p>Необходимо было реализовать украинскую версию на Symfony. И какое же было разочарование когда возникли проблемы со словами, где встречается апостроф, например: П’ятниця, и др&#8230; В браузерах IE, Opera слова просто &#8220;рвало&#8221; или, что ещё хуже был просто квадрат, как будто такого символа в таблице UTF-8 нет. При изучения файла <strong>uk.xml</strong> стало ясно, что это символ апостроф. В итоге после исправления всех слов получился пропатченный файл, который прекрасно работает во всех браузерах.<br />
Скачать <strong><a href="http://tigor.com.ua/blog/wp-content/uploads/uk.xml" >пропатченный файл uk.xml</a></strong>.</p>
<p>Возник вопрос: откуда разработчики скопировали данные для файлов i18n и выяснилось, что данные были получены с сайта: <a href="http://unicode.org" onclick="javascript:pageTracker._trackPageview('/outbound/article/unicode.org/unicode.org');">unicode.org</a>, где те же проблемы :(. Хотя  в файле <strong>Ukrainian-Latin-BGN.xml</strong> символ апострофа правильный.</p>
<p><strong>P.S.</strong><br />
За что я обожаю сообщество Symfony - за его оперативность, не успел разместить задачу с этой проблемой, как сегодня же обновили этот файл моим патчем :), так что ждем несколько дней и скачиваем обновление из SVN.</p>
]]></content>
		<link rel="replies" type="text/html" href="http://tigor.com.ua/blog/2008/09/21/symfony-bad-symbol-apostrophe-in-i18n-ukdat-file/#comments" thr:count="0" />
		<link rel="replies" type="application/atom+xml" href="http://tigor.com.ua/blog/2008/09/21/symfony-bad-symbol-apostrophe-in-i18n-ukdat-file/feed/atom/" thr:count="0" />
		<thr:total>0</thr:total>
	</entry>
		<entry>
		<author>
			<name>ingvar</name>
					</author>
		<title type="html"><![CDATA[Замыкания в JavaScript]]></title>
		<link rel="alternate" type="text/html" href="http://tigor.com.ua/blog/2008/09/03/closure-in-javascript/" />
		<id>http://tigor.com.ua/blog/?p=548</id>
		<updated>2008-09-23T21:28:12Z</updated>
		<published>2008-09-02T23:00:43Z</published>
		<category scheme="http://tigor.com.ua/blog" term="Javascript" />		<summary type="html"><![CDATA[Если вы используете JavaScript, но при этом так до конца и не разобрались, что же это за чудная штука такая — замыкания, и зачем она нужна — эта статья для вас.
Читать подробнее&#8230;
Хорошая презентация о замыканиях в JavaScript, Secrets of JavaScript closures
]]></summary>
		<content type="html" xml:base="http://tigor.com.ua/blog/2008/09/03/closure-in-javascript/"><![CDATA[<p>Если вы используете JavaScript, но при этом так до конца и не разобрались, что же это за чудная штука такая — <a href="http://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%BC%D1%8B%D0%BA%D0%B0%D0%BD%D0%B8%D0%B5_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)" onclick="javascript:pageTracker._trackPageview('/outbound/article/ru.wikipedia.org/ru.wiki_%D0%97%D0%B0%D0%BC%D1%8B%D0%BA%D0%B0%D0%BD%D0%B8%D0%B5_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)');">замыкания</a>, и зачем она нужна — эта статья для вас.</p>
<p><a href="http://habrahabr.ru/blogs/webdev/38642/" onclick="javascript:pageTracker._trackPageview('/outbound/article/habrahabr.ru/blogs_webdev_38642');">Читать подробнее&#8230;</a></p>
<p><a href="http://www.kryogenix.org/code/browser/secrets-of-javascript-closures/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.kryogenix.org/www.code_browser_secrets-of-javascript-closures');">Хорошая презентация о замыканиях в JavaScript, Secrets of JavaScript closures</a></p>
]]></content>
		<link rel="replies" type="text/html" href="http://tigor.com.ua/blog/2008/09/03/closure-in-javascript/#comments" thr:count="0" />
		<link rel="replies" type="application/atom+xml" href="http://tigor.com.ua/blog/2008/09/03/closure-in-javascript/feed/atom/" thr:count="0" />
		<thr:total>0</thr:total>
	</entry>
		<entry>
		<author>
			<name>ingvar</name>
					</author>
		<title type="html"><![CDATA[Symfony 1.2: небольшие приятные изменения 2008-09-02]]></title>
		<link rel="alternate" type="text/html" href="http://tigor.com.ua/blog/2008/09/03/new-in-symfony-1-2-small-things-matter/" />
		<id>http://tigor.com.ua/blog/?p=539</id>
		<updated>2008-09-02T22:01:13Z</updated>
		<published>2008-09-02T22:00:18Z</published>
		<category scheme="http://tigor.com.ua/blog" term="Symfony" />		<summary type="html"><![CDATA[С каждой новой версией разработчики Symfony делают все, чтобы упростить работу API и сделать её более интуитивной и мощной. Вот некоторые примеры того, чем можно будет воспользоваться в symfony 1.2.
1. Названия Приложениний в CLI задачах / Application name in CLI tasks
Некоторые Propel задачи требует указания имя application в аргументах, потому что требуется подключение к БД. [...]]]></summary>
		<content type="html" xml:base="http://tigor.com.ua/blog/2008/09/03/new-in-symfony-1-2-small-things-matter/"><![CDATA[<p>С каждой новой версией разработчики Symfony делают все, чтобы упростить работу API и сделать её более интуитивной и мощной. Вот некоторые примеры того, чем можно будет воспользоваться в symfony 1.2.</p>
<p><strong>1. Названия Приложениний в CLI задачах / Application name in CLI tasks</strong></p>
<p>Некоторые Propel задачи требует указания имя application в аргументах, потому что требуется подключение к БД. На самом деле в некоторых случаях этот параметр не требуется, когда задачи работают с БД не для конкретного application, а всего проекта.</p>
<p>Команда propel:build-all-load теперь выполняется как и другие задачи вида propel:build-*:</p>
<pre name="code" class="php">

# symfony 1.2
./symfony propel:build-all-load

# symfony 1.1
./symfony propel:build-all-load application_name
</pre>
<p><span id="more-539"></span></p>
<p>Выполнять загрузку данных (fixtures) или дамп БД в YAML можно следующими задачами:</p>
<pre name="code" class="html">

./symfony propel:data-load
./symfony propel:data-dump
</pre>
<p>Если же требуется специфическая конфигурация задачи, то можно использовать нужное application:</p>
<pre name="code" class="html">

./symfony propel:buil-all-load --application=frontend
</pre>
<p><br/><br />
<strong>2. Поддержка запросов PUT и DELETE посредством браузера</strong></p>
<p>Теперь можно имитировать запросы вида PUT и DELETE через метод POST добавив в форму специальный скрытый параметр sf_method:</p>
<pre name="code" class="html">

&lt;form action=&quot;#&quot; method=&quot;POST&quot;&gt;
  &lt;input type=&quot;hidden&quot; name=&quot;sf_method&quot; value=&quot;PUT&quot; /&gt;

  &lt;!-- // ... --&gt;
&lt;/form&gt;
</pre>
<p>Если работая с формой вызвать метод sfRequest::getMethod(), то получим в ответ PUT. Новые возможности позволяют работать с REST приложениями в более родной форме.</p>
<p><br/><br />
<strong>3. Ссылки в ответах / Shortcuts in the response</strong></p>
<p>Порою требуется получить список Stylesheets или JavaScripts от текущего ответа (response). Но т.к. методы getStylesheets() и getJavascripts() возвращают внутреннее отображение информации, то это не то, чего мы ожидали. В symfony 1.2 эти два метода возвращают информацию в удобном формате:</p>
<pre name="code" class="php">

array(
  &#039;bar.css&#039; =&gt; array(),
  &#039;foo.css&#039; =&gt; array(),
)
</pre>
<p><br/><br />
<strong>4. Валидатор sfValidatorSchemaCompare</strong></p>
<p>Изменилась константа валидатора sfValidatorSchemaCompare. Следующее два примера сейчас эквивалентны:</p>
<pre name="code" class="php">

// symfony 1.1 and 1.2
$v = new sfValidatorSchemaCompare(&#039;left&#039;, sfValidatorSchemaCompare::EQUAL, &#039;right&#039;);

// symfony 1.2 only
$v = new sfValidatorSchemaCompare(&#039;left&#039;, &#039;==&#039;, &#039;right&#039;);
</pre>
<p>Новый формат стал более читабельным и интуитивным.</p>
<p>Оригинал статьи: <a href="http://www.symfony-project.org/blog/2008/09/02/new-in-symfony-1-2-small-things-matter" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.symfony-project.org/www.blog_2008_09_02_new-in-symfony-1-2-small-things-matter');">New in symfony 1.2: Small things matter</a></p>
]]></content>
		<link rel="replies" type="text/html" href="http://tigor.com.ua/blog/2008/09/03/new-in-symfony-1-2-small-things-matter/#comments" thr:count="0" />
		<link rel="replies" type="application/atom+xml" href="http://tigor.com.ua/blog/2008/09/03/new-in-symfony-1-2-small-things-matter/feed/atom/" thr:count="0" />
		<thr:total>0</thr:total>
	</entry>
		<entry>
		<author>
			<name>ingvar</name>
					</author>
		<title type="html"><![CDATA[Стандарты кодирования на PHP]]></title>
		<link rel="alternate" type="text/html" href="http://tigor.com.ua/blog/2008/08/27/coding-standards-php/" />
		<id>http://tigor.com.ua/blog/?p=537</id>
		<updated>2008-08-27T21:26:52Z</updated>
		<published>2008-08-27T21:26:52Z</published>
		<category scheme="http://tigor.com.ua/blog" term="Программирование" /><category scheme="http://tigor.com.ua/blog" term="Стандарты кодирования" />		<summary type="html"><![CDATA[Хорошая подборка по разным стандартам кодирования в PHP.

PEAR Coding Standards
Zend Framework Coding Standard
Symfony Coding Standards
Cake PHP Coding Standards
DB Medialab PHP Coding Standard
Стандарты оформления кода PHP (производная предыдущего пункта)
PHP Coding Guidelines
GForge: PHP Coding Standards

Источник:
Стандарты кодирования
]]></summary>
		<content type="html" xml:base="http://tigor.com.ua/blog/2008/08/27/coding-standards-php/"><![CDATA[<p>Хорошая подборка по разным стандартам кодирования в PHP.</p>
<ol>
<li><a href="http://pear.php.net/manual/en/standards.php" onclick="javascript:pageTracker._trackPageview('/outbound/article/pear.php.net/pear.manual_en_standards.php');">PEAR Coding Standards</a></li>
<li><a href="http://framework.zend.com/manual/en/coding-standard.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/framework.zend.com/framework.manual_en_coding-standard.html');">Zend Framework Coding Standard</a></li>
<li><a href="http://trac.symfony-project.org/wiki/HowToContributeToSymfony#CodingStandards" onclick="javascript:pageTracker._trackPageview('/outbound/article/trac.symfony-project.org/trac.wiki_HowToContributeToSymfony#CodingStandards');">Symfony Coding Standards</a></li>
<li><a href="http://trac.cakephp.org/wiki/Developement/CodingStandards" onclick="javascript:pageTracker._trackPageview('/outbound/article/trac.cakephp.org/trac.wiki_Developement_CodingStandards');">Cake PHP Coding Standards</a></li>
<li><a href="http://www.dagbladet.no/development/phpcodingstandard/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.dagbladet.no/www.development_phpcodingstandard');">DB Medialab PHP Coding Standard</a></li>
<li><a href="http://tony2001.phpclub.net/doc/standard/" onclick="javascript:pageTracker._trackPageview('/outbound/article/tony2001.phpclub.net/tony2001.doc_standard');">Стандарты оформления кода PHP</a> (производная предыдущего пункта)</li>
<li><a href="http://www.evolt.org/article/PHP_coding_guidelines/18/60247/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.evolt.org/www.article_PHP_coding_guidelines_18_60247');">PHP Coding Guidelines</a></li>
<li><a href="http://gforge.org/docman/view.php/1/2/coding-standards.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/gforge.org/docman_view.php_1_2_coding-standards.html');">GForge: PHP Coding Standards</a></li>
</ol>
<p>Источник:<br />
<a href="http://habrahabr.ru/blogs/php/38214/" onclick="javascript:pageTracker._trackPageview('/outbound/article/habrahabr.ru/blogs_php_38214');">Стандарты кодирования</a></p>
]]></content>
		<link rel="replies" type="text/html" href="http://tigor.com.ua/blog/2008/08/27/coding-standards-php/#comments" thr:count="0" />
		<link rel="replies" type="application/atom+xml" href="http://tigor.com.ua/blog/2008/08/27/coding-standards-php/feed/atom/" thr:count="0" />
		<thr:total>0</thr:total>
	</entry>
		<entry>
		<author>
			<name>ingvar</name>
					</author>
		<title type="html"><![CDATA[Триггеры в MySQL]]></title>
		<link rel="alternate" type="text/html" href="http://tigor.com.ua/blog/2008/08/27/mysql-triggers/" />
		<id>http://tigor.com.ua/blog/?p=525</id>
		<updated>2008-09-04T21:14:05Z</updated>
		<published>2008-08-27T21:08:27Z</published>
		<category scheme="http://tigor.com.ua/blog" term="MySQL" />		<summary type="html"><![CDATA[Поддержка триггеров появилась в MySQL 5.0.2. Триггер - это аналог процедуры, который ассоциируется с конкретной таблицей и запускается при наступлении определенных событий связанных с этой самой таблицей. Событий для таблиц всего несколько: INSERT, DELETE, UPDATE.
Пример триггера INSERT для таблицы News - записываем в поле `created_at` дату и время создания записи. Конечно можно было воспользоваться TIMESTAMP, [...]]]></summary>
		<content type="html" xml:base="http://tigor.com.ua/blog/2008/08/27/mysql-triggers/"><![CDATA[<p>Поддержка триггеров появилась в MySQL 5.0.2. Триггер - это аналог процедуры, который ассоциируется с конкретной таблицей и запускается при наступлении определенных событий связанных с этой самой таблицей. Событий для таблиц всего несколько: INSERT, DELETE, UPDATE.</p>
<p>Пример триггера INSERT для таблицы News - записываем в поле `created_at` дату и время создания записи. Конечно можно было воспользоваться TIMESTAMP, но в данном случае это сделано в качестве простого примера:<br />
<span id="more-525"></span></p>
<pre name="code" class="sql">

DROP TRIGGER IF EXISTS  `database`.`news_insert`;

DELIMITER $$

CREATE
    DEFINER = CURRENT_USER
    TRIGGER `database`.`news_insert` BEFORE INSERT
    ON `database`.`news`
    FOR EACH ROW BEGIN
      SET NEW.`created_at` = NOW();
    END$$

DELIMITER ;
</pre>
<p>В триггере используем оператор <b>DEFINER = CURRENT_USER</b>, его можно опустить, тогда в этом случае будут права текущего пользователя, но можно и установить другого, например:</p>
<pre name="code" class="sql">

CREATE
    DEFINER = `user`@&#039;localhost&#039;
    TRIGGER `database`.`news_insert` BEFORE INSERT
....
</pre>
<p>До версии MySQL 5.1.6 для создания триггеров требовались полномочия суперпользователя, что не совсем хорошо, т.к. во-первых на шаровых хостингах не предоставляют полномочия суперпользователя, а во-вторых это потенциальная брешь в системе безопасности. Без прав суперпользователя в MySQL ниже 5.1.6 получим ошибку:</p>
<pre name="code" class="sql">

ERROR 1227 (42000): Access denied; you need the SUPER privilege for this operation
</pre>
<p>Пример создание пользователя в MySQL и предоставления ему обычных прав:</p>
<pre name="code" class="sql">

#DROP USER &#039;user&#039;@&#039;localhost&#039;;
# &#039;&#039;
CREATE USER &#039;user&#039;@&#039;localhost&#039; IDENTIFIED BY PASSWORD &#039;*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4;
GRANT USAGE ON *.* TO &#039;user&#039;@&#039;localhost&#039; IDENTIFIED BY PASSWORD &#039;*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4;
GRANT ALL PRIVILEGES ON `database`.* TO &#039;user&#039;@&#039;localhost&#039; WITH GRANT OPTION;
</pre>
<p>Пример создание пользователя и предоставления ему прав суперпользователя:</p>
<pre name="code" class="sql">

CREATE USER &#039;user&#039;@&#039;localhost&#039; IDENTIFIED BY &#039;my_password&#039;;
GRANT USAGE ON *.* TO &#039;user&#039;@&#039;localhost&#039;;
GRANT ALL PRIVILEGES ON `database`.* TO &#039;user&#039;@&#039;localhost&#039; WITH GRANT OPTION;
GRANT SUPER ON *.* TO &#039;user&#039;@&#039;localhost&#039;;
</pre>
<p>Ссылки по теме:<br />
&bull; <a href="http://dev.mysql.com/doc/refman/5.1/en/create-trigger.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/dev.mysql.com/dev.doc_refman_5.1_en_create-trigger.html');">MySQL 5.1 Reference Manual :: 24.1 CREATE TRIGGER Syntax</a><br />
&bull; <a href="http://dev.mysql.com/doc/refman/5.1/en/grant.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/dev.mysql.com/dev.doc_refman_5.1_en_grant.html');">MySQL 5.1 Reference Manual :: 12.5.1.3 GRANT Syntax</a><br />
&bull; <a href="http://dev.mysql.com/tech-resources/articles/mysql-triggers.pdf" onclick="javascript:pageTracker._trackPageview('/outbound/article/dev.mysql.com/dev.tech-resources_articles_mysql-triggers.pdf');">PDF: MySQL 5.0 Triggers</a><br />
&bull; <a href="http://habrahabr.ru/blogs/mysql/37693/" onclick="javascript:pageTracker._trackPageview('/outbound/article/habrahabr.ru/blogs_mysql_37693');">Habrahabr: Триггеры в MySQL</a></p>
]]></content>
		<link rel="replies" type="text/html" href="http://tigor.com.ua/blog/2008/08/27/mysql-triggers/#comments" thr:count="0" />
		<link rel="replies" type="application/atom+xml" href="http://tigor.com.ua/blog/2008/08/27/mysql-triggers/feed/atom/" thr:count="0" />
		<thr:total>0</thr:total>
	</entry>
		<entry>
		<author>
			<name>ingvar</name>
					</author>
		<title type="html"><![CDATA[sfPropelPagerI18n: расширяем sfPropelPager для поддержки i18n]]></title>
		<link rel="alternate" type="text/html" href="http://tigor.com.ua/blog/2008/08/24/symfony-sfpropelpageri18n-propel-pager-with-i18n/" />
		<id>http://tigor.com.ua/blog/?p=502</id>
		<updated>2008-08-24T00:56:26Z</updated>
		<published>2008-08-24T00:26:53Z</published>
		<category scheme="http://tigor.com.ua/blog" term="Symfony" /><category scheme="http://tigor.com.ua/blog" term="Propel" />		<summary type="html"><![CDATA[sfPropelPager - это встроенная в Propel функциональность, которая позволяет организовать Пейджинг (Propel Pager), т.е. разбивку на страницы. Работает великолепно, ознакомиться подробнее можно в статье How to paginate a list. Описанные ниже действия проводились на Propel 1.3.
Принцип работы простой, сначала делается запрос на определение количества записей, а далее получаем данные с учетом количества их на странице. [...]]]></summary>
		<content type="html" xml:base="http://tigor.com.ua/blog/2008/08/24/symfony-sfpropelpageri18n-propel-pager-with-i18n/"><![CDATA[<p>sfPropelPager - это встроенная в Propel функциональность, которая позволяет организовать Пейджинг (Propel Pager), т.е. разбивку на страницы. Работает великолепно, ознакомиться подробнее можно в статье<a href="http://www.symfony-project.org/cookbook/1_1/en/pager" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.symfony-project.org/www.cookbook_1_1_en_pager');"> How to paginate a list</a>. Описанные ниже действия проводились на Propel 1.3.</p>
<p>Принцип работы простой, сначала делается запрос на определение количества записей, а далее получаем данные с учетом количества их на странице. Например, имеем Propel ORM объекты <strong>News</strong> и <strong>NewsI18n</strong>.<br />
<span id="more-502"></span><br />
Пример php-кода:</p>
<pre name="code" class="php">

$c = new Criteria();
$c-&gt;addDescendingOrderByColumn(NewsPeer::PUBLISHED_AT);
$pager = new sfPropelPager(&#039;News&#039;, 10);
$pager-&gt;setPage($this-&gt;getRequestParameter(&#039;page&#039;, 1));
$pager-&gt;setCriteria($c);
$pager-&gt;init();
$this-&gt;pager = $pager;
</pre>
<p>Реальные SQL-запросы, что получились в итоге:</p>
<pre name="code" class="sql">

# Первый запрос: определяем кол-во объектов
SELECT COUNT(*) FROM `news`

# Выборка данных
SELECT news.* FROM `news` ORDER BY news.PUBLISHED_AT DESC LIMIT 10;
</pre>
<p>Вопрос: как заставить sfPropelPager работать с таблицей <strong>news_i18n</strong>?</p>
<p>Оказывается есть замечательные методы в sfPropelPager: <strong>setPeerMethod</strong> и <strong>setPeerCountMethod</strong>, которые можно переназначить. И все вроде должно быть в порядке. Как бы не так. Вот что имеем:</p>
<pre name="code" class="php">

$c = new Criteria();
$c-&gt;addDescendingOrderByColumn(NewsPeer::PUBLISHED_AT);
$pager = new sfPropelPager(&#039;News&#039;, 10);
$pager-&gt;setPage($this-&gt;getRequestParameter(&#039;page&#039;, 1));
$pager-&gt;setPeerMethod(&#039;doSelectWithI18n&#039;);
$pager-&gt;setCriteria($c);
$pager-&gt;init();
$this-&gt;pager = $pager;
</pre>
<p>Журнал запросов:</p>
<pre name="code" class="sql">

# Первый запрос: определяем кол-во объектов
SELECT COUNT(*) FROM `news`

# Выборка данных
SELECT news.*, news_i18n.*, FROM `news`, `news_i18n` WHERE news_i18n.CULTURE=&#039;en&#039; AND news.ID=news_i18n.ID
ORDER BY news.PUBLISHED_AT DESC LIMIT 10
</pre>
<p>После изучения принципов работы sfPropelPager выяснилось следующее:<br />
1) Как в setPeerCountMethod указать правильный метод, которого кстати нет, чтобы выборка количества осуществлялась с учётом языка?<br />
2) Как задать язык для формирования пейджинга? В оригинальном BaseNewsPeer.php метод doSelectWithI18n имеет следующий вид: <strong>public static function doSelectWithI18n(Criteria $c, $culture = null, PropelPDO $con = null)</strong>. Тут есть $culture, а sfPropelPager  никак не переназначает значение, значит, срабатывает следующий код:</p>
<pre name="code" class="php">

    if ($culture === null)
    {
      $culture = sfPropel::getDefaultCulture();
    }
</pre>
<p>В итоге устанавливается язык по умолчанию.</p>
<p>В связи с эти был написан класс, который позволяет решить данную проблему. За основу была взята идея <a href="http://www.symfony-project.org/forum/index.php/t/14991/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.symfony-project.org/www.forum_index.php_t_14991');">sfPropelPager with I18n </a> и доработана до нормального состояния.<br />
Мой класс, который можно <a href="http://tigor.com.ua/blog/wp-content/uploads/sfPropelPagerI18n.zip" onclick="javascript:pageTracker._trackPageview('/downloadstigor./blog/wp-content/uploads/sfPropelPagerI18n.zip');">скачать <b>sfPropelPagerI18n</b></a>. Файл надо положить в директорию <b>lib</b>.</p>
<pre name="code" class="php">

&lt;?php
/**
 * This class is the Propel implementation of sfPropelPager.  It interacts with the propel record set and
 * manages criteria.
 *
 * @package    symfony
 * @subpackage propel
 * @author     Igor Brovchenko
 */
class sfPropelPagerI18n extends sfPropelPager
{

  /**
    * Constructor.
    *
    * @param string $class
    * @param int $maxPerPage
    * @param string $culture
    * @param string $joinField default value &#039;ID&#039;
    * @return sfPropelPagerI18n
    */
  public function __construct($class, $maxPerPage = 10, $culture = null, $joinField = &#039;ID&#039;)
  {
    if ($culture === null)
    {
      $culture = sfPropel::getDefaultCulture();
    }

    $this-&gt;joinField       = $joinField;
    $this-&gt;currentCulture  = $culture;

    parent::__construct($class, $maxPerPage);

    $this-&gt;setPeerMethod(&#039;doSelectWithI18n&#039;);
  }

  /**
   * Set Criteria for Pager.
   *
   * @param Criteria $c
   */
  public function setCriteriaI18n($c)
  {
    $this-&gt;criteria = $c;

    $this-&gt;criteria-&gt;addJoin(
      constant($this-&gt;getClass() . &#039;Peer::&#039; . $this-&gt;joinField),
      constant($this-&gt;getClass() . &#039;I18nPeer::&#039; . $this-&gt;joinField),
      Criteria::INNER_JOIN
    );
    $c-&gt;add(constant($this-&gt;getClass() . &#039;I18nPeer::CULTURE&#039;), $this-&gt;currentCulture);
  }

  /**
   * Returns the result of Pager.
   *
   */
  public function getResults()
  {
    $c = $this-&gt;getCriteria();

    return call_user_func(array($this-&gt;getClassPeer(), $this-&gt;getPeerMethod()), $c, $this-&gt;currentCulture);
  }
}
</pre>
<p><b>Пример использования sfPropelPagerI18n:</b></p>
<pre name="code" class="php">

    $c = new Criteria();
    $c-&gt;addDescendingOrderByColumn(NewsPeer::PUBLISHED_AT);
    $c-&gt;add(NewsPeer::IS_PUBLISHED, true, Criteria::EQUAL);
    // Создаем новый Pager вместо старого sfPropelPager, и указываем язык
    $pager = new sfPropelPagerI18n(&#039;News&#039;, 1, &#039;ru&#039;);
    $pager-&gt;setPage($this-&gt;getRequestParameter(&#039;page&#039;, 1));
    // Критерий тоже новый
    $pager-&gt;setCriteriaI18n($c);
    $pager-&gt;init();
    $this-&gt;pager = $pager;
</pre>
<p>Что же позволяет делать класс sfPropelPagerI18n:<br />
1) Автоматически формируется запрос типа <b>doCount</b> с учётом i18n и всех дополнительных условий Criteria.<br />
2) Указать свою culture или учитывается текущая.<br />
3) Указать поле для связи таблицы, по умолчанию &#8216;ID&#8217;, но его можно переназначить.</p>
<p>Ссылки по теме:<br />
&bull; <a href="http://www.symfony-project.org/api/1_1/sfPropelPager" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.symfony-project.org/www.api_1_1_sfPropelPager');">symfony API: sfPropelPager Class</a><br />
&bull; <a href="http://www.symfony-project.org/api/1_1/sfPager" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.symfony-project.org/www.api_1_1_sfPager');">symfony API: sfPager Class</a><br />
&bull; <a href="http://www.symfony-project.org/forum/index.php/t/14991/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.symfony-project.org/www.forum_index.php_t_14991');">http://www.symfony-project.org/forum/index.php/t/14991/</a><br />
&bull; <a href="http://groups.google.fr/group/symfony-users/browse_thread/thread/413beca4468e57e2" onclick="javascript:pageTracker._trackPageview('/outbound/article/groups.google.fr/groups.group_symfony-users_browse_thread_thread_413beca4468e57e2');">http://groups.google.fr/group/symfony-users/browse_thread/thread/413beca4468e57e2</a><br />
&bull; <a href="http://groups.google.es/group/symfony-es/browse_thread/thread/e8f00368d629649a" onclick="javascript:pageTracker._trackPageview('/outbound/article/groups.google.es/groups.group_symfony-es_browse_thread_thread_e8f00368d629649a');">http://groups.google.es/group/symfony-es/browse_thread/thread/e8f00368d629649a</a></p>
]]></content>
		<link rel="replies" type="text/html" href="http://tigor.com.ua/blog/2008/08/24/symfony-sfpropelpageri18n-propel-pager-with-i18n/#comments" thr:count="2" />
		<link rel="replies" type="application/atom+xml" href="http://tigor.com.ua/blog/2008/08/24/symfony-sfpropelpageri18n-propel-pager-with-i18n/feed/atom/" thr:count="2" />
		<thr:total>2</thr:total>
	</entry>
	</feed>
