5
Май

Doctrine 1.2: сортировка связей

Чтобы связанные объекты выводились в определенном нами порядке, при описании модели можно указать поле по которому будет вестись сортировка.

Пример:

Image:
  columns:
    # .... необходимые поля
    pos: { type: integer, notnull: true, default: 0}  # поле по которому будет вестись сортировка
    product_id: {type: integer}

Product:
   # .... необходимые поля
  relations:
    Images:
      type: many
      class: Image
      local: id
      foreign: product_id
      onDelete: 'SET NULL'
      orderBy: 'pos ASC' # <- указываем по какому полю сортировать

Данная заметка касается версии Doctrine 1.2. Как я знаю в более старых версиях, подобное приходилось решать самостоятельно правкой классов в дистрибутиве Doctrine.

27
Апр

Вышла стабильная версия Propel 1.5

Propel
Хорошая новость для разработчиков использующих Propel, вышла стабильная версия Propel 1.5!!!
Кто не знает – это ORM для PHP (http://www.propelorm.org/).
Изменений в данной версии очень много, но как обещают разработчики, не надо этого пугаться, так как остается совместимость с версиями 1.4, 1.3.
Для апгрейда надо всего лишь перегенерировать модель и начать использовать новые возможности Propel.

Что нового, смотрим здесь http://www.propelorm.org/wiki/Documentation/1.5/WhatsNew.

Удачи!

29
Мар

Symfony: Фильтры формы – выпадающий список для текстового поля

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


// SomeModelFormFilter.class.php
// ...
  public function configure()
  {
    $this->setWidget('status', new sfWidgetFormChoice(
      array('choices'=>self::getStatuses())
    ));

    $this->setValidator('status', new sfValidatorChoice(array(
      'choices'=>array_keys(self::getStatuses()),
      'required'=>false
    )));
  }

  public static function getStatuses()
  {
     return array(
         '' => '',
         'one'=>'Один',
         'two'=>'Два',
         'three'=>'Три'
     );
  }
//...

Оказалось, что хоть выпадающий список появляется, фильтрация не происходит.
Немного покопавшись, нашел такое решение: надо для нашего поля status, указать тип «ForeignKey».
Делаем это перекрываем родительский метод «getFields()».


// SomeModelFormFilter.class.php
// ...
  public function getFields()
  {
    $fields = parent::getFields();
    $fields['status']='ForeignKey';

    return $fields;
  }
// ...

Все, теперь работает.

17
Фев

phpBB 4.0 & Symfony 2.0

Отличная новость прилетела! Говорят, что phpBB 4.0 будет написана с использованием Symfony 2.0

18
Янв

Некоторые особенности CSRF Protection в symfony

В данной заметке я привожу некоторую ситуацию, с которой я столкнулся при работе с формами в замечательном PHP фреймворке Symfony. В частности, проблема возникала при включенной защите от межсайтовых запросов (CSRF Protection), что это за защита вы можете прочитать в википедии (http://ru.wikipedia.org/wiki/CSRF, http://www.inattack.ru/article/552.html).

В симфонии при включенной CSRF защите в форму подставляется скрытое поле с именем _csrf_token, его значение формируется как md5 хеш от секретной строки, имени класса и идентификатора сессии (session_id).
Пример формирования значения токена в Symfony:

  // sfForm.class.php
  public function getCSRFToken($secret = null)
  {
    ....
    return md5($secret.session_id().get_class($this));
  }

Следовательно, если после некоторого действия, значение возвращаемое session_id() меняется, то дальнейшая валидация созданных до этого момента форм, не будет корректно обрабатываться.
Такие случаи могут возникать, например, при авторизации пользователя (sfGuardPlugin) и дальнейшей обработке форм в одном запросе.
Пример, у нас есть две формы c полями:
1. sfGuardFormSignin: signin[username], signin[password]
2. AddressForm: address[phone],address[city],… -

Мы хотим в одном запросе авторизовать пользователя с помощью логина пароля и сохранить обязательные поля из формы address
Делаем примерно так:

$this->auth_form = !$this->getUser()->isAuthenticated() ? new sfGuardFormSignin() : null;
$this->address_form = new AddressForm();
if ($request->isMethod('post'))
{
  // авторизуемся
  if (!$this->getUser()->isAuthenticated())
  {
    $this->auth_form->bind($request->getParameter('signin'));
    if ($this->auth_form->isValid())
    {
      $values = $this->auth_form->getValues();

      $this->getUser()->signin($values['user'], array_key_exists('remember', $values) ? $values['remember'] : false);
      $this->auth_form=null;
    }
  }

  // обрабатываем форму адреса
  $this->address_form->bind($request->getParameter('address'));
  if ($this->address_form->isValid())
  {
    // что-то делаем с формой, например сохраняем
    $this->address_form->save();
    //...

    $this->redirect('@somepath');
  }

}

Допустим, авторизация прошла успешно, но валидация формы адреса не прошла. Тогда нам покажется форма адреса с ошибками, но исправив ошибки мы все равно получим не валидную форму так как session_id изменился, а форма адреса создавалась с учетом старого его значения и
нам будет в любом случае выдавать ошибку «csrf token: Required.».
Как избежать подобного?
Способ который я применил (на мой взгляд не очень красивый) заключается в следующем: нужно после авторизации поставить в сессию атрибут
о временном отключении «CSRF Protetion» перед обработкой форм проверять данный атрибут и отключать защиту CSRFT.
Пример:

$this->auth_form = !$this->getUser()->isAuthenticated() ? new sfGuardFormSignin() : null;
$this->address_form = new AddressForm();
if ($request->isMethod('post'))
{

  // Авторизация
  if (!$this->getUser()->isAuthenticated())
  {
    $this->auth_form->bind($request->getParameter('signin'));
    if ($this->auth_form->isValid())
    {
      $values = $this->auth_form->getValues();

      $this->getUser()->signin($values['user'], array_key_exists('remember', $values) ? $values['remember'] : false); // <<< здесь меняется session_id
      $this->getUser()->setAttribute('disable_csrf',true); // <<< снимаем защиту CSRF
      $this->auth_form=null;
    }
  }

  // Отключаем защиту если требуется
  if ($this->getUser()->getAttribute('disable_csrf',false))
  {
    sfForm::disableCSRFProtection();
    unset ($this->address_form[sfForm::getCSRFFieldName()]);
  }

  // обработка формы адреса
  $this->address_form->bind($request->getParameter('address'));

  if ($this->address_form->isValid())
  {

    // что-то делаем с формой, например сохраняем
    $this->address_form->save();
    //...

    $this->getUser()->setAttribute('disable_csrf',null); // если все хорошо включаем защиту CSRF обратно
    $this->redirect('@somepath');
  }

}

Все вышеприведенное тестировалось на версии symfony 1.2, в этой версии CSRF защиту можно отключить только глобально. В более новых версиях фреймворка (1.3, 1.4), появилась возможность отключать защиту локально для конкретной формы по отдельности, что более правильно.
З.Ы. Если кто-то скажет, как подобное можно более красиво решить, буду очень благодарен :)

2
Дек

sfYandexYMLPlugin – генерация YML для Яндекс.Маркета

Выложил еще один плагин, который позволяет упростить генерацию XML для сервиса Яндекс.Маркет.
В плагин входят несколько классов, которые предоставляют объектный интерфейс для генерации YML файла.
Описание формата YML можно посмотреть здесь http://partner.market.yandex.ru/legal/tt/

В данный момент, не все еще возможности реализованы.
Например, по товарным предложениям, реализовано только самое простое предложение (offer).

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

Взять плагин сейчас возможно только из SVN.

Страница плагина: http://www.symfony-project.org/plugins/sfYandexYMLPlugin
SVN: http://svn.symfony-project.com/plugins/sfYandexYMLPlugin/trunk/

25
Ноя

Интернет-магазин на symfony

logo Некоторое время назад, создал очень интересный проект – Интернет-магазин оригинальных подарков Expetro.Ru.
Магазин делался с нуля, написан на PHP с использованием фреймворка Symfony, Propel, JQuery.
Есть интеграция с 1С, экспорт в Yandex YML.

Для сжатия Javascript скриптов, используется плагин sfGoogleClosureCompilerPlugin.

Сам магазин очень интересный, он существует как дочерний проект компании «Экспедиция» и в нем можно купить различные удивительные подарки.
Советую посмотреть подарки судьбы и обязательно остальные товары.

PS Сайт магазина еще в разработке, но функции свои выполняет.

12
Ноя

sfGoogleClosureCompilerPlugin – Мой первый плагин для Symfony

Первый плагин который я выложил в паблик доступ.
Сильно не пинать! :)
С английским у меня проблемы.
Если кто-то хочет помочь, буду очень рад.

Суть такая:
Подключается фильтр, который ищет на странице подключаемые js-скрипты, потом передает их через rest-api на сервер гугла, после чего сохраняет резултат и вставляет на него линк, старые ссылки из html удаляет.

http://www.symfony-project.org/plugins/sfGoogleClosureCompilerPlugin

28
Авг

Развертывание проектов используя Capistrano

Давно хотел попробовать для деплоймента своих symfony проектов, систему Capistrano.
На хабре есть пара статей про него: