CActiveRecord::save() issue

So i was programming away and ran into an issue where an object is created, saved, updated and saved again. I had the problem in full class, but whittled it down to the following test case. this test code assumes Yii::app()->db points to an sqlite database.

For now I’m using the following workaround before saving since i don’t use the ability to change the primary key value.




$model->setPrimaryKey($model->id);



Seems perhaps some logic is needed when setting the oldPrimaryKey on insert




Yii::app()->db->createCommand('CREATE TEMP TABLE IF NOT EXISTS activeRecordTest ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,title TEXT )')->execute();

class someModel extends CActiveRecord {

  public function tableName() { return 'activeRecordTest'; }

}


class activeRecordTest extends CTestCase

{


  public function testSaveTwice()

  {

    $record = new someModel;

    $record->title = 'The Title';

    $this->assertTrue($record->save(), 'initial save');


    $record->title = 'The Name';

    $this->assertTrue($record->save(), 'second save');

    $this->assertEquals('The Name', $record->title, 'before refresh');

    $this->assertTrue($record->refresh(), 'refresh');

    $this->assertEquals('The Name', $record->title, 'after refresh');

  }

}



phpunit reports




There was 1 failure:


1) activeRecordTest::testSaveTwice

after refresh

Failed asserting that two strings are equal.

--- Expected

+++ Actual

@@ @@

-The Name

+The Title


/var/www/nmtdvr/protected/tests/unit/activeRecordTest.php:21


FAILURES!

Tests: 1, Assertions: 5, Failures: 1.



the relevant logging is




2010/02/23 22:19:23 [trace] [system.web.CModule] Loading "log" application component

2010/02/23 22:19:23 [trace] [system.web.CModule] Loading "db" application component

2010/02/23 22:19:23 [trace] [system.db.CDbConnection] Opening DB connection

2010/02/23 22:19:23 [trace] [system.db.CDbCommand] Executing SQL: CREATE TEMP TABLE IF NOT EXISTS activeRecordTest ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,title TEXT,name TEXT )

2010/02/23 22:19:23 [trace] [system.db.CDbCommand] Querying SQL: PRAGMA table_info('activeRecordTest')

2010/02/23 22:19:23 [trace] [system.db.CDbCommand] Querying SQL: PRAGMA foreign_key_list('activeRecordTest')

2010/02/23 22:19:23 [trace] [system.db.ar.CActiveRecord] someModel.insert()

2010/02/23 22:19:23 [trace] [system.db.CDbCommand] Executing SQL: INSERT INTO 'activeRecordTest' ("title") VALUES (:yp0). Bind with parameter :yp0='The Title'

2010/02/23 22:19:23 [trace] [system.db.ar.CActiveRecord] someModel.update()

2010/02/23 22:19:23 [trace] [system.db.ar.CActiveRecord] someModel.updateByPk()

2010/02/23 22:19:23 [trace] [system.db.CDbCommand] Executing SQL: UPDATE 'activeRecordTest' SET "title"=:yp0, "id"=:yp1 WHERE 'activeRecordTest'."id" IS NULL. Bind with parameter :yp0='The Name', :yp1=1

2010/02/23 22:19:23 [trace] [system.db.ar.CActiveRecord] someModel.refresh()

2010/02/23 22:19:23 [trace] [system.db.ar.CActiveRecord] someModel.findByPk()

2010/02/23 22:19:23 [trace] [system.db.CDbCommand] Querying SQL: SELECT * FROM 'activeRecordTest' 't' WHERE 't'."id"=1 LIMIT 1



journey4712

Just a bump here, is this me mis-using CActiveRecord, a bug in CActiveRecord, or ? Curious to hear opinions. As for a workarround i’ve found using $model->setPrimaryKey($model->id) will fix up CActiveRecord->$_pk being null.

journey4712

Problem is that update() uses getOldPrimaryKey() and in this case the oldPrimaryKey ($this->_pk in CActiveRecord.php) is not set…

Maybe the refresh() method should update the oldPrimaryKey.

As it says the refresh() repopulates the active record with the latest data, so the next update should use the new values so it should use the new primary key… and for update() to use it the only way is to set the getOldPrimaryKey()…

I’m curious, why would you be updating the same record multiple times in the same request?

While you have seemed to verify that this is a bug in the operation of ActiveRecord (as it’s not updating the old pk value), it also seems that your own code isn’t very optimal, if it’s creating and updating a single record in the same request.

To avoid this issue, I would refactor your code so that you only create or update once per request, unless your situation truly justifies your approach.

Additionally, if you don’t mind, would you report this issue as a bug?

http://code.google.com/p/yii/issues/entry

The static method model() is required for every AR class.

So


class someModel extends CActiveRecord {

  public function tableName() { return 'activeRecordTest'; }

}



should be like


class someModel extends CActiveRecord {


  public static function model($className=__CLASS__) {

        return parent::model($className);

  }

  public function tableName() { return 'activeRecordTest'; }

}