Hi, sorry for reopening old subject, but I had kinda hard time doing this exact thing so I thougt I’ll share my solution.
Previously linked clean solution didn’t work for me either.
Unfortunately, the “ugly” solution didn’t work either, but with slight modification it did in the end.
First, I created init.php in my fixtures folder, with instructions from https://blogs.law.harvard.edu/acts/2012/05/09/yii-fixtures-with-foreign-keys/ (basically the same idea as mentioned here, you set order of the tables to do resets and cleanups in two separate cycles).
However, my tests still weren’t working since there are some cleanups going on before every single test method, and those are indepedent on the init script (and they always tried to do something like “delete posts, fill posts, delete users” -> error since there exists posts referencing the users table). This is apparently going on in a load() method of CDbFixtureManager, which was the important method for me to override (basically in the same way - sort the fixtures in a usable way, do one cycle deleting, and another one inserting).
When overriding the CDbFixtureManager, I ran into problems with missing getRows() method in my extended class, which returns private value of _rows (which I also had to redeclare, together with _records). I simply copied getRows() from CDbFixtureManager into my new class. Pretty ugly, but only way I managed it to work.
Think overriding core classes of the framework like this isn’t really the good way, and I’m not even sure I didn’t screw some other functionality (due to the private properties, I guess), but for now it seems it’s working. I’d definitely be glad if someone would give me a hint how to settle this in a better way though.
Here’s my CDbFixtureManager extension:
<?php
Yii::import('system.test.CDbFixtureManager');
/**
* Overriding default fixture manager in order to reset & load fixtures in specified order
*/
class FDbFixtureManager extends CDbFixtureManager {
/**
* @var array fixture name, row alias => row
*/
private $_rows;
/**
* @var array fixture name, row alias => record (or class name)
*/
private $_records; //
/**
* Loads fixtures in a specific order
* it's important to reset all tables first, and then fill them again in a specified order
*
* @param array array of fixtures to run
*/
public function load($fixtures) {
$fixtures = array_merge(array_flip(ORDERED_ARRAY), $fixtures);
$load_order = array_reverse($fixtures);
$schema = $this->getDbConnection()->getSchema();
$schema->checkIntegrity(false);
$this->_rows = array();
$this->_records = array();
foreach ($fixtures as $fixtureName => $tableName) {
if ($tableName[0] === ':') {
$tableName = substr($tableName, 1);
unset($modelClass);
} else {
$modelClass = Yii::import($tableName, true);
$tableName = CActiveRecord::model($modelClass)->tableName();
if (($prefix = $this->getDbConnection()->tablePrefix) !== null)
$tableName = preg_replace('/{{(.*?)}}/', $prefix . '\1', $tableName);
}
$this->resetTable($tableName);
}
foreach ($load_order as $fixtureName => $tableName) {
if ($tableName[0] === ':') {
$tableName = substr($tableName, 1);
unset($modelClass);
} else {
$modelClass = Yii::import($tableName, true);
$tableName = CActiveRecord::model($modelClass)->tableName();
if (($prefix = $this->getDbConnection()->tablePrefix) !== null)
$tableName = preg_replace('/{{(.*?)}}/', $prefix . '\1', $tableName);
}
$rows = $this->loadFixture($tableName);
if (is_array($rows) && is_string($fixtureName)) {
$this->_rows[$fixtureName] = $rows;
if (isset($modelClass)) {
foreach (array_keys($rows) as $alias)
$this->_records[$fixtureName][$alias] = $modelClass;
}
}
}
$schema->checkIntegrity(true);
}
/**
* Returns the fixture data rows.
* The rows will have updated primary key values if the primary key is auto-incremental.
*
* @param string $name the fixture name
* @return array the fixture data rows. False is returned if there is no such fixture data.
*/
public function getRows($name) {
if (isset($this->_rows[$name]))
return $this->_rows[$name];
else
return false;
}
}
?>