I want to share my experiences regarding fixtures and table prefixes as I think it might be helpful for others.
For the Post model I have a table named ‘tbl_post’ and the prefix configured as ‘tbl_’. To make it work with fixtures I defined the following fixture in my test file:
protected $fixtures = array(
'{{post}}' => 'Post',
);
As mentioned in previous posts, the fixture file needs to be named to the full table name: ‘tbl_post.php’. The file looks like this:
return array(
'sample1' => array(
'title' => 'post1',
'text' => 'post1',
),
'sample2' => array(
'title' => 'post2',
'text' => 'post2',
),
);
This all worked well but then I wanted to refer to the fixture data in the test cases. Normally you would be able to use the following code to refer to the fixture AR instance with alias ‘sample1’:
$this->post('sample1');
However, this doesn’t work when using a prefix and ‘{{post}}’ as the fixure name. In order to resolve this I wrote a class DbTestCase that extends CDbTestCase and overrides the PHP magic methods __get and __call. This is the result:
/**
* Class extends from CDbTestCase to include support for fixture data getters
* with prefixed table names.
*/
class DbTestCase extends CDbTestCase {
/**
* Sets up before each test method runs.
*/
protected function setUp() {
parent::setUp();
}
/**
* PHP magic method.
* This method is overridden so that named fixture data can be accessed like
* a normal property even when a table prefix is used.
* @param string $name the property name
* @throws Exception if unknown property is used
* @return mixed the property value
*/
public function __get($name) {
if (is_array($this->fixtures) && ($rows = $this->getFixtureManager()->getRows($name)) !== false) {
return $rows;
} else if (Yii::app()->db->tablePrefix !== null) {
$name = '{{' . $name . '}}';
if (is_array($this->fixtures) && ($rows = $this->getFixtureManager()->getRows($name)) !== false) {
return $rows;
}
} else {
throw new Exception("Unknown property '$name' for class '" . get_class($this) . "'.");
}
}
/**
* PHP magic method.
* This method is overridden so that named fixture ActiveRecord instances
* can be accessed in terms of a method call even when a table prefix is used.
* @param string $name method name
* @param string $params method parameters
* @throws Exception if unknown method is used
* @return mixed the property value
*/
public function __call($name, $params) {
if (is_array($this->fixtures) && isset($params[0]) && ($record = $this->getFixtureManager()->getRecord($name, $params[0])) !== false) {
return $record;
} else if (Yii::app()->db->tablePrefix !== null) {
$name = '{{' . $name . '}}';
if (is_array($this->fixtures) && isset($params[0]) && ($record = $this->getFixtureManager()->getRecord($name, $params[0])) !== false) {
return $record;
}
} else {
throw new Exception("Unknown method '$name' for class '" . get_class($this) . "'.");
}
}
}
Extending my test case classes from this new class DbTestCase, I was able to refer to fixture data:
class PostTest extends DbTestCase {
protected $fixtures = array(
'{{post}}' => 'Post',
);
function testDelete() {
$post = $this->post('sample1');
$post->delete();
$this->assertNull(Post::model()->findByPk($post->id));
}
}
I hope this can be helpful for anyone that is using prefixes and fixtures. The only thing I’m still looking at is making the fixture file name independent from the prefix. Maybe someone has a good suggestion?