В данной заметке я расскажу об использованном мной способе добавления к sfGuardUser связи many-to-many.
Начальные условия.
Имеется symfony 1.4 проект с установленным sfDoctrineGuardPlugin.
Цели.
Требуется реализовать для пользователя список ежедневных задач (памятка).
Решение.
Схема.
Создадим в БД следующие таблицы:
# таблица ежедневных задач CREATE TABLE IF NOT EXISTS `daily_task` ( `id` int(11) NOT NULL AUTO_INCREMENT, `daily_task_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1; # таблица связей many-to-many задач и sfGuardUser CREATE TABLE IF NOT EXISTS `user_has_daily_task` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL, `daily_task_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `user_id_idx` (`user_id`), KEY `daily_task_id_idx` (`daily_task_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1; # внешние ключи для связок ALTER TABLE `user_has_daily_task` ADD CONSTRAINT `daily_task_id_idx` FOREIGN KEY (`daily_task_id`) REFERENCES `daily_task` (`id`), ADD CONSTRAINT `user_id_idx` FOREIGN KEY (`user_id`) REFERENCES `sf_guard_user` (`id`);
Этой структуре соответствует следующая схема Doctrine:
# схема для таблицы daily_task
DailyTask:
connection: doctrine
tableName: daily_task
columns:
id:
type: integer(4)
fixed: false
unsigned: false
primary: true
autoincrement: true
daily_task_name:
type: string(255)
fixed: false
unsigned: false
primary: false
notnull: true
autoincrement: false
relations:
UserHasDailyTask:
local: id
foreign: daily_task_id
type: many
onDelete: NO ACTION
onUpdate: NO ACTION
# схема для таблицы user_has_daily_task
UserHasDailyTask:
connection: doctrine
tableName: user_has_daily_task
columns:
id:
type: integer(4)
fixed: false
unsigned: false
primary: true
autoincrement: true
user_id:
type: integer(4)
fixed: false
unsigned: false
primary: false
notnull: true
autoincrement: false
daily_task_id:
type: integer(4)
fixed: false
unsigned: false
primary: false
notnull: true
autoincrement: false
relations:
sfGuardUser:
local: user_id
foreign: id
type: one
onDelete: NO ACTION
onUpdate: NO ACTION
DailyTask:
local: daily_task_id
foreign: id
type: one
onDelete: NO ACTION
onUpdate: NO ACTION
Выполняем пересоздание классов Doctrine (модели, формы, фильтры) и очищаем кэш:
$ ./symfony doctrine:build --all-classes $ ./symfony cache:clear
Подготовительную часть на этом можно считать завершенной.
Админ-генератор sfGuardUser
Теперь на уровне БД у нас есть привязка ежедневных задач к пользователю sfGuardUser. Для редактирования пользователя sfGuard предусматривает одноименный модуль sfGuardUser с админ-генератором. По-умолчанию этот модуль позволяет редактировать имя, пароль пользователя, а также группы и список прав доступа. Вот они то нас и интересуют. Почему? Потому что эти параметры также представляют из себя связки many-to-many, а значит, если мы хотим добавить ежедневные задачи в редактирование пользователя, то сделать это логично по образу и подобию.
Модель sfGuardUser
Для того чтобы sfGuardUser корректно понимал нашу новую связь, необходимо модифицировать сгенерированный класс его модели следующим образом:
# lib/model/doctrine/sfDoctrineGuardPlugin/sfGuardUser.class.php
class sfGuardUser extends PluginsfGuardUser
{
public function setUp()
{
parent::setUp();
$this->hasMany('DailyTask', array(
'refClass' => 'UserHasDailyTask',
'local' => 'user_id',
'foreign' => 'daily_task_id'
));
}
}
Отлично, теперь модель пользователя знает о новой связке ) Теперь можно заняться формой
Форма sfGuardUserForm
Теперь мы можем модифицировать форму sfGuardUserForm. Для начала добавим виджет для нашего нового поля (виджет назовем daily_tasks_list):
# lib/form/doctrine/sfDoctrineGuardPlugin/sfGuardUserForm.class.php
class sfGuardUserForm extends BasesfGuardUserAdminForm
{
public function configure()
{
$this->widgetSchema['daily_tasks_list'] = new sfWidgetFormDoctrineChoice(array(
'multiple' => true,
'model' => 'DailyTask'
));
$this->validatorSchema['daily_tasks_list'] = new sfValidatorDoctrineChoice(array(
'multiple' => true,
'model' => 'DailyTask',
'required' => false
));
}
Тут желательно еще раз пересобрать все классы и очистить кэш.
Теперь при редактировании пользователя, вы уже сможете увидеть наш новый виджет. Но он еще не пригоден к использованию. Т.е. то что вы выберете, не будет сохранено.
Сохранение новой связки
Небольшое лирическое отступление, написанное по здравому размышлению. Если у вас виджет называется точно также как таблица +
_list(в нашем случае это будетdaily_task_list), то, скорее всего вы уже готовы использовать форму по полной. Я же назвал виджет не так как таблицу (в статье еще опущен префикс таблицы) и поимел последующий геморрой, который все-таки считаю нужным описать полностью.
Вот тут нам пригодится аналогия с группами и правами доступа. Если покопошиться в исходниках плагина, выяснится, что для сохранения связей, реализовано два дополнительных метода – savegroupsList и savepermissionsList. Т.о. мы реализуем наш метод savedailytasksList и переопределяем метод doSave():
# lib/form/doctrine/sfDoctrineGuardPlugin/sfGuardUserForm.class.php
class sfGuardUserForm extends BasesfGuardUserAdminForm
{
//...
/**
* Сохранение списка ежедневных задач
*/
public function savedailytasksList($con = null)
{
if (!$this->isValid())
{
throw $this->getErrorSchema();
}
if (!isset($this->widgetSchema['daily_tasks_list']))
{
// somebody has unset this widget
return;
}
if (null === $con)
{
$con = $this->getConnection();
}
$existing = $this->object->DailyTask->getPrimaryKeys();
$values = $this->getValue('daily_tasks_list');
if (!is_array($values))
{
$values = array();
}
$unlink = array_diff($existing, $values);
if (count($unlink))
{
$this->object->unlink('DailyTask', array_values($unlink));
}
$link = array_diff($values, $existing);
if (count($link))
{
$this->object->link('DailyTask', array_values($link), true);
}
}
/**
* Сохранение формы
*/
protected function doSave($con = null)
{
$this->savedailytasksList($con);
parent::doSave($con);
}
}
Ура! Теперь мы можем сохранять наши задачи! )) Остался один маленький заключительный штришок – сохранить то мы сохраняем, но при повторном редактировании пользователя сохраненные связи не отображаются. Для этого переопределим метод определения значения по-умолчанию для виджета:
# lib/form/doctrine/sfDoctrineGuardPlugin/sfGuardUserForm.class.php
class sfGuardUserForm extends BasesfGuardUserAdminForm
{
//...
/**
* Значение по-умолчанию для виджета ежедневных задач
*/
public function updateDefaultsFromObject()
{
parent::updateDefaultsFromObject();
if (isset($this->widgetSchema['daily_tasks_list']))
{
$this->setDefault('daily_tasks_list', $this->object->DailyTask->getPrimaryKeys());
}
}
}
Ну вот, кажется и все! Пользуйтесь на здоровье )) И ставьте “плюсики”, если понравилось! ))
One Comment
+спасибо, то что искал
One Trackback
[...] оригинал Symfony ← Unescape для User flash message в symfony LikeBe the first to like this post. [...]