简体   繁体   中英

Doctrine 1.2 save Record relations UPDATE rather than INSERT

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

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.

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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM