Closed danizord closed 9 years ago
@danizord I'd personally keep it out of ZfrRest - every time we try to implement that thing it bites us back (every. frikken. time.)
I already tried recursive hydrators for ZF3. I failed lamentably. REALLY hard thing.
Ok, so what's the best/easy workaround? A custom hardcoded hydrator for each entity? Meh!
Right now, yes. What ZfrRest can do is removing fields from submitted input data and from extracted data when building the output.
The "recursive hydrator" thing is something that I'd likely get from https://github.com/leedavis81/drest instead
:+1:
Hi again,
@danizord , the only solution currently is to hardcode them. I really can't find of a clean way of automating this for various reasons:
Furthermore, the current Doctirne hydrator is WAY too complex. It really should be splitted in smaller, more specialized hydrator (but this will be hard until we get to ZF3 to have separate interfaces for hydrator and extractor... and find a good name for this new object that will combine "Hydrator" and "Extractor" :D).
Of course, we could go the complex way and using something like JMSSerializer, but I'm a bit afraid of the performance, and it means even more configuration, even more annotations... it will really lead to entities that are 90% comments and 10% code and I don't like this.
Ruby On Rails' Active model serializer uses a "Serializer" object for this. An example:
class PostSerializer < ActiveModel::Serializer
attributes :title, :body
end
So what we could provide are sane defaults:
class MyHydrator
{
protected $attributes = ['title', 'body'];
}
better than:
class MyHydrator
{
public function extract($object)
{
return ['id' => $object->getId(), 'body' => $object->getBody()];
}
}
Any better? I dunno.
@bakura10 @danizord @Ocramius Hi, maybe a solution about to prevent circular dependency by creating a new annotation or params in association's annotation to specifying a level. User's hydrator trying to serialize tweet using TweetHydrator. User's tweet association has one level so TweetHydrator don't serialize user.
Sorry for my very bad english it's not easy to explain my idea :(
It does not work. For instance what if the tweet entity contains a user (that you don't want to serialize to avoid circular) but other associations that you indeed want to serialize ?
Envoyé de mon iPhone
Le 10 févr. 2014 à 13:38, Florent Blaison notifications@github.com a écrit :
@bakura10 @danizord @Ocramius Hi, maybe a solution about to prevent circular dependency by creating a new annotation or params in association's annotation to specifying a level. User's hydrator trying to serialize tweet using TweetHydrator. User's tweet association has one level so TweetHydrator don't serialize user.
Sorry for my very bad english it's not easy to explain my idea :(
— Reply to this email directly or view it on GitHub.
You can specifying the level for each association. User => Tweet : one level but User => Role => Permission : two level
What if "Tweet" has another association "Favorited" that you want to serialize too ? ;)....
Arf :(
@bakura10 @Ocramius What is a good way to use the ZF2 HydratorInterface
in conjunction with DoctrineORMModule\Paginator\Adapter\DoctrinePaginator
? In general, I've run into trouble hydrating / extracting from Doctrine collections using ZF2 hydrators.
I'm referring mostly to this code from the slides at http://marco-pivetta.com/doctrine-orm-zf2-tutorial/
use Doctrine\ORM\Tools\Pagination\Paginator as ORMPaginator;
use Zend\Paginator\Paginator;
// Create a Doctrine Collection
$query = $em->createQuery('SELECT f FROM Foo f JOIN f.bar b');
// Create the paginator itself
$paginator = new Paginator(
new DoctrinePaginator(new ORMPaginator($query))
);
Is there an easy way I'm missing to use a custom hydrator when returning such a result?
Some thoughts: https://github.com/doctrine/DoctrineModule/issues/385
Using your example @danizord:
class TweetHydrator extends DoctrineComposableHydrator
{
public function __construct()
{
$this->addStrategy('author', new SingleAssociationEmbeddedStrategy());
}
}
In turn, author will use the DoctrineComposableHydrator when extracted. This has some sane defaults (output all fields, and each associations using id only). So if "author" has other properties you want recursively extract:
class TweetHydrator extends DoctrineComposableHydrator
{
public function __construct()
{
$this->addStrategy('author', new SingleAssociationEmbeddedStrategy(
new UserHydrator()
));
}
}
class UserHydrator extends DoctrineComposableHydrator
{
public function __construct()
{
$this->addFilter('password');
$this->addStrategy('addresses', new CollectionAssociationEmbeddedStrategy());
}
}
Is this enough or you were actually toward even more automatization? If so, what would be your ideal usage?
In the context of ZfrRest, this could also help us to add mapping information and build hydrators automatically:
/**
* @REST\Resource(hydrator="DoctrineModule\Hydrator\DoctrineComposableHydrator")
*/
class Tweet
{
/**
* @REST\Association(extractionStrategy="EMBEDDED")
*/
protected $author;
}
This will automatically create a DoctrineComposableHydrator for the tweet AND adding a strategy for the author using the hydrator bound in the resource of the User entity (associated entity). If you want a custom hydrator:
The more I think about it, the more I believe that we actually should separate hydration and extraction, as other framework do.
Regarding circular, I think we can use the following workaround. Imagine we extract User, that has an Embedded extraction for tweet, and tweet itself has an embedded for author => we hit circular dependency problem:
class UserHydrator
{
static protected $circularChecker = [];
public function extract($object)
{
if (self::$circularChecker[spl_object_hash($object)) {
return $object->getId(); // return id for any association entity that may ask for the user
}
self::$circularChecker[spl_object_hash($object)] = true;
/// when extracting the tweet association... its hydrator will also ask for extracting the user,
// but to avoid circular dependency... use the id only!
self::$circularChecker[spl_object_hash($object)] = false;
}
}
Will result in:
[
'id' => 5,
'firstName' => 'Michaël',
'tweets' => [
[
'id' => 5654,
'content' => 'Foo',
'author' => 5
]
]
]
Thanks, I was actually trying to use the standard Doctrine AbstractHydrator
here: https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php
I like the direction you're heading with this though. My issue stemmed more from the fact that I didn't know how to set a ZF2 HydratorInterface
object to be the hydrator for the standard Doctrine paginators (DoctrineORMModule\Paginator\Adapter\DoctrinePaginator
).
No longer apply with #184. Rendering is now done using template, and ZfrRest will support render sub-resources. So each template is now only responsible to render its own part.
What's the best way to support this? Let's create a
RecursiveHydrator
?