2

Has anyone been able to to use Zend_Test_DbAdapter with Zend_Db_Table_Abstract?

I'm trying to test a Model I created that extends Zend_Db_Table_Abstract and I get an exception regarding the primary key not being set if I use a Zend_Test_DbAdapter (other adapters like mysql or sqlite) work fine.


class Model_Category extends Zend_Db_Table_Abstract
{
    protected $_name = 'categories';

    protected $_dependentTables = array('Model_Video');

    public function getMap()
    {
        $map = array();
        $rows = $this->fetchAll();
        foreach($rows as $row)
        {
            $map[$row->id] = $row->name;
        }

        return $map;
    }
}

Snippet from a PHPUnit test class:

protected function setUp()
{
    $adapter = new Zend_Test_DbAdapter();
    $stmt = Zend_Test_DbStatement::createSelectStatement(array(
        array('id' => 1, 'name' => 'pranks'),
        array('id' => 2, 'name' => 'physical_feats'),
        array('id' => 3, 'name' => 'art'),
        array('id' => 4, 'name' => 'cute'),
        array('id' => 5, 'name' => 'philanthropy')
    ));
    $adapter->appendStatementToStack($stmt);

    $this->fixture = new Model_Category($adapter);
}

Exceptions are thrown when exercising the Model's methods:

public function testGetMap()
{
    $expected = array(
        '1' => 'pranks',
        '2' => 'physical_feats',
        '3' => 'art',
        '4' => 'cute',
        '5' => 'philanthropy'
    );
    $actual = $this->fixture->getMap();
    $this->assertEquals($expected, $actual);
}

Results in:

Model_CategoryTest::testGetMap()
Zend_Db_Table_Exception: A table must have a primary key, but none was found
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:876
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:969
ZendFramework-1.10.6/library/Zend/Db/Table/Select.php:100
ZendFramework-1.10.6/library/Zend/Db/Table/Select.php:78
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:1005
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:1303
application/models/Category.php:35
tests/unit/application/models/CategoryTest.php:90

Forcing a primary key does not work either:

protected function setUp()
{
    $adapter = new Zend_Test_DbAdapter();
    $stmt = Zend_Test_DbStatement::createSelectStatement(array(
        array('id' => 1, 'name' => 'pranks'),
        array('id' => 2, 'name' => 'physical_feats'),
        array('id' => 3, 'name' => 'art'),
        array('id' => 4, 'name' => 'cute'),
        array('id' => 5, 'name' => 'philanthropy')
    ));
    $adapter->appendStatementToStack($stmt);

    $this->fixture = new Model_Category(array(
        'db' => $adapter,
        'primary' => 'id'
    ));
}

Executing the same unit test, from above results in:

Model_CategoryTest::testGetMap()
Zend_Db_Table_Exception: Primary key column(s) (id) are not columns in this table ()
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:888
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:969
ZendFramework-1.10.6/library/Zend/Db/Table/Select.php:100
ZendFramework-1.10.6/library/Zend/Db/Table/Select.php:78
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:1005
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:1303
application/models/Category.php:35
tests/unit/application/models/CategoryTest.php:93
Nick
  • 21
  • 5
  • Can you post the code for your model and what the stack trace of the exception is? – drew010 Oct 15 '12 at 19:26
  • I've added code and strack traces to my original post. I hope this helps.I should mention that the line numbers listed for Category.php and CategoryTest.php in the traces are wrong because I have condensed the source code for this post. – Nick Oct 17 '12 at 06:00
  • In both cases the trace is referring to "$rows = $this->fetchAll()" in Category.php, and "$actual = $this->fixture->getMap();" in CategoryTest.php. – Nick Oct 17 '12 at 06:06

2 Answers2

2

You can define the primary key by doing the following on your Zend_Test_DbAdapter instance:

$adapter = new Zend_Test_DbAdapter();
$adapter->setDescribeTable('table_name', array('column_name' =>
    array(
        'SCHEMA_NAME' => 'schema_name',
        'TABLE_NAME'  => 'table_name'
        'COLUMN_NAME' => 'column_name',     
        'PRIMARY'     => true
    )
));

And then transposing table_name, column_name and schema_name with the values from your implementation. You would need to do this for every table you are interacting with in the class under test.

Thys Swart
  • 21
  • 1
  • That solved part of the issue but exposed a new related problem. When Zend_Db_Adapter_Abstract's fetchPairs() method is called, $row[0] and $row[1] don't exist. That is because somehow $stmt->fetch(Zend_Db::FETCH_NUM) still returns an associative array, not a numeric array. – Nick Nov 14 '12 at 07:27
0

The reason you are getting the exception Zend_Db_Table_Exception: A table must have a primary key, but none was found is because all tables that use Zend_Db_Table must have a primary key defined. When you go to use the table, since a primary key was not defined in your DbTable class, Zend_Db attempts to determine the table's primary key by examining the table's properties from the information schema. It sees your table doesn't have a primary key and fails.

From the manual:

If you don't specify the primary key, Zend_Db_Table_Abstract tries to discover the primary key based on the information provided by the describeTable() method.

Note: Every table class must know which columns can be used to address rows uniquely. If no primary key columns are specified in the table class definition or the table constructor arguments, or discovered in the table metadata provided by **describeTable(), then the table cannot be used with Zend_Db_Table.

Trying to force the primary key isn't working because it looks like your table doesn't have a column called id which you are specifying as the primary key.

The solution would be to add a primary key to the table you are trying to use.

In your model class that extends Zend_Db_Table_Abstract you can specify a primary key that isn't ID using protected $_primary = 'primary_column';

drew010
  • 68,777
  • 11
  • 134
  • 162
  • Thanks for the response! The problem is I'm trying to use a Zend_Test_DbAdapter for testing. That adapter does not connect to a real database and thus has no concept of table metadata (as far as I know). – Nick Oct 22 '12 at 05:42
  • That's true but it extends from `Zend_Db_Table_Abstract` which performs this check very early in construction so that's why you're getting the error. It testing, you still need to set a primary key which you would have to do in production anyway. Adding the definition for the primary key should get past the error. – drew010 Oct 22 '12 at 15:28
  • As I stated before Zend_Test_DbAdapter has no concept of metadata, therefor no column can be set as the primary key. In the end I think you answered my question- because Zend_Db_Table_Abstract requires a primary key, the Zend_Test_DbAdapter cannot be used. That is a shame... – Nick Oct 24 '12 at 05:50
  • It just seems like if you added a dummy `protected $_primary = 'id';` to your Category model the error should go away. – drew010 Oct 24 '12 at 17:00