29
Мар
Symfony: Фильтры формы – выпадающий список для текстового поля
Запостил Jeka, в раздел Программирование
Так получилось, что у одной модели имеется текстовое поле с не очень большим количеством вариантов значения. По идее можно сделать по правильному и вывести все в отдельную таблицу, но по некоторым причинам этого не делается.
Следовательно в админке при фильтрации, не очень охота писать значения этого поля ручками, легко можно сделать выпадающий список со всеми вариантами.
Сначала сделал так:
// 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;
}
// ...
Все, теперь работает.
18
Янв
Некоторые особенности CSRF Protection в symfony
Запостил Jeka, в раздел Программирование
В данной заметке я привожу некоторую ситуацию, с которой я столкнулся при работе с формами в замечательном 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), появилась возможность отключать защиту локально для конкретной формы по отдельности, что более правильно.
З.Ы. Если кто-то скажет, как подобное можно более красиво решить, буду очень благодарен
6
Фев
Symfony Forms I18n изменение поведения
Запостил Jeka, в раздел Программирование
Появилась задача унифицировать поведение всех форм, конкретней, чтобы в многоязычной форме автоматически устанавливалась текущая культура пользователя. Для красоты еще добавил иконку флага как идентификатор культуры.
Все формы у нас наследуются от BaseFormPropel, следовательно открываем файл в нашем проекте $project/lib/form/BaseFormPropel.class.php и в методе класса setup(), пишем нужный нам код.
abstract class BaseFormPropel extends sfFormPropel
{
public function setup()
{
if ($this->isI18n())
{
$culture = sfContext::getInstance()->getUser()->getCulture();
$this->embedI18n(array($culture));
$icons = sfConfig::get('app_site_language_icons'); // get icon array
$this->widgetSchema->setLabel($culture, '<img src="'.$icons[$culture].'" width="24" height="24" alt="" />');
}
}
}
В файле app.yml у меня хранятся соответствия названий культур и иконок флажков.
all:
site:
language_icons:
ru: /images/flags/24/ru.png
en: /images/flags/24/en.png
de: /images/flags/24/de.png
В итоге получаем вот такой вид формы:

Последние записи:
- 03 Ноя Что я использую в работе...
- 09 Сен Анонс: Конференция Symfony Camp ...
- 24 Авг Особенности whereIn в Doctrine...
- 16 Авг PHP, получение контекста в $this...
- 30 Июн Поездочка в Казань, Чистополь и ...
- 10 мая Вакансия: Разыскиваются талантли...
- 20 Апр WideImage – манипуляция из...
- 08 Апр prettyPhoto – отличная зам...
- 06 Апр Предпринимательство в России...
- 13 Янв Apostrophe CMS: Получить значени...
Разделы:
- Администрирование (2)
- Новости (45)
- Программирование (29)
- Управление проектами (1)
Архивы:
- Ноябрь 2011
- Сентябрь 2011
- Август 2011
- Июнь 2011
- Май 2011
- Апрель 2011
- Январь 2011
- Ноябрь 2010
- Октябрь 2010
- Сентябрь 2010
- Август 2010
- Июль 2010
- Май 2010
- Апрель 2010
- Март 2010
- Февраль 2010
- Январь 2010
- Декабрь 2009
- Ноябрь 2009
- Сентябрь 2009
- Август 2009
- Июль 2009
- Июнь 2009
- Май 2009
- Апрель 2009
- Март 2009
- Февраль 2009
- Январь 2009
- Ноябрь 2008
- Октябрь 2008
- Сентябрь 2008
- Август 2008