Using an ORM
I would use any ORM, that allows you to configure the mappings outside of the model classes.
Personally, i like doctrine. I use to map the fields with the database by means of the docbloc annotations since it is easier, but I agree that the information about how the data is stored should not be inside the model. Luckily you have the XML and YAML options for data mapping.
For instance, if you have this Entity en your model:
<?php
class Message
{
private $id;
private $text;
private $postedAt;
}
You can configure this mapping in XML
<doctrine-mapping>
<entity name="Message" table="message">
<field name="id" type="integer" />
<field name="text" length="140" />
<field name="postedAt" column="posted_at" type="datetime" />
</entity>
</doctrine-mapping>
So, the mappings are in configuration files, so the model itself is not aware of how it is persisted.
You have the DoctrineORMModule module to easily integrate Doctrine into ZF2:
If you don't want to use an ORM
You can create the mappings in the config files. Having that you use the table gateway pattern, after that, you can inject that config to the Table class. I will give you an example adapting the example of the Album module from the ZF2 skeleton application (you can adapt it to your real scenario, i think this is easy than if i have to study your system)
You create the mappings in the module.config.php file. You map the classname to a table, and every field on the class, to a column name
'mappings' => array ( 'AlbumTable' => array ( 'table'=> 'TABLENAME', 'fields' => array ( 'field1_en_clase' => 'field1_en_db', 'field2_en_clase' => 'field2_en_db', ) ), 'OTHER_ENTITY' => array ( 'table'=> 'TABLENAME', 'fields' => array ( 'field1_en_clase' => 'field1_en_db', 'field2_en_clase' => 'field2_en_db', ) ), //... );
Then, when you configure the service in the Module.php, you send that information to the classes that need them:
class Module { public function getServiceConfig() { return array( 'factories' => array( 'Album\Model\AlbumTable' => function($sm) { $config = $sm->get('AlbumTableGateway'); $tableGateway = $sm->get('AlbumTableGateway'); //when you create the AlbumTable class, you send the field mappings $table = new AlbumTable($tableGateway, $config ['mappings']['AlbumTable']['fields']); return $table; }, 'AlbumTableGateway' => function ($sm) { $config = $sm->get('AlbumTableGateway'); $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter'); $resultSetPrototype = new ResultSet(); $resultSetPrototype->setArrayObjectPrototype(new Album()); //when you create the TableGateway, you also take the table name from the config. return new TableGateway($config ['mappings']['AlbumTable']['table'] , $dbAdapter, null, $resultSetPrototype); }, ), ); } }
Then, you just need to adapt your AlbumTable class, so it receives and uses that mappings.
class AlbumTable { protected $tableGateway, $mappings; //First, in the constructor, you receive and save the mappings public function __construct(TableGateway $tableGateway, $mappings) { $this->tableGateway = $tableGateway; $this->mappings = $mappings; } //then in every function where you before hardcoded the column names, now you take it from the mappings, for instance: public function saveAlbum(Album $album) { /* here you had $data = array( 'artist' => $album->artist, 'title' => $album->title, ); */ // now you change it to: $data = array( $mappings['artist'] => $album->artist, $mappings['title'] => $album->title, ); $id = (int) $album->id; if ($id == 0) { $this->tableGateway->insert($data); } else { if ($this->getAlbum($id)) { $this->tableGateway->update($data, array('id' => $id)); } else { throw new \Exception('Album id does not exist'); } } }
And the same everywhere a column name was harcoded.
I think you can easily adapt this to your system.