Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Doctrine metadata driver #116

Closed
wants to merge 20 commits into from

9 participants

@rosstuck

Inherits "type" from the Doctrine metadata. Still needs some cleanup and tests.

@phiamo

works like a charm, added a pr to add the config for it: #122

Ross Tuck Merge pull request #2 from phiamo/doctrine_type_driver
Adds Symfony config for doctrine driver
d3bb696
@schmittjoh schmittjoh commented on the diff
Metadata/Driver/DoctrineTypeDriver.php
((15 lines not shown))
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace JMS\SerializerBundle\Metadata\Driver;
+
+use Metadata\Driver\DriverInterface,
+ Doctrine\Common\Persistence\ObjectManager,
+ Doctrine\ORM\EntityManager,
+ Doctrine\ORM\Mapping\ClassMetadataInfo;
+
+/**
+ * This class decorates any other driver. If the inner driver does not provide a
+ * a property type, the decorator will guess based on Doctrine 2 metadata.
+ */
+class DoctrineTypeDriver implements DriverInterface
@schmittjoh Owner

Maybe call this DoctrineORMTypeDriver? If I'm not mistaken, then it only applies to the ORM, no?

@rosstuck
rosstuck added a note

In theory, this could be ported to MongoDB, etc. If we go by the Common persistence interfaces, DoctrineObjectManagerTypeDriver would be correct. Real mouthful though. :)

@schmittjoh Owner

fair point, let's leave it as is :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Metadata/Driver/DoctrineTypeDriver.php
((42 lines not shown))
+ 'smallint' => 'integer',
+ 'bigint' => 'integer',
+
+ 'datetime' => 'DateTime',
+ 'datetimetz'=> 'DateTime',
+ 'time' => 'DateTime',
+
+ 'float' => 'float',
+ 'boolean' => 'boolean',
+ 'array' => 'array<string, string>',
+ );
+
+ /**
+ * @var Metadata\Driver\DriverInterface
+ */
+ protected $innerDriver;
@schmittjoh Owner

Could you call this delegate instead?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Metadata/Driver/DoctrineTypeDriver.php
((51 lines not shown))
+ 'array' => 'array<string, string>',
+ );
+
+ /**
+ * @var Metadata\Driver\DriverInterface
+ */
+ protected $innerDriver;
+
+ /**
+ * @var Doctrine\Common\Persistence\ObjectManager
+ */
+ protected $em;
+
+ public function __construct(DriverInterface $innerDriver, ObjectManager $em)
+ {
+ $this->setInnerDriver($innerDriver);
@schmittjoh Owner

Could you directly set the properties instead of adding specific setters?

Or, do you have a use-case where we need to set the properties after construction?

@rosstuck
rosstuck added a note

Just a stylistic choice. I'll change it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Metadata/Driver/DoctrineTypeDriver.php
((68 lines not shown))
+ }
+
+ protected function setInnerDriver(DriverInterface $innerDriver)
+ {
+ $this->innerDriver = $innerDriver;
+ }
+
+ protected function setEntityManager($em)
+ {
+ if (!$em instanceof EntityManager) {
+ throw new InvalidArgumentException('Only the Doctrine ORM is supported at this time');
+ }
+
+ // Workaround: Doctrine's hasMetadataFor() only checks loaded metadata
+ // so we need to load them all, otherwise we'll raise several errors.
+ $em->getMetadataFactory()->getAllMetadata();
@schmittjoh Owner

Maybe we could delay that until it is actually needed to decrease the performance impact that has?

@rosstuck
rosstuck added a note

The problem I had was that if you try and fetch classMetadata that hasn't been loaded, then Doctrine throws an exception. I either had to do this or catch exceptions as flow control. I'm not sure there's a workaround. I'll try on IRC next week

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Metadata/Driver/DoctrineTypeDriver.php
((93 lines not shown))
+ if (!$dbMetadataFactory->hasMetadataFor($classMetadata->name)) {
+ return $classMetadata;
+ }
+
+ // We base our scan on the internal driver's property list so that we
+ // respect any internal white/blacklisting like in the AnnotationDriver
+ $dbMapping = $dbMetadataFactory->getMetadataFor($classMetadata->name);
+ foreach ($classMetadata->propertyMetadata as $propertyMetadata) {
+ // If the inner driver provides a type, don't guess anymore.
+ if ($propertyMetadata->type) {
+ continue;
+ }
+
+ $propertyName = $propertyMetadata->name;
+ if (isset($dbMapping->fieldMappings[$propertyName])) {
+ $propertyMetadata->type = $this->normalizeFieldType(
@schmittjoh Owner

Does a separate method makes sense here? It is only called once afais.

@rosstuck
rosstuck added a note

For me, it's a readability decision. It's bulldozer code but it reduces the nesting/work going on in the main loop. I'd rather keep it for now if you don't have a huge objection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@schmittjoh
Owner

I really like the documentation effort that you have put into the code, and apart from a few minor improvements it looks really good.

Could you maybe also add a few unit tests to ensure we do not break this in the future? That would be much appreciated.

@rosstuck

Thanks a lot! As a general note, I also need to move all the direct property lookups on the Doctrine metadata to use the public API.

I'll take a swing at unit tests over the next week or two. Would you prefer mocking out the Doctrine metadata or adding Doctrine ORM to require-dev? The former is a lot lighter, but the latter might be more in the spirit of the "functional" test suite.

Metadata/Driver/DoctrineTypeDriver.php
((40 lines not shown))
+
+ 'integer' => 'integer',
+ 'smallint' => 'integer',
+ 'bigint' => 'integer',
+
+ 'datetime' => 'DateTime',
+ 'datetimetz'=> 'DateTime',
+ 'time' => 'DateTime',
+
+ 'float' => 'float',
+ 'boolean' => 'boolean',
+ 'array' => 'array<string, string>',
+ );
+
+ /**
+ * @var Metadata\Driver\DriverInterface
@stof
stof added a note

you should either add the leading \ to have a FQCN or use the short class name relying on the use statements. Currently, this will confuse all IDEs resolving the name in the phpdoc with the same rules than in the code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
rosstuck added some commits
@rosstuck rosstuck Add tests for DoctrineType driver 79db2f7
@rosstuck rosstuck Refactored DoctrineType driver based on feedback
-Renamed several variables
-Now uses ClassMetadata interface instead of properties
-Fixed Docblock class names
ce27443
@rosstuck

These latest commits should address the previous issues. Also, I slightly refactored the existing driver tests to run for these as well, which hits all the main cases. The two trade-offs to look out for are:
1) I couldn't successfully mock the Doctrine entity manager so it's using an SQLite database (that will never be created).
2) There is now some almost duplicate fixture models, but I don't think there's any other way to do it well.

All in all though, seems to work pretty well!

Tests/bootstrap.php
@@ -15,7 +15,12 @@
* limitations under the License.
*/
-include_once dirname(__DIR__).'/vendor/autoload.php';
+$loader = include_once dirname(__DIR__).'/vendor/autoload.php';
+
+Doctrine\Common\Annotations\AnnotationRegistry::registerLoader(function($class) use ($loader) {
+ $loader->loadClass($class);
+ return class_exists($class, false);
+});
@stof
stof added a note

this could be simplified with Doctrine\Common\Annotations\AnnotationRegistry::registerLoader(array($loader, 'loadClass')); as Composer already returns the boolean. And btw, it will also handle the annotations of the bundle (as Composer also autoloads the bundle) so the next piece of code can be dropped too

Fixed. However, the composer autoloader did not pick up the rest of the bundle so I left the next section of code as well.

@stof
stof added a note

ah, indeed. The bundle is autoloaded by a separate closure, which is not returned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
composer.json
@@ -20,7 +20,9 @@
"symfony/yaml": "2.*",
"symfony/form": "2.*",
"symfony/validator": "2.*",
- "twig/twig": "dev-master"
+ "twig/twig": "dev-master",
+ "doctrine/orm": "2.2.*"
+
@schmittjoh Owner

Can you remove this blank line?

Whoops. Fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@schmittjoh
Owner

Does it really make sense to extend the base driver?

How about extending PHPUnit_Framework_TestCase, and then test the overwriting of the metadata?

@rosstuck

The BaseDriverTest did a great job of hitting all of the main points I needed to test, and since the primary testing style is functional, it made sense to try and integrate with that, rather than writing individual unit tests. I'm not opposed to it in principle or adding a few unit tests besides this one though; just not sure if it adds much then what this already does (besides not requiring the small changes to the existing code).

Metadata/Driver/DoctrineTypeDriver.php
((63 lines not shown))
+
+ public function __construct(DriverInterface $delegate, ObjectManager $em)
+ {
+ $this->delegate = $delegate;
+
+ if (!$em instanceof EntityManager) {
+ throw new InvalidArgumentException('Only the Doctrine ORM is supported at this time');
+ }
+ $this->em = $em;
+ }
+
+ public function loadMetadataForClass(\ReflectionClass $class)
+ {
+ // Abort if the given class is not a mapped entity
+ if ($this->em->getMetadataFactory()->isTransient($class->name)) {
+ return $classMetadata;
@schmittjoh Owner

This seems to be a bit early, should be after the loadMetadataForClass call below.

@rosstuck
rosstuck added a note

Whoops, stupid mistake. Fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@schmittjoh
Owner

For this driver, I would prefer a few simple unit tests instead of functional tests. For the XML driver on the other hand, mocking all the lower level XML-API calls is not so fun, that's why these are mostly (exclusively) functional tests.

@rosstuck

Okay, I'll start hacking up unit tests that still use a live entity manager but just test a few situations on a smaller set of models.

Would you like me to back out the changes to the functional tests or keep them for the heck of it?

@rosstuck

Okay, all tests converted. Everything seems to be covered.

There are, however, 3 tests that are failing. At least one of them is from @phiamo's DI container patch. I'm not a heavy Symfony2 user so if someone else can fix this, I think all issues are resolved.

@rosstuck

Updated against upstream master. This fixes all of the failing tests except for the DI, which has changed to a new error in testLoad "Undefined offset: 16." I'm not sure this actually has anything to do with the change as removing it from the services.xml had no effect.

@schmittjoh
Owner

Just looked at the PR again, thanks for your great work!

There is just one thing, right now we register the driver always. If I'm not mistaken that will break the bundle for non-ORM users. So, I think we need to register this driver conditionally (only when Doctrine ORM is available). Probably, even better would be to add something to the configuration and make the entity manager which is used configurable, or inject the ManagerRegistry object (but I think that would be 2.1 only). Thoughts?

@rosstuck

As I mentioned, I'm not a heavy Symfony2 user, so you might as well being telling me the thingamajiggy is jamming the whatzit so we should adjust the doodad. ;)

No seriously, I'm in agreement. I think the Manager Registry is probably the way to go, it's something I've wanted as well (I'd still like to have it accept either an ObjectManager or ManagerRegistry though) A quick glance at the code shows getManagerForClass() appears to null if no manager is found so, this ought to be easy to add in.

Conditional registration in the DIC makes sense to me. Additionally, I'm not certain but simply flipping this on for default might sliiiightly change the behavior for existing applications so either via a switch or just for >2.1 (if that's possible?) would probably be the best time to do this.

rosstuck added some commits
@rosstuck rosstuck Remove EntityManager only restriction.
Now that this class uses the ClassMetadata public interface it should work
with all types of Doctrine persistence managers
b6525e0
@rosstuck rosstuck Added ManagerRegistry support 1d84208
@rosstuck

ManagerRegistry support added, with a quick test. All that's left is the DI config, which (as discussed) I'll leave to you.

Metadata/Driver/DoctrineTypeDriver.php
((26 lines not shown))
+/**
+ * This class decorates any other driver. If the inner driver does not provide a
+ * a property type, the decorator will guess based on Doctrine 2 metadata.
+ */
+class DoctrineTypeDriver implements DriverInterface
+{
+ /**
+ * Map of doctrine 2 field types to JMS\SerializerBundle types
+ * @var array
+ */
+ protected $fieldMapping = array(
+ 'string' => 'string',
+ 'text' => 'string',
+ 'blob' => 'string',
+
+ 'integer' => 'integer',
@stof
stof added a note

you also need to map int as the MongoDB ODM uses it instead of integer IIRC

@schmittjoh Owner

Let's wait for a PR that adds Mongo ODM support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Metadata/Driver/DoctrineTypeDriver.php
((45 lines not shown))
+ 'datetime' => 'DateTime',
+ 'datetimetz'=> 'DateTime',
+ 'time' => 'DateTime',
+
+ 'float' => 'float',
+ 'boolean' => 'boolean',
+ 'array' => 'array<string, string>',
+ );
+
+ /**
+ * @var \Metadata\Driver\DriverInterface
+ */
+ protected $delegate;
+
+ /**
+ * @var \Doctrine\Common\Persistence\ObjectManager
@stof
stof added a note

this is wrong as it can be a registry

@schmittjoh Owner

fixed this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@stof stof commented on the diff
Resources/config/services.xml
@@ -59,8 +60,13 @@
<service id="jms_serializer.metadata.annotation_driver" class="%jms_serializer.metadata.annotation_driver.class%" public="false">
<argument type="service" id="annotation_reader" />
</service>
+ <service id="jms_serializer.metadata.doctrine_type_driver" class="%jms_serializer.metadata.doctrine_type_driver.class%" public="false">
+ <argument type="service" id="jms_serializer.metadata.annotation_driver" />
+ <argument type="service" id="doctrine.orm.entity_manager" />
@stof
stof added a note

this does not support multiple entity managers. You should inject the registry instead.

and we should find a way to register it only when the ORM is configured, to avoid breaking other cases (and we should register the mongoDB, CouchDB and PHPCR ones)

@schmittjoh Owner

I will take care of updating the config as @rosstuck is using this bundle with Zend Framework.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
added some commits
@schmittjoh Merge branch '103-doctrine-metadata' of git://github.com/rosstuck/JMS…
…SerializerBundle into doctrineMetadata

Conflicts:
	Tests/bootstrap.php
e76cba2
@schmittjoh some minor tweaks
- removed support for a single object manager always requiring
  a registry
- cleaned up some namings
ce44a10
@schmittjoh
Owner

@rosstuck, I have played with some minor changes. Basically, I have added a few more types to the map, and also removed the support for a single object manager to clean the code up a bit. I've pushed my work to a branch https://github.com/schmittjoh/JMSSerializerBundle/tree/doctrineMetadata

Then, I wanted to update the tests, but we have a problem there. Basically, a lot of logic is inside the data provider getMetadata this is dangerous as these data providers are executed before all tests are run, and PHPUnit provides no error handling if something goes wrong in them. Therefore, they should be as lightweight as possible, best just return some constants (like strings). I do not have time to update the tests at this point to shift more logic into the individual test methods, so I hope you don't mind if I ask you to take a look again. Otherwise, feedback on the other changes that I made is appreciated.

@rosstuck

Just a quick glance at the changes: Looks good to me. I'd have rather kept the single entity manager support, but I can live without. Yay for more types, I think there's several more that could be added.

Regarding MongoDB support, I haven't tested it personally. As I said, it should hold to the same interface, but yes, I think waiting for a PR is a good idea.

Tests: yes, this is tricky. Would it be satisfactory to clean up the code and simply retrieve the metadata with a getMetadata() call or similar at the beginning of the relevant tests?

@VictorMateo

Any news of this issue ?

@rosstuck

Thought I'd pushed these fixes back during my vacation but apparently I did not. Very sorry.

I've merged @schmittjoh's branch, fixed a bug and changed the test suite. This hasn't been tested since The Great Refactoring though, that needs to be checked.

@rosstuck

A bit more investigation. I've merged upstream master locally and started testing. The refactoring changes have indeed broken this, mostly in 1) using setType vs. direct assignment and 2) handling of null values in there. This will take a bit of rework to get up and running again.

@rosstuck

Okay, all fixed up to the latest versions. Ready for review.

@Dynom

+1 Would really like to see this in soon. It's exactly what I need.

@schmittjoh
Owner

@Dynom, if you have time to work on this, I believe this PR is still missing the configuration part as @rosstuck is not using Symfony2, but Zend Framework.

@schmittjoh schmittjoh closed this in cc487e3
@schmittjoh
Owner

Finally, merged this.

Thanks for all your work, @rosstuck!

@Mihailoff

This config has been replaced by schmittjoh@cc487e3#L2R105 which uses the EntityRegistry. I haven't tested it against the ODM but it uses the common interface so (in theory) this should work but may need more types added to the map.

@Mihailoff

Also i have this error: the service "jms_serializer.metadata_driver" has a dependency on a non-existent service "doctrine"

Composer has "doctrine/doctrine-bundle": "1.0.*"

@jdewit

Getting the same error.

@mthenw

Same here

@schmittjoh
Owner

Relates to #251

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 23, 2012
  1. @rosstuck
Commits on Jun 1, 2012
  1. @phiamo
Commits on Jun 4, 2012
  1. Merge pull request #2 from phiamo/doctrine_type_driver

    Ross Tuck authored
    Adds Symfony config for doctrine driver
Commits on Jun 27, 2012
  1. @rosstuck
  2. @rosstuck

    Refactored DoctrineType driver based on feedback

    rosstuck authored
    -Renamed several variables
    -Now uses ClassMetadata interface instead of properties
    -Fixed Docblock class names
Commits on Jun 28, 2012
  1. @rosstuck
  2. @rosstuck
Commits on Jul 5, 2012
  1. @rosstuck
Commits on Jul 15, 2012
  1. @rosstuck
Commits on Jul 17, 2012
  1. @rosstuck

    Merge remote-tracking branch 'upstream/master' into 103-doctrine-meta…

    rosstuck authored
    …data
    
    Conflicts:
    	composer.json
Commits on Jul 28, 2012
  1. @rosstuck

    Remove EntityManager only restriction.

    rosstuck authored
    Now that this class uses the ClassMetadata public interface it should work
    with all types of Doctrine persistence managers
  2. @rosstuck

    Added ManagerRegistry support

    rosstuck authored
Commits on Jul 30, 2012
  1. Merge branch '103-doctrine-metadata' of git://github.com/rosstuck/JMS…

    authored
    …SerializerBundle into doctrineMetadata
    
    Conflicts:
    	Tests/bootstrap.php
  2. some minor tweaks

    authored
    - removed support for a single object manager always requiring
      a registry
    - cleaned up some namings
Commits on Oct 30, 2012
  1. @rosstuck

    Fix incorrect parameter in DoctrineTypeDriver

    rosstuck authored
    Reflection class was passed in instead of class name
  2. @rosstuck
  3. @rosstuck
Commits on Nov 11, 2012
  1. @rosstuck
  2. @rosstuck
  3. @rosstuck
This page is out of date. Refresh to see the latest.
View
126 Metadata/Driver/DoctrineTypeDriver.php
@@ -0,0 +1,126 @@
+<?php
+
+/*
+ * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace JMS\SerializerBundle\Metadata\Driver;
+
+use Doctrine\Common\Persistence\ManagerRegistry;
+use Metadata\Driver\DriverInterface;
+
+/**
+ * This class decorates any other driver. If the inner driver does not provide a
+ * a property type, the decorator will guess based on Doctrine 2 metadata.
+ */
+class DoctrineTypeDriver implements DriverInterface
@schmittjoh Owner

Maybe call this DoctrineORMTypeDriver? If I'm not mistaken, then it only applies to the ORM, no?

@rosstuck
rosstuck added a note

In theory, this could be ported to MongoDB, etc. If we go by the Common persistence interfaces, DoctrineObjectManagerTypeDriver would be correct. Real mouthful though. :)

@schmittjoh Owner

fair point, let's leave it as is :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+{
+ /**
+ * Map of doctrine 2 field types to JMS\SerializerBundle types
+ * @var array
+ */
+ protected $fieldMapping = array(
+ 'string' => 'string',
+ 'text' => 'string',
+ 'blob' => 'string',
+
+ 'integer' => 'integer',
+ 'smallint' => 'integer',
+ 'bigint' => 'integer',
+
+ 'datetime' => 'DateTime',
+ 'datetimetz' => 'DateTime',
+ 'time' => 'DateTime',
+
+ 'float' => 'float',
+ 'decimal' => 'float',
+
+ 'boolean' => 'boolean',
+
+ 'array' => 'array',
+ 'json_array' => 'array',
+ 'simple_array' => 'array<string>',
+ );
+
+ /**
+ * @var DriverInterface
+ */
+ protected $delegate;
+
+ /**
+ * @var ManagerRegistry
+ */
+ protected $registry;
+
+ public function __construct(DriverInterface $delegate, ManagerRegistry $registry)
+ {
+ $this->delegate = $delegate;
+ $this->registry = $registry;
+ }
+
+ public function loadMetadataForClass(\ReflectionClass $class)
+ {
+ $classMetadata = $this->delegate->loadMetadataForClass($class);
+
+ // Abort if the given class is not a mapped entity
+ if (!$doctrineMetadata = $this->tryLoadingDoctrineMetadata($class->name)) {
+ return $classMetadata;
+ }
+
+ // We base our scan on the internal driver's property list so that we
+ // respect any internal white/blacklisting like in the AnnotationDriver
+ foreach ($classMetadata->propertyMetadata as $propertyMetadata) {
+ // If the inner driver provides a type, don't guess anymore.
+ if ($propertyMetadata->type) {
+ continue;
+ }
+
+ $propertyName = $propertyMetadata->name;
+ if ($doctrineMetadata->hasField($propertyName) && $fieldType = $this->normalizeFieldType($doctrineMetadata->getTypeOfField($propertyName))) {
+ $propertyMetadata->setType($fieldType);
+ } elseif ($doctrineMetadata->hasAssociation($propertyName)) {
+ $targetEntity = $doctrineMetadata->getAssociationTargetClass($propertyName);
+ if ($doctrineMetadata->isSingleValuedAssociation($propertyName)) {
+ $propertyMetadata->setType($targetEntity);
+ } else {
+ $propertyMetadata->setType("ArrayCollection<{$targetEntity}>");
+ }
+ }
+ }
+
+ return $classMetadata;
+ }
+
+ private function tryLoadingDoctrineMetadata($className) {
+ if (!$manager = $this->registry->getManagerForClass($className)) {
+ return null;
+ }
+
+ if ($manager->getMetadataFactory()->isTransient($className)) {
+ return null;
+ }
+
+ return $manager->getClassMetadata($className);
+ }
+
+ private function normalizeFieldType($type)
+ {
+ if (!isset($this->fieldMapping[$type])) {
+ return;
+ }
+
+ return $this->fieldMapping[$type];
+ }
+}
View
6 Resources/config/services.xml
@@ -7,6 +7,7 @@
<parameters>
<parameter key="jms_serializer.metadata.file_locator.class">Metadata\Driver\FileLocator</parameter>
<parameter key="jms_serializer.metadata.annotation_driver.class">JMS\SerializerBundle\Metadata\Driver\AnnotationDriver</parameter>
+ <parameter key="jms_serializer.metadata.doctrine_type_driver.class">JMS\SerializerBundle\Metadata\Driver\DoctrineTypeDriver</parameter>
<parameter key="jms_serializer.metadata.chain_driver.class">Metadata\Driver\DriverChain</parameter>
<parameter key="jms_serializer.metadata.yaml_driver.class">JMS\SerializerBundle\Metadata\Driver\YamlDriver</parameter>
<parameter key="jms_serializer.metadata.xml_driver.class">JMS\SerializerBundle\Metadata\Driver\XmlDriver</parameter>
@@ -91,8 +92,13 @@
<service id="jms_serializer.metadata.annotation_driver" class="%jms_serializer.metadata.annotation_driver.class%" public="false">
<argument type="service" id="annotation_reader" />
</service>
+ <service id="jms_serializer.metadata.doctrine_type_driver" class="%jms_serializer.metadata.doctrine_type_driver.class%" public="false">
+ <argument type="service" id="jms_serializer.metadata.annotation_driver" />
+ <argument type="service" id="doctrine.orm.entity_manager" />
@stof
stof added a note

this does not support multiple entity managers. You should inject the registry instead.

and we should find a way to register it only when the ORM is configured, to avoid breaking other cases (and we should register the mongoDB, CouchDB and PHPCR ones)

@schmittjoh Owner

I will take care of updating the config as @rosstuck is using this bundle with Zend Framework.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ </service>
<service id="jms_serializer.metadata.chain_driver" class="%jms_serializer.metadata.chain_driver.class%" public="false">
<argument type="collection">
+ <argument type="service" id="jms_serializer.metadata.doctrine_type_driver" />
<argument type="service" id="jms_serializer.metadata.yaml_driver" />
<argument type="service" id="jms_serializer.metadata.xml_driver" />
<argument type="service" id="jms_serializer.metadata.php_driver" />
View
49 Tests/Fixtures/Doctrine/Author.php
@@ -0,0 +1,49 @@
+<?php
+
+/*
+ * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace JMS\SerializerBundle\Tests\Fixtures\Doctrine;
+
+use JMS\SerializerBundle\Annotation\Type;
+use JMS\SerializerBundle\Annotation\SerializedName;
+
+use Doctrine\ORM\Mapping as ORM;
+
+/** @ORM\Entity */
+class Author
+{
+ /**
+ * @ORM\Id @ORM\Column(type="integer")
+ */
+ protected $id;
+
+ /**
+ * @ORM\Column(type="string")
+ * @SerializedName("full_name")
+ */
+ private $name;
+
+ public function __construct($name)
+ {
+ $this->name = $name;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+}
View
102 Tests/Fixtures/Doctrine/BlogPost.php
@@ -0,0 +1,102 @@
+<?php
+
+/*
+ * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace JMS\SerializerBundle\Tests\Fixtures\Doctrine;
+
+use JMS\SerializerBundle\Annotation\SerializedName;
+use JMS\SerializerBundle\Annotation\XmlRoot;
+use JMS\SerializerBundle\Annotation\XmlAttribute;
+use JMS\SerializerBundle\Annotation\XmlList;
+use JMS\SerializerBundle\Annotation\Groups;
+use JMS\SerializerBundle\Annotation\Type;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity
+ * @XmlRoot("blog-post")
+ */
+class BlogPost
+{
+ /**
+ * @ORM\Id @ORM\Column(type="integer")
+ */
+ protected $id;
+
+ /**
+ * @ORM\Column(type="string")
+ * @Groups({"comments","post"})
+ */
+ private $title;
+
+ /**
+ * @ORM\Column(type="some_custom_type")
+ */
+ protected $slug;
+
+ /**
+ * @ORM\Column(type="datetime")
+ * @XmlAttribute
+ */
+ private $createdAt;
+
+ /**
+ * @ORM\Column(type="boolean")
+ * @Type("integer")
+ * This boolean to integer conversion is one of the few changes between this
+ * and the standard BlogPost class. It's used to test the override behavior
+ * of the DoctrineTypeDriver so notice it, but please don't change it.
+ *
+ * @SerializedName("is_published")
+ * @Groups({"post"})
+ * @XmlAttribute
+ */
+ private $published;
+
+ /**
+ * @ORM\OneToMany(targetEntity="Comment", mappedBy="blogPost")
+ * @XmlList(inline=true, entry="comment")
+ * @Groups({"comments"})
+ */
+ private $comments;
+
+ /**
+ * @ORM\OneToOne(targetEntity="Author")
+ * @Groups({"post"})
+ */
+ private $author;
+
+ public function __construct($title, Author $author, \DateTime $createdAt)
+ {
+ $this->title = $title;
+ $this->author = $author;
+ $this->published = false;
+ $this->comments = new ArrayCollection();
+ $this->createdAt = $createdAt;
+ }
+
+ public function setPublished()
+ {
+ $this->published = true;
+ }
+
+ public function addComment(Comment $comment)
+ {
+ $this->comments->add($comment);
+ }
+}
View
57 Tests/Fixtures/Doctrine/Comment.php
@@ -0,0 +1,57 @@
+<?php
+
+/*
+ * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace JMS\SerializerBundle\Tests\Fixtures\Doctrine;
+
+use JMS\SerializerBundle\Annotation\Type;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\ORM\Mapping as ORM;
+
+/** @ORM\Entity */
+class Comment
+{
+ /**
+ * @ORM\Id @ORM\Column(type="integer")
+ */
+ protected $id;
+
+ /**
+ * @ORM\Column(type="Author")
+ */
+ private $author;
+
+ /** @ORM\ManyToOne(targetEntity="BlogPost") */
+ private $blogPost;
+
+ /**
+ * @ORM\Column(type="string")
+ */
+ private $text;
+
+ public function __construct(Author $author, $text)
+ {
+ $this->author = $author;
+ $this->text = $text;
+ $this->blogPost = new ArrayCollection();
+ }
+
+ public function getAuthor()
+ {
+ return $this->author;
+ }
+}
View
132 Tests/Metadata/Driver/DoctrineDriverTest.php
@@ -0,0 +1,132 @@
+<?php
+
+/*
+ * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace JMS\SerializerBundle\Tests\Metadata\Driver;
+
+use JMS\SerializerBundle\Metadata\Driver\AnnotationDriver;
+use JMS\SerializerBundle\Metadata\Driver\DoctrineTypeDriver;
+use Doctrine\Common\Annotations\AnnotationReader;
+use Doctrine\ORM\Configuration;
+use Doctrine\ORM\EntityManager;
+use Doctrine\ORM\Mapping\Driver\AnnotationDriver as DoctrineDriver;
+
+class DoctrineDriverTest extends \PHPUnit_Framework_TestCase
+{
+ public function getMetadata()
+ {
+ $refClass = new \ReflectionClass('JMS\SerializerBundle\Tests\Fixtures\Doctrine\BlogPost');
+ $metadata = $this->getDoctrineDriver()->loadMetadataForClass($refClass);
+
+ return $metadata;
+ }
+
+ public function testTypelessPropertyIsGivenTypeFromDoctrineMetadata()
+ {
+ $metadata = $this->getMetadata();
+
+ $this->assertEquals(
+ array('name'=> 'DateTime', 'params' => array()),
+ $metadata->propertyMetadata['createdAt']->type
+ );
+ }
+
+ public function testSingleValuedAssociationIsProperlyHinted()
+ {
+ $metadata = $this->getMetadata();
+ $this->assertEquals(
+ array('name'=> 'JMS\SerializerBundle\Tests\Fixtures\Doctrine\Author', 'params' => array()),
+ $metadata->propertyMetadata['author']->type
+ );
+ }
+
+ public function testMultiValuedAssociationIsProperlyHinted()
+ {
+ $metadata = $this->getMetadata();
+
+ $this->assertEquals(
+ array('name'=> 'ArrayCollection', 'params' => array(
+ array('name' => 'JMS\SerializerBundle\Tests\Fixtures\Doctrine\Comment', 'params' => array()))
+ ),
+ $metadata->propertyMetadata['comments']->type
+ );
+ }
+
+ public function testTypeGuessByDoctrineIsOverwrittenByDelegateDriver()
+ {
+ $metadata = $this->getMetadata();
+
+ // This would be guessed as boolean but we've overriden it to integer
+ $this->assertEquals(
+ array('name'=> 'integer', 'params' => array()),
+ $metadata->propertyMetadata['published']->type
+ );
+ }
+
+ public function testUnknownDoctrineTypeDoesNotResultInAGuess()
+ {
+ $metadata = $this->getMetadata();
+ $this->assertNull($metadata->propertyMetadata['slug']->type);
+ }
+
+ public function testNonDoctrineEntityClassIsNotModified()
+ {
+ // Note: Using regular BlogPost fixture here instead of Doctrine fixture
+ // because it has no Doctrine metadata.
+ $refClass = new \ReflectionClass('JMS\SerializerBundle\Tests\Fixtures\BlogPost');
+
+ $plainMetadata = $this->getAnnotationDriver()->loadMetadataForClass($refClass);
+ $doctrineMetadata = $this->getDoctrineDriver()->loadMetadataForClass($refClass);
+
+ $this->assertEquals($plainMetadata, $doctrineMetadata);
+ }
+
+ protected function getEntityManager()
+ {
+ $config = new Configuration();
+ $config->setProxyDir(sys_get_temp_dir() . '/JMSDoctrineTestProxies');
+ $config->setProxyNamespace('JMS\Tests\Proxies');
+ $config->setMetadataDriverImpl(
+ new DoctrineDriver(new AnnotationReader(), __DIR__.'/../../Fixtures/Doctrine')
+ );
+
+ $conn = array(
+ 'driver' => 'pdo_sqlite',
+ 'memory' => true,
+ );
+
+ return EntityManager::create($conn, $config);
+ }
+
+ public function getAnnotationDriver()
+ {
+ return new AnnotationDriver(new AnnotationReader());
+ }
+
+ protected function getDoctrineDriver()
+ {
+ $registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry');
+ $registry->expects($this->atLeastOnce())
+ ->method('getManagerForClass')
+ ->will($this->returnValue($this->getEntityManager()));
+
+ return new DoctrineTypeDriver(
+ $this->getAnnotationDriver(),
+ $registry
+ );
+ }
+}
View
20 Tests/bootstrap.php
@@ -16,8 +16,6 @@
* limitations under the License.
*/
-use Doctrine\Common\Annotations\AnnotationRegistry;
-
call_user_func(function() {
if ( ! is_file($autoloadFile = __DIR__.'/../vendor/autoload.php')) {
throw new \RuntimeException('Did not find vendor/autoload.php. Did you run "composer install --dev"?');
@@ -25,19 +23,5 @@
require_once $autoloadFile;
- $bundleLoader = function($v) {
- if (0 !== strpos($v, 'JMS\\SerializerBundle')) {
- return false;
- }
-
- if ( ! is_file($file = __DIR__.'/../'.str_replace('\\', '/', substr($v, 21)).'.php')) {
- return false;
- }
-
- require_once $file;
-
- return true;
- };
- spl_autoload_register($bundleLoader);
- AnnotationRegistry::registerLoader($bundleLoader);
-});
+ Doctrine\Common\Annotations\AnnotationRegistry::registerLoader('class_exists');
+});
Something went wrong with that request. Please try again.