• No results found

Table Data Gateway Pattern

];

This tells ZF2 that for our database, we want to use thePdo_Sqlitedriver, and that our database file is located at data/database.db, after doing some back tracking from the current file’s directory to get there.

Any*.local.phpfile is not supposed to be committed to source control. Instead, you should commit a*.local.php.distexplaining how the configuration file should be set up. This keeps secrets, such as database passwords, from being committed to source control and potentially leaked or exposed.

Since we don’t have any secrets here, and in the interest of committing a workable app, I’m going to put this file in source control anyway.

We’ve now done everything we need to do to tell ZF2 how to talk to our database. Now we just have to write some code to do it.

Table Data Gateway Pattern

Zend Framework 2 uses the Table Data Gateway Pattern, which we very briefly mentioned in Design Patterns, A Primer. In the Table Data Gateway Pattern, a single object acts as a gateway to a database table, handling the retrieving and persisting of all rows for that table.³⁸This pattern is described in great detail in Martin Fowler’sPatterns of Enterprise Application Architecture³⁹.

Essentially, we’re going to have one object, a Data Table, which represents all operations on one of our Entity classes. We’re going to go ahead and make these classes implement our Repository Interfaces, so that they can fulfill the needed contract in our code.

We’ll place all these files within thesrc/Persistence/Zend directory as our Zend Persistence layer. Let’s start with anAbstractDataTable class nested under theDataTable/ directory that will define our generic database operations that the rest of our DataTable classes can inherit from:

³⁸http://martinfowler.com/eaaCatalog/tableDataGateway.html

³⁹http://martinfowler.com/books/eaa.html

Zend Framework 2 Setup 125 // src/Persistence/Zend/DataTable/AbstractDataTable.php

namespace CleanPhp\Invoicer\Persistence\Zend\DataTable;

use CleanPhp\Invoicer\Domain\Entity\AbstractEntity;

use CleanPhp\Invoicer\Domain\Repository\RepositoryInterface;

use Zend\Db\TableGateway\TableGateway;

use Zend\Stdlib\Hydrator\HydratorInterface;

abstract class AbstractDataTable implements RepositoryInterface { protected $gateway;

protected $hydrator;

public function __construct(

TableGateway $gateway, HydratorInterface $hydrator ) {

$this->gateway = $gateway;

$this->hydrator = $hydrator;

}

public function getById($id) {

$result = $this->gateway

->select(['id' => intval($id)])

->current();

return $result ? $result : false;

}

public function getAll() {

$resultSet = $this->gateway->select();

return $resultSet;

}

public function persist(AbstractEntity $entity) {

$data = $this->hydrator->extract($entity);

if ($this->hasIdentity($entity)) {

$this->gateway->update($data, ['id' => $entity->getId()]);

} else {

$this->gateway->insert($data);

$entity->setId($this->gateway->getLastInsertValue());

}

return $this;

}

Zend Framework 2 Setup 126

public function begin() {

$this->gateway->getAdapter()

->getDriver()->getConnection()->beginTransaction();

return $this;

}

public function commit() {

$this->gateway->getAdapter()

->getDriver()->getConnection()->commit();

return $this;

}

protected function hasIdentity(AbstractEntity $entity) { return !empty($entity->getId());

} }

We’re defining our basic database operations - the ones required by ourRepositoryInterface that all other repositories inherit from. These methods are mostly just wrappers around Zend’s TableGateway(that we’ll take a look at in just a minute).

The only interesting piece we have here is the hasIdentity() method, which just (loosely) determines if our entity had already been persisted, so that we know whether we’re doing an insert()orupdate()operation. We’re relying on the presence of an ID here, which might not always work. It’s good enough for now.

TableGateway

The first thing that our AbstractDataTable requires is an instance of TableGateway. The TableGatewayis Zend’s workhorse that does all the database heavy lifting. As you can see by looking atAbstractDataTable, all of our operations live off one of it’s methods.

We’re essentially going to use Zend’s concrete implementation, just configured to work with our own tables. We’ll define those when we worry about actually instantiating aDataTable.

Hydrators

The second thing that wants to be injected into theAbstractDataTableis an instance of Zend’s HydratorInterface. A hydrator is responsible for hydrating an object, meaning, filling out it’s attributes with values. In our case, we’re going from an array of data to a hydrated entity (think posted form data).

Zend’s hydrators are also responsible for data extraction, which is the opposite of hydrating: we take data from a hydrated object and store it in an array representation, which is necessary for Zend’s database update operations. You can see how it’s used in thepersist()method above.

Zend Framework 2 Setup 127 For the most part, we’ll use a hydrator provided by Zend called theClassMethodshydrator. This hydrator scans the object for set and get methods, and uses them to determine how to hydrate or extract that object.

For instance, if an object has asetAmount()method, the hydrator will look for anamountkey in the array and, if found, pass the value at that key to thesetAmount()method to hydrate that information to the object.

Likewise, if an object has agetAmount()method, the hydrator calls it to get the value and adds an element to the resulting array with the key ofamountand the value returned fromgetAmount(). In some instances, we’ll use the ClassMethods hydrator directly. In others, we’ll wrap this hydrator to provide some additional functionality to it.