Aug 24 2008

sfPropelPagerI18n: расширяем sfPropelPager для поддержки i18n

Category: Symfonyingvar @ 02:26

sfPropelPager — это встроенная в Propel функциональность, которая позволяет организовать Пейджинг (Propel Pager), т.е. разбивку на страницы. Работает великолепно, ознакомиться подробнее можно в статье How to paginate a list. Описанные ниже действия проводились на Propel 1.3.

Принцип работы простой, сначала делается запрос на определение количества записей, а далее получаем данные с учетом количества их на странице. Например, имеем Propel ORM объекты News и NewsI18n.

Пример php-кода:

$c = new Criteria();
$c->addDescendingOrderByColumn(NewsPeer::PUBLISHED_AT);
$pager = new sfPropelPager('News', 10);
$pager->setPage($this->getRequestParameter('page', 1));
$pager->setCriteria($c);
$pager->init();
$this->pager = $pager;

Реальные SQL-запросы, что получились в итоге:

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

# Выборка данных
SELECT news.* FROM `news` ORDER BY news.PUBLISHED_AT DESC LIMIT 10;

Вопрос: как заставить sfPropelPager работать с таблицей news_i18n?

Оказывается есть замечательные методы в sfPropelPager: setPeerMethod и setPeerCountMethod, которые можно переназначить. И все вроде должно быть в порядке. Как бы не так. Вот что имеем:

$c = new Criteria();
$c->addDescendingOrderByColumn(NewsPeer::PUBLISHED_AT);
$pager = new sfPropelPager('News', 10);
$pager->setPage($this->getRequestParameter('page', 1));
$pager->setPeerMethod('doSelectWithI18n');
$pager->setCriteria($c);
$pager->init();
$this->pager = $pager;

Журнал запросов:

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

# Выборка данных
SELECT news.*, news_i18n.*, FROM `news`, `news_i18n` WHERE news_i18n.CULTURE='en' AND news.ID=news_i18n.ID
ORDER BY news.PUBLISHED_AT DESC LIMIT 10

После изучения принципов работы sfPropelPager выяснилось следующее:
1) Как в setPeerCountMethod указать правильный метод, которого кстати нет, чтобы выборка количества осуществлялась с учётом языка?
2) Как задать язык для формирования пейджинга? В оригинальном BaseNewsPeer.php метод doSelectWithI18n имеет следующий вид: public static function doSelectWithI18n(Criteria $c, $culture = null, PropelPDO $con = null). Тут есть $culture, а sfPropelPager никак не переназначает значение, значит, срабатывает следующий код:

    if ($culture === null)
    {
      $culture = sfPropel::getDefaultCulture();
    }

В итоге устанавливается язык по умолчанию.

В связи с эти был написан класс, который позволяет решить данную проблему. За основу была взята идея sfPropelPager with I18n и доработана до нормального состояния.
Мой класс, который можно скачать sfPropelPagerI18n. Файл надо положить в директорию lib.

<?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 'ID'
    * @return sfPropelPagerI18n
    */
  public function __construct($class, $maxPerPage = 10, $culture = null, $joinField = 'ID')
  {
    if ($culture === null)
    {
      $culture = sfPropel::getDefaultCulture();
    }

    $this->joinField       = $joinField;
    $this->currentCulture  = $culture;

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

    $this->setPeerMethod('doSelectWithI18n');
  }

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

    $this->criteria->addJoin(
      constant($this->getClass() . 'Peer::' . $this->joinField),
      constant($this->getClass() . 'I18nPeer::' . $this->joinField),
      Criteria::INNER_JOIN
    );
    $c->add(constant($this->getClass() . 'I18nPeer::CULTURE'), $this->currentCulture);
  }

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

    return call_user_func(array($this->getClassPeer(), $this->getPeerMethod()), $c, $this->currentCulture);
  }
}

Пример использования sfPropelPagerI18n:

    $c = new Criteria();
    $c->addDescendingOrderByColumn(NewsPeer::PUBLISHED_AT);
    $c->add(NewsPeer::IS_PUBLISHED, true, Criteria::EQUAL);
    // Создаем новый Pager вместо старого sfPropelPager, и указываем язык
    $pager = new sfPropelPagerI18n('News', 1, 'ru');
    $pager->setPage($this->getRequestParameter('page', 1));
    // Критерий тоже новый
    $pager->setCriteriaI18n($c);
    $pager->init();
    $this->pager = $pager;

Что же позволяет делать класс sfPropelPagerI18n:
1) Автоматически формируется запрос типа doCount с учётом i18n и всех дополнительных условий Criteria.
2) Указать свою culture или учитывается текущая.
3) Указать поле для связи таблицы, по умолчанию ‘ID’, но его можно переназначить.

Ссылки по теме:
symfony API: sfPropelPager Class
symfony API: sfPager Class
http://www.symfony-project.org/forum/index.php/t/14991/
http://groups.google.fr/group/symfony-users/browse_thread/thread/413beca4468e57e2
http://groups.google.es/group/symfony-es/browse_thread/thread/e8f00368d629649a

Tags: ,

2 Responses to “sfPropelPagerI18n: расширяем sfPropelPager для поддержки i18n”

  1. Александр says:

    Круто! Спасибо за i18n класс! :)

  2. ingvar says:

    😉 Там много просветов на счет работы с мультиязычностью, что можем расширяем

Leave a Reply