-
Notifications
You must be signed in to change notification settings - Fork 33
Multiple hydrator strategy per field #18
Comments
You could use one of the default strategies in DoctrineModule. These make it possible to call the add* and remove* methods for collections. The entity will contain the logic to remove and add related entities from your entity. |
I don't think it's a good idea to let hydrators be aware of URLs parameters. Also, how could I use the default strategies from DoctrineModule and specify whether I want to add or remove elements from the collection... or even remove all the elements from the collection? Given that hydrate() only expects one parameter to be a the value (or collection of values) to hydrate the entity with, I think it could be improved by building Add and Remove strategies with the required logic inside. Do you still think default strategies in DoctrineModule are the answer? |
An alternative with a custom hydrator could be:
Then, a custom hydrator could be set to identify "add" and "remove" parts, and process them accordingly. |
Hello @santiph, Assume following simplified json to start with:
As you can see the album contains 3 artists and 3 tracks. Next you want to remove an artist and a track, you could patch it like this:
This means that the artist and track property will be replaced with the new data. The ORM hydrators will call the removeArtist() and removeTrack() method on the entity. Not sure if the IDs are also translated to the corresponding entities, but normally they should do this. If you want to clear a property, you just send an empty array to the server. With other words: if you PATCH the json, you will always have to specify the full NEW state of the object. If you want the hydrators to work in another way, you will have to write your own custom hydrator. |
If you apply a PATCH on a resource, its field- and not value-driven. A collection of associated resources is not a field, but value. If you want to PATCH the association collection it must become a resource itself. Then you can POST/DELETE (evtl. +list) it. Regarding multiple strategies: Something like that: namespace module\Stdlib\Hydrator;
use DoctrineModule\Stdlib\Hydrator\Strategy\AbstractCollectionStrategy;
use Zend\Stdlib\Hydrator\Strategy\StrategyChain;
/**
* Class DoctrineCollectionStrategy
* @package module\Stdlib\Hydrator
*/
class DoctrineCollectionStrategy extends AbstractCollectionStrategy
{
/**
* Strategy chain for extraction
*
* @var StrategyInterface[]
*/
private $extractionStrategies;
/**
* Strategy chain for hydration
*
* @var StrategyInterface[]
*/
private $hydrationStrategies;
/**
* Constructor
*
* @param array|\Traversable $extractionStrategies
*/
public function __construct($extractionStrategies)
{
$extractionStrategies = ArrayUtils::iteratorToArray($extractionStrategies);
$this->extractionStrategies = array_map(
function (StrategyInterface $strategy) {
// this callback is here only to ensure type-safety
return $strategy;
},
$extractionStrategies
);
$this->hydrationStrategies = array_reverse($extractionStrategies);
}
/**
* @param ClassMetadata $classMetadata
* @return AbstractCollectionStrategy
*/
public function setClassMetadata(ClassMetadata $classMetadata)
{
foreach ($this->hydrationStrategies as $hydrationStrategy) {
if ($hydrationStrategy instanceof AbstractCollectionStrategy) {
$hydrationStrategy->setClassMetadata($classMetadata);
}
}
return parent::setClassMetadata($classMetadata);
}
/**
* @param string $collectionName
* @return AbstractCollectionStrategy
*/
public function setCollectionName($collectionName)
{
foreach ($this->hydrationStrategies as $hydrationStrategy) {
if ($hydrationStrategy instanceof AbstractCollectionStrategy) {
$hydrationStrategy->setCollectionName($collectionName);
}
}
return parent::setCollectionName($collectionName);
}
/**
* @param object $object
* @return AbstractCollectionStrategy
*/
public function setObject($object)
{
foreach ($this->hydrationStrategies as $hydrationStrategy) {
if ($hydrationStrategy instanceof AbstractCollectionStrategy) {
$hydrationStrategy->setObject($object);
}
}
return parent::setObject($object);
}
/**
* {@inheritDoc}
*/
public function extract($value)
{
foreach ($this->extractionStrategies as $strategy) {
$value = $strategy->extract($value);
}
return $value;
}
/**
* {@inheritDoc}
*/
public function hydrate($value)
{
foreach ($this->hydrationStrategies as $strategy) {
$value = $strategy->hydrate($value) ?: $value;
}
return $value;
}
} If you think that makes sense, I would implement it the "proper" way (more generic) and make a PR. Note: i'm not that happy with that part: $value = $strategy->hydrate($value) ?: $value; But otherwise some implementations, which depend on doctrine-hydration-module would break. For example the CollectionExtraft of zf-apigility-doctrine does not return the value (hydrate method), which evaluates to NULL: I wanted a working example. |
Sorry for the late response, I was on a 2 weeks vacation. For example: I am looking forward to your look on this. |
For the same reason, why HydratorInterface, which you use, implements both ExtractionInterface and HydrationInterface: https://github.com/zendframework/zend-stdlib/blob/master/src/Hydrator/HydratorInterface.php Example: zf-apigility-doctrine specifies one hydrator for entities, which is used for both extraction and hydration, which is the whole point of using an hydrator interface, which implements both. Otherwise, you would have to create an hydrator and extractor and possibly make same configuration for two different objects with slight differences, which would create a lot overhead and boilerplate. The extraction is used on GET, the hydration is used on POST and PUT. |
Hello @akomm, It looks like I did not phrase the question very wel. Let me try again: Thanks! |
Example, if you want to use both: and: And you don't want to copy & paste code into a custom class which has both implementations. It is also a problem if you create a class, which internaly invokes the |
Hi @akomm, Thanks! |
Ok. I'm on it, but the example is not a good enough so I will have to make some adjustments and test to see if it actually works out. I see a prob using chains with some strategy implementations currently around. Example: the already mentioned |
I'm, working with three entities: Albums, Artists and Tracks. They have a many-to-many relationship with doctrine. And I'm trying to update the Artists and Tracks list for a particular venue.
The way I'm trying to do so is by using PATCH.
A detailed example of what I'm proposing could be found zfcampus/zf-apigility-doctrine#215
Problem is, where in the modules could I specify this multiple strategies and make them dependent on the query being sent?
The text was updated successfully, but these errors were encountered: