Доктрина 1.2 сохранить запись отношений ОБНОВЛЕНИЕ, а не ВСТАВИТЬ - PullRequest
2 голосов
/ 13 июля 2011

Я пытаюсь сохранить в каскаде некоторых пользователей:

 $user = new User();
 $user->name = 'xxx';
 $user->location->id = 1;
 $user->location->name = 'yyy';
 $user->save;

 $user2 = new User();
 $user2->name = 'zzz';
 $user2->location->id = 1;
 $user2->location->name = 'yyy';
 $user2->location->zip = '123456';
 $user2->save;

В этой ситуации я хочу, чтобы Doctrine был достаточно умен и обновил местоположение 1, так как я изменяю содержимое для идентификатора 1, но чтоУ меня вместо этого есть еще одна вставка.Я попытался обойти, используя метод preSave () внутри User:

public function preSave( Doctrine_Event $event )
{
    $invoker = $event->getInvoker();
    if ( /...decide to UPDATE the record .../ )
    {
        $invoker->state( Doctrine_Record::STATE_DIRTY );
    }
    else
    {
        $invoker->state( Doctrine_Record::STATE_CLEAN );
    }
}

, но когда доктрина пытается обновить, он не имеет идентификатора и выдает эту ошибку:

Doctrine_Connection_Mysql_Exception: SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens

Isn 'Это что-то, что Docrtrine должен предоставить из коробки?Я что-то здесь упускаю?Почему мне нужно реализовать это поведение вручную?


Дополнительные примечания

У нас есть 3 случая, которые определяют, должна ли запись быть вставлена, обновлена ​​или просто связана:

1

  • Новый пользователь в нашей базе данных - должен быть добавлен
  • Новый адрес в нашей базе данных - должен быть вставлен

2

  • Новый пользователь в нашей базе данных - должен быть добавлен
  • Существующее местоположение - должен быть связан с записью пользователя

3

  • Новый пользователь в нашей базе данных - должен быть вставлен
  • Существующий идентификатор местоположения, обновленные данные - должен быть обновлен и связан с записью пользователя

Нам нужно найти наиболее эффективный способсделать это.Очевидно, что мы можем сделать множество выборок в preSave () и т. Д., Нам просто нужно максимально использовать Doctrine

Ответы [ 2 ]

6 голосов
/ 20 июля 2011

Обычно я бы делал что-то вроде этого:

$location = new Location();
$location->name = 'yyy';
$location->save(); // this will assign location id using autoincrement

// or alternatively if you have generated table classes
// $location = LocationTable::getInstance()->create(array('name' => 'yyy');
// or if you have an already existing location
// $location = LocationTable::getInstance()->find($location_id);
// keep in mind that if you haven't generated any table class you should replace
// LocationTable::getInstance() with Doctrine_Core::getTable('Location');

$user = new User();
$user->name = 'xxx';
$user->location = $location;
// or $user->Location = $location; // the case of the l depends on how you have declared the model relationships
$user->save;

$user2 = new User();
$user2->name = 'zzz';
$user2->location = $location;
$user2->save;

Вообще говоря, в Doctrine есть много удобных методов для работы с отношениями, правильный выбор зависит от ваших конкретных потребностей.Например, вы должны указать, как строится ваш объект местоположения, сколько пользовательских экземпляров у вас в одном и том же коде, если у вас есть идентификатор местоположения или данные местоположения и т. Д.

Для пунктов 1,2 и 3в rails я бы использовал метод find_or_create_by, который недоступен в Doctrine, но вы всегда можете написать его самостоятельно.Поэтому, если у вас есть класс LocationTable, вы можете сделать это:

// in LocationTable class
public function findOrCreateBy($fieldName, $value, array $data = array())
{
    if (!$record = $this->findBy($fieldName, $value)) {
        // record doesn't exist, create it with provided data
        $record = $this->create(array($fieldName => $value));
    }
    // update record data
    $record->fromArray($data);
    // optionally save the record, depend on your needs
    $record->save(); // it won't trigger actual save if record fields aren't updated
    return $record;
}


// then in your example code you could fetch the location code with
$location = LocationTable::getInstance()
    ->findOrCreateBy('name', 'yyyy', array('field_to_update' => 'new value'));

Не используйте preSave-хуки для такого рода вещей, я думаю, что их следует использовать для других случаев использования.

0 голосов
/ 20 июля 2011

Может быть это поможет

 $user = new User();
 $user->name = 'xxx';
 $user->location->id = 1;
 $user->location->name = 'yyy';
 $user->save;

 $user2 = new User();
 $user2->name = 'zzz';
 $user2->location->assignIdentifier(1);
 $user2->location->name = 'yyy';
 $user2->location->zip = '123456';
 $user2->save;

Bye!

...