Doctrine 1.2保存记录关系UPDATE而不是INSERT

时间:2011-07-13 19:19:10

标签: php orm doctrine save doctrine-1.2

我试图以级联方式保存一些用户:

 $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,因为我正在更改id 1的内容,但我所拥有的是另一个插入。 我试图在User:

中使用preSave()方法解决方法
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尝试UPDATE时它没有标识符并产生这个错误:

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

这不是Docrtrine开箱即用的东西吗?我在这里错过了什么吗?为什么我需要手动实现这种行为?


进一步说明

我们有3个案例要求记录是应该插入,更新还是仅仅相关:

1

  • 应该插入我们数据库的全新用户
  • 应该插入我们数据库的全新位置

2

  • 应该插入我们数据库的全新用户
  • 现有位置 - 应链接到用户记录

3

  • 应该插入我们数据库的全新用户
  • 现有位置ID,更新数据 - 应更新并链接到用户记录

我们需要找到最有效的方法来实现这一目标。显然我们可以在preSave()等中做多个选择,我们只需要充分利用Doctrine

2 个答案:

答案 0 :(得分:6)

通常我会做这样的事情:

$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有很多方便的方法来处理关系,正确使用它取决于你的确切需求。例如,您应该指定您的位置对象的构建方式,相同代码中有多少个用户实例,如果您有位置ID或位置数据等等。

对于rails中的第1,2和3点,我使用了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挂钩用于此类事情,我认为它们应该用于其他用例。

答案 1 :(得分:0)

也许this会有所帮助

 $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;

再见!