In diesem Beitrag erstellen wir einen (so von mir genannten) EntityMapper. Dieser basiert auf dem Code aus dem Beitrag Zend: RowGateway, Entitiy und Hydration – “Persistente Entitäten”.

Den fertigen EntityMapper können wir anschließend in der gesamten Anwendung als unseren Mapper zwischen Datenbank und Entities verwenden.

Der Ort an dem wir den EntityMapper erstellen, ist grundsätzlich egal. Jedes Modul wird später ohne Probleme auf den EntityMapper zugreifen können, da alle Module im Zuge der Initialisierung zusammengeführt werden.
Um besser erkenntlich zu machen, dass dieser EntityMapper aber tatsächlich von allen Modulen verwendet werden kann/soll, legen wir diesen innerhalb des Application Moduls ab.

Pfad

\modules\Application\src\Application\Mapper\EntityMapper.php

Basierend auf dem zuvor genannten Beitrag haben wir hier eine modifizierte Klasse des Customer Mappers:

namespace Application\Mapper;

use Zend\Db\TableGateway\TableGateway;
use Zend\Stdlib\Hydrator\Reflection;
use Zend\Db\TableGateway\Feature\RowGatewayFeature;
use Zend\Db\ResultSet\HydratingResultSet;

class EntityMapper extends TableGateway {
    
    protected $tableName = "";
    protected $idCol = "id";
    protected $entityPrototype = null;
    protected $hydrator = null;
    protected $adapter = null;
    
    public function __construct($adapter) {
        $this->hydrator = new Reflection();
        $this->adapter = $adapter;
    }
    
    public function setup($tableName, $entityPrototype) {
        $this->tableName = $tableName;
        $this->entityPrototype = $entityPrototype;
        
        parent::__construct($this->tableName, $this->adapter, new RowGatewayFeature($this->idCol));
    }
    
    public function hydrate($rows) {
        $result = new HydratingResultSet($this->hydrator, $this->entityPrototype);
        return $result->initialize($rows->toArray());
    }
    
    public function getAll(array $where = array()) {
        return $this->hydrate($this->select($where));
    }
    
    public function get($closure) {
    	  return $this->hydrate($this->select($closure));
    }
    
    public function insert($entity) {
        return parent::insert($this->hydrator->extract($entity));
    } 
    
    public function updateEntity($entity) {
        return parent::update($this->hydrator->extract($entity), array("id" => $entity->id));
    }
}

Damit wir von überall auf unseren EntityMapper zugreifen können, registrieren wir diesen innerhalb des ServiceManagers mithilfe einer “inline” Factory:

'service_manager' => array(
        'factories' => array(
            [....]            
            'Application\Mapper\EntityMapper' => function($sm) {
                 $adapter = $sm->get('Zend\Db\Adapter\Adapter');
            	 return new \Application\Mapper\EntityMapper($adapter);
            }
        ),
        
        'aliases' => array(
        	'EntityMapper' => 'Application\Mapper\EntityMapper'
        ),
    ),

Mit Hilfe des ServerManagers können wir nun eine Instanz dieser Klasse erhalten, in der die zuvor (zur Designtime) bekannten Abhängigkeiten bereits durch den ServiceManager injiziert werden (\Zend\Db\Adapter\Adapter).
Die weiteren Abhängigkeiten, wie den entityPrototype können wir noch nicht injizieren, da wir diesen erst zur Runtime kennen.
Andernfalls könnte man sich auch für jede Entity innerhalb der Anwendung eine eigene EntityMapperFactory bauen

Die noch notwendigen Abhängigkeiten können wir nun mittels ->setup() injizieren.

$customerTable = $this->getServiceLocator()->get('EntityMapper');
$customerTable->setup("customer", new \Customer\Entity\Customer());

Operationen:

$rows = $customerTable->getAll();
// oder:
$rows = $customerTable->getAll(array("id" => "2"));
// oder:
$rows = $customerTable->get(function($select) {
    $select->where(array("id >= ?" => "2"));
});

$customerTable->insert($newCustomer);
$customerTable->updateEntity($customer);