Using Direct ArrayObject instance as ObjectPrototype in Zend\Db
When creating a table model for ZF2 or ZF3 application with Zend\DB, direct ArrayObject instance can be usefull as ResultSet object prototype. We can no longer need to create an individual class that has getArrayCopy()
or exchangeArray()
for data transformation.
For example, we have the following table model:
<?php namespace Application\Model; use Zend\Db\TableGateway\AbstractTableGateway; class CountryTable { public static $table = 'country'; private $tableGateway; public function __construct(AbstractTableGateway $tableGateway) { $this->tableGateway = $tableGateway; } public function getCountriesInAsia() { $select = $this->tableGateway->getSql()->select(); $select->where([ 'continent' => 'ASIA' ]); return $this->tableGateway->selectWith($select); } }
The ArrayObject
usage we can use is:
new ArrayObject([], ArrayObject::ARRAY_AS_PROPS);
So, we can build the factory for above table model as follows:
<?php namespace Application\Model; use ArrayObject; use Interop\Container\ContainerInterface; use Zend\ServiceManager\Factory\FactoryInterface; use Zend\Db\ResultSet\HydratingResultSet; use Zend\Db\TableGateway\TableGateway; class CountryTableFactory implements FactoryInterface { public function __invoke(ContainerInterface $container, $requestedName, array $options = null) { $resultSetPrototype = new HydratingResultSet(); $resultSetPrototype->setObjectPrototype( new ArrayObject([], ArrayObject::ARRAY_AS_PROPS) ); $tableGateway = new TableGateway( CountryTable::$table, $container->get('Zend\Db\Adapter\Adapter'), null, $resultSetPrototype ); return new CountryTable($tableGateway); } }
and register it into service_manager under factories:
<?php namespace Application; return [ // ... 'service_manager' => [ 'factories' => [ Model\CountryTable::class => Model\CountryTableFactory:class, ], ], ];
When retrieving the data, you can do the followings:
use Application\Model\CountryTable; $countryTable = $container->get(CountryTable::class); $countriesInAsia = $countryTable->getCountriesInAsia(); foreach ($countriesInAsia as $key => $row) { // dump a copy of the ArrayObject var_dump($arrayCopy = $row->getArrayCopy()); // echoed column as property echo $row->name; // with value "INA" echo $row->iso; // with value "ID" echo $row->continent; // with value "ASIA" // echoed as array with provided key echo $row['name']; // with value "INA" echo $row['iso']; // with value "ID" echo $row['continent']; // with value "ASIA" // modify data via exhangeArray $row->exchangeArray(array_merge( $arrayCopy, [ 'name' => 'INDONESIA', ] )); // or modify its data by its property $row->name = 'INDONESIA'; // or modify its data by its index array $row['name'] = 'INDONESIA'; echo $row->name; // now has value "INDONESIA" echo $row['name']; // now has value "INDONESIA" }
Bonus:
To avoid repetitive creating factory class for each table model, we can create an abstract factory for it:
<?php namespace Application\Model; use ArrayObject; use Interop\Container\ContainerInterface; use Zend\Db\ResultSet\HydratingResultSet; use Zend\Db\TableGateway\TableGateway; use Zend\ServiceManager\Factory\AbstractFactoryInterface; class CommonModelTableFactory implements AbstractFactoryInterface { public function canCreate(ContainerInterface $container, $requestedName) { return ((substr($requestedName, -5) === 'Table') && class_exists($requestedName)); } public function __invoke(ContainerInterface $container, $requestedName, array $options = null) { $tableModel = '\\' . $requestedName; $resultSetPrototype = new HydratingResultSet(); $resultSetPrototype->setObjectPrototype( new ArrayObject([], ArrayObject::ARRAY_AS_PROPS) ); $tableGateway = new TableGateway( $tableModel::$table, $container->get('Zend\Db\Adapter\Adapter'), null, $resultSetPrototype ); return new $tableModel($tableGateway); } }
So, now, we can have 1 abstract factory for all table model services:
<?php namespace Application; return [ // ... 'service_manager' => [ 'abstract_factories' => [ Model\CommonModelTableFactory:class, ], ], ];
That’s it 😉
13 comments