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

Posted

技术标签:

【中文标题】Doctrine 1.2 保存记录关系 UPDATE 而不是 INSERT【英文标题】:Doctrine 1.2 save Record relations UPDATE rather than INSERT 【发布时间】:2011-07-13 19:19:09 【问题描述】:

我正在尝试级联保存一些用户:

 $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_Connection_mysql_Exception: SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens

这不是 Doctrine 应该提供的开箱即用的东西吗?我在这里错过了什么吗?为什么我需要手动实现此行为?


补充说明

我们有 3 种情况决定记录是应该插入、更新还是简单关联:

1

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

2

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

3

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

我们需要找到最有效的方法来做到这一点。显然我们可以在 preSave() 等中进行大量选择,我们只需要充分利用 Doctrine

【问题讨论】:

您确定位置表中“id”上的主键存在并且在模型类中正确写入了吗?你能提供模型类的代码吗?需要带有表格和关系描述的部分。 $this->hasColumn('location_id', 'integer', 8, array('type' => 'integer', 'length' => 8, 'fixed' => false, '无符号' => false, 'primary' => true, 'autoincrement' => false, )); 您在代码中引用字段“id”,而不是字段“location_id” - 为什么它应该工作? 【参考方案1】:

通常我会这样做:

$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 钩子用于此类事情,我认为它们应该用于其他用例。

【讨论】:

这是我们前进的方向。一旦我们到达那里,我会给出完整的答案。这个答案当然有帮助。谢谢。【参考方案2】:

也许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;

再见!

【讨论】:

以上是关于Doctrine 1.2 保存记录关系 UPDATE 而不是 INSERT的主要内容,如果未能解决你的问题,请参考以下文章

Doctrine 1.2 hasOne 覆盖 hasMany 关系

Doctrine 1.2 多对多关系的列命名约定

如何检索包含所有关系记录的 Doctrine 记录?

教义 1.2:定义关系的条件

Symfony2-Doctrine:ManyToMany 关系未保存到数据库

Symfony2 2.3.7 -Doctrine 2.4.1:ManyToMany 关系未保存到数据库