Closed weierophinney closed 11 years ago
Sounds interesting :+1:
:+1:
I would be good if there is a way to plug in custom hydrators, i.e DoctrineModule\Stdlib\Hydrator\DoctrineObject, at runtime.
@trondal There already is -- you can grab the HalLinks plugin and map a class to its hydrator:
// in your module.php, likely a route or dispatch listener:
$app = $e->getApplication();
$services = $app->getServiceManager();
$halLinks = $services->get('ViewHelperManager')->get('HalLinks');
$halLinks->addHydrator('My\Entity\Somewhere', new DoctrineObjectHydrator());
I've made some progress on this already, and have one more test to write, a change to make to the HalLinks factory, some documentation, and CS stuff; however, it works already, and should make it easier to embed other resources and collections.
One thing it makes me want to do, however, is also modify the ResourceController
to not cast the return values from the resource events to HalResource
and HalCollection
objects, as if the metadata exists, we could potentially omit that step. I might be able to achieve this by injecting the MetadataMap
into the controller as well, so it can create the resource/collection based on that if the object returned is found.
@weierophinney, this looks awesome. I already started testing. I have a few doubts though. Just expressing them, because I don't know if I am right on this.
Mapping on classname is not sufficient. At least not for generic classtypes since a resource can f.e. have multiple collections. (It is actually quite common.)
I was looking into this, and the problem is that I cannot hint on an array, as the only way to know if the array should be an embedded collection would be to traverse it -- it becomes a very expensive operation that way. What I did instead was make the decision that the functionality will only work on traversable objects.
This actually works really well, and fits well with good OO practices, as you should define a first-class collection object that is an aggregate of related objects. Within a given resource, if you have multiple embedded collections, each should be an instance of a different collection type.
It seems possible to have an infinate extraction of resources/collections when resources reference each other.
It's completely non-circular. The extraction happens at traversal time, and is based on the contents of the current value -- it will not traverse up the tree, only down. The only case where infinite recursion could happen is if, on extraction, a resource has a reference to an ancestor resource. This typically will not happen, though -- and if it does, it's a design flaw by the implementer.
At this point, the only "todo" items I have listed are:
I've moved the resource and collection creation into HalLinks, and ResourceController now consumes HalLinks to create resources and collections -- which means that if a metadata map exists for a given resource or collection, it will be used to create the HalResource/HalCollection for it. Once I have route params/options (for either the main object or embedded resources of collections) in the metadata map, most link generation should be controllable via configuration instead of listeners!
Finished all tasks. I realized this morning that there's no use case for having resource route params/options, as those can be defined in the metadata map for each resource type.
Documentation is in the README.md file now. I'll merge this to the develop branch, and then my next plan is to build full-fledged documentation for the project that can live on rtfd.org.
Merged to develop.
@weierophinney Sorry for the late reply and thanks for the clarification about the collection types.
This actually works really well, and fits well with good OO practices, as you should define a first-class collection object that is an aggregate of related objects. Within a given resource, if you have multiple embedded collections, each should be an instance of a different collection type.
Although I now see your point, people who use ORM's like Doctrine still have a 'problem' since they can't choose their collection type. Anyway, I can understand and see the advantages of this approach.
It's completely non-circular. The extraction happens at traversal time, and is based on the contents of the current value -- it will not traverse up the tree, only down. The only case where infinite recursion could happen is if, on extraction, a resource has a reference to an ancestor resource. This typically will not happen, though -- and if it does, it's a design flaw by the implementer.
Hm, I am thinking about bidirectional relationships. They are not unusual in the domain layer, and cannot always be described as a child/parent relationship.
If you have a resource object that contains nested objects, currently the only way to ensure they are rendered as embedded resources is if they are themselves HalResource or HalCollection instances. Ideally, this shouldn't be necessary.
One idea gleaned from #zftalk.dev is to inject a "metadata map" into the renderer and/or HalLinks plugin. When traversing an object's properties, if a given property has a match in the map, the data in that map will be used to seed a HalResource or HalCollection during hydration and/or rendering.