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

发布于 2024-11-24 04:03:32 字数 1421 浏览 1 评论 0原文

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

 $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

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


进一步说明

我们有 3 种情况决定记录是否应插入、更新或简单相关:

1

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

2

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

3

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

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

I am trying to save in cascade some users:

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

In this situation I wish Doctrine to be smart enough and update the location 1 since I am changing the content for the id 1, but what I have instead is another insert.
I tried to workaround using a preSave() method inside 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 );
    }
}

but when doctrine tries to UPDATE it doesn't have identifier and produces this error:

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

Isn't this something that Docrtrine should provide out of the box? Am I missing something here? Why do I need to implement this behavior by hand?


Further Notes

We have 3 cases that dictate whether the record should be inserted, updated, or simply related:

1

  • Brand new user to our database - should be inserted
  • Brand new location to our database - should be inserted

2

  • Brand new user to our database - should be inserted
  • Existing location - should be linked to user record

3

  • Brand new user to our database - should be inserted
  • Existing location id, updated data - should be updated and linked to user record

We need to find the most efficient way to do this. Obviously we can do a multitude of selects in preSave() etc, we just need to get the most out of Doctrine

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

篱下浅笙歌 2024-12-01 04:03:32

通常我会这样做:

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

Normally I would do something like this:

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

Generally speaking Doctrine has a lot of convenient methods to deal with relations, the correct one to use depends on your exact needs. For example you should specify how your location object is built, how many user instances do you have in the same code, if you have a location id or location data and so on.

For point 1,2 and 3 in rails I'd use find_or_create_by method, which is not available in Doctrine, but you can always write it by yourself. So if you have the LocationTable class you can do this:

// 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'));

Don't use preSave hooks for this kind of things, I think they should be used for other use cases.

时光病人 2024-12-01 04:03:32

也许这个 将帮助

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

再见!

Maybe this will help

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文