zend-db
zend-db copied to clipboard
RowGatewayFeature and HydratingResultSet
4 years ago @RalfEggert wrote a post http://zend-framework-community.634137.n4.nabble.com/Combine-TableGateway-RowGateway-and-Entity-classes-td4658325.html about combining RowGatewayFeature with HydratingResultSet. It is still not working. I think it may be commonly used feature - work with hydrated object and then save it.
@xorock
…work with hydrated object and then save it.
The hydrated object is your own target object. If you want to have the save or delete methods in your target object, then you must implement the Zend\Db\RowGateway\RowGatewayInterface
in the target object.
Please look at the documentation: "Row Gateways - ActiveRecord Style Objects". (Important info is missing here: you must implement the exchangeArray
method in your target object!)
For this solution the HydratingResultSet
is not needed.
But I see problem in the ResultSet
class:
public function setArrayObjectPrototype($arrayObjectPrototype)
{
if (!is_object($arrayObjectPrototype)
|| (!$arrayObjectPrototype instanceof ArrayObject && !method_exists($arrayObjectPrototype, 'exchangeArray'))
) {
throw new Exception\InvalidArgumentException('Object must be of type ArrayObject, or at least implement exchangeArray');
}
}
https://github.com/zendframework/zend-db/blob/master/src/ResultSet/ResultSet.php#L65
So the next steps are:
- Extend the unit tests for
setArrayObjectPrototype
method to check side effects - Update the condition in
setArrayObjectPrototype
method - Update the documentation and the code example
If you want to use the HydratingResultSet
:
- Implement the
RowGatewayInterface
in your target object - A new
RowGatewayFeature
class is needed - A new
RowGateway
class is needed
@froschdesign A little time has passed since the last comment but I've encountered the same issue.
I have an Active Record style implementation, but sometimes I want to transform the database result using a hydrator - for example:-
public function initialize()
{
parent::initialize();
$resultSetPrototype = $this->resultSetPrototype;
$hydrator = $resultSetPrototype->getHydrator();
$hydrator->addStrategy('data', new JsonStrategy());
}
There doesn't seem to be too much required to make the Zend\Db\TableGateway\Feature\RowGatewayFeature
a little more versatile
- Line 39 expects an instance of
ResultSet
which instead could beResultSetInterface
- Line 75 uses a method
setArrayObjectPrototype
which could instead be aliased tosetObjectPrototype
meaning that method is standardised across the ResultSet (maybe even part of the interface?) - Finally, the
RowGatewayFeature
also locks in the use of aRowGateway
class when it would be infinitely more useful for it to make that class configurable (setRowGatewayPrototype
andgetRowGatewayPrototype
) to create instances of my own classes
Slight refactoring to allow the flexibility per above. The only other change needed would be to ResultSet
to change the method names
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Db\TableGateway\Feature;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\RowGateway\RowGatewayInterface;
use Zend\Db\TableGateway\Exception;
class RowGatewayFeature extends AbstractFeature
{
protected $primaryKey;
protected $rowGatewayPrototype = ResultSet::class;
/**
* @param null $primaryKey
*/
public function __construct()
{
$args = func_get_args();
if (isset($args[0])) {
if (is_string($args[0]) {
$this->setPrimaryKey($args[0]);
} elseif ($args[0] instanceof RowGatewayInterface) {
$this->setRowGatewayPrototype($args[0]);
}
}
}
public function getPrimaryKey()
{
$this->constructorArguments = func_get_args();
}
public function setPrimaryKey($primaryKey)
{
$this->primaryKey = $primaryKey;
}
public function getRowGatewayPrototype()
{
$this->constructorArguments = func_get_args();
}
public function setRowGatewayPrototype($prototype)
{
$this->constructorArguments = func_get_args();
}
public function postInitialize()
{
$primaryKey = $this->getPrimaryKey();
if (null === $primaryKey) {
// get from metadata feature
$metadata = $this->tableGateway->featureSet->getFeatureByClassName(
'Zend\Db\TableGateway\Feature\MetadataFeature'
);
if ($metadata === false || ! isset($metadata->sharedData['metadata'])) {
throw new Exception\RuntimeException(
'No information was provided to the RowGatewayFeature and/or no MetadataFeature could be consulted '
. 'to find the primary key necessary for RowGateway object creation.'
);
}
$primaryKey = $metadata->sharedData['metadata']['primaryKey'];
}
$rowGatewayPrototype = $this->getRowGatewayPrototype();
if (is_string($rowGatewayPrototype)) {
$rowGatewayPrototypeClass = $rowGatewayPrototype;
$rowGatewayPrototype = new $rowGatewayPrototypeClass(
$primaryKey,
$this->tableGateway->table,
$this->tableGateway->adapter
);
}
if (! $rowGatewayPrototype instanceof RowGatewayInterface) {
throw new Exception\RuntimeException(
'This feature ' . __CLASS__ . ' expects the RowGateway to be an instance of Zend\Db\RowGateway\RowGatewayInterface'
);
}
if (! $this->tableGateway->resultSetPrototype instanceof ResultSetInterface) {
throw new Exception\RuntimeException(
'This feature ' . __CLASS__ . ' expects the ResultSet to be an instance of Zend\Db\ResultSet\ResultSetInterface'
);
}
/** @var $resultSetPrototype ResultSetInterface */
$resultSetPrototype = $this->tableGateway->resultSetPrototype;
$resultSetPrototype->setObjectPrototype($rowGatewayPrototype);
}
}
@simon-mundy I'm sorry, but the topic is already very old. Please create a pull request for your proposal, add a description of the improvement, and also add unit tests.
@froschdesign OK will do. It's been a while and a bit rusty but will get onto it ASAP
This repository has been closed and moved to laminas/laminas-db; a new issue has been opened at https://github.com/laminas/laminas-db/issues/101.