diff --git a/bin/phpcrodm.php b/bin/phpcrodm.php index 3890c9e6e..181bad317 100755 --- a/bin/phpcrodm.php +++ b/bin/phpcrodm.php @@ -71,6 +71,7 @@ new \Doctrine\ODM\PHPCR\Tools\Console\Command\GenerateProxiesCommand(), new \Doctrine\ODM\PHPCR\Tools\Console\Command\DumpQueryBuilderReferenceCommand(), new \Doctrine\ODM\PHPCR\Tools\Console\Command\InfoDoctrineCommand(), + new \Doctrine\ODM\PHPCR\Tools\Console\Command\VerifyUniqueNodeTypesMappingCommand(), new \Doctrine\ODM\PHPCR\Tools\Console\Command\RegisterSystemNodeTypesCommand(), )); if (isset($extraCommands) && ! empty($extraCommands)) { diff --git a/lib/Doctrine/ODM/PHPCR/Tools/Console/Command/VerifyUniqueNodeTypesMappingCommand.php b/lib/Doctrine/ODM/PHPCR/Tools/Console/Command/VerifyUniqueNodeTypesMappingCommand.php new file mode 100644 index 000000000..5995989dd --- /dev/null +++ b/lib/Doctrine/ODM/PHPCR/Tools/Console/Command/VerifyUniqueNodeTypesMappingCommand.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\ODM\PHPCR\Tools\Console\Command; + +use Doctrine\ODM\PHPCR\Tools\Helper\UniqueNodeTypeHelper; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Show information about mapped documents + */ +class VerifyUniqueNodeTypesMappingCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:phpcr:mapping:verify_unique_node_types') + ->setDescription('Verify that documents with unique node types are correctly mapped') + ->setHelp(<<%command.name% command checks all mapped PHPCR-ODM documents +and verifies that any marked as having unique node types are, in fact, unique. +EOT + ); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $documentManager = $this->getHelper('phpcr')->getDocumentManager(); + + UniqueNodeTypeHelper::checkNodeTypeMappings($documentManager, $output); + + return 0; + } +} diff --git a/lib/Doctrine/ODM/PHPCR/Tools/Helper/UniqueNodeTypeHelper.php b/lib/Doctrine/ODM/PHPCR/Tools/Helper/UniqueNodeTypeHelper.php new file mode 100644 index 000000000..913324f3c --- /dev/null +++ b/lib/Doctrine/ODM/PHPCR/Tools/Helper/UniqueNodeTypeHelper.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\ODM\PHPCR\Tools\Helper; + +use Doctrine\ODM\PHPCR\DocumentManagerInterface; +use Doctrine\ODM\PHPCR\Mapping\MappingException; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Provides unique node type mapping verification. + */ +class UniqueNodeTypeHelper +{ + /** + * Check each mapped PHPCR-ODM document for the given document manager, + * throwing an exception if any document is set to use a unique node + * type but the node type is re-used. If an OutputInterface is provided, + * write some basic information to it. + * + * @param DocumentManagerInterface $documentManager The document manager to check mappings for. + * @param OutputInterface $output If provided, output will be written here. + * + * @throws MappingException + */ + public static function checkNodeTypeMappings(DocumentManagerInterface $documentManager, OutputInterface $output = null) + { + $knownNodeTypes = array(); + $allMetadata = $documentManager->getMetadataFactory()->getAllMetadata(); + + foreach ($allMetadata as $classMetadata) { + if ($classMetadata->hasUniqueNodeType() && isset($knownNodeTypes[$classMetadata->getNodeType()])) { + throw new MappingException(sprintf( + 'The class "%s" is mapped with uniqueNodeType set to true, but the node type "%s" is used by "%s" as well.', + $classMetadata->name, + $classMetadata->getNodeType(), + $knownNodeTypes[$classMetadata->getNodeType()] + )); + } + + $knownNodeTypes[$classMetadata->getNodeType()] = $classMetadata->name; + + if (!is_null($output)) { + + $output->writeln(sprintf( + 'The document %s uses %snode type %s', + $classMetadata->name, + $classMetadata->hasUniqueNodeType() ? 'uniquely mapped ' : '', + $classMetadata->getNodeType() + )); + + } + } + } +} diff --git a/tests/Doctrine/Tests/ODM/PHPCR/Tools/Helper/UniqueNodeTypeHelperTest.php b/tests/Doctrine/Tests/ODM/PHPCR/Tools/Helper/UniqueNodeTypeHelperTest.php new file mode 100644 index 000000000..09c9dc2b7 --- /dev/null +++ b/tests/Doctrine/Tests/ODM/PHPCR/Tools/Helper/UniqueNodeTypeHelperTest.php @@ -0,0 +1,98 @@ +getMockBuilder('Doctrine\ODM\PHPCR\Mapping\ClassMetadataFactory') + ->disableOriginalConstructor() + ->setMethods(array('getAllMetadata')) + ->getMock(); + $classMetadataFactory->expects($this->once()) + ->method('getAllMetadata') + ->will($this->returnValue($metadata)); + + $documentManager = $this->getMockBuilder('Doctrine\ODM\PHPCR\DocumentManager') + ->disableOriginalConstructor() + ->setMethods(array('getMetadataFactory')) + ->getMock(); + $documentManager->expects($this->once()) + ->method('getMetadataFactory') + ->will($this->returnValue($classMetadataFactory)); + + return $documentManager; + } + + /** + * Verify that a MappingException is correctly thrown when more than + * one document uses the same node type, but one is marked as unique. + * + * @expectedException Doctrine\ODM\PHPCR\Mapping\MappingException + * @expectedExceptionMessage The class "Doctrine\PHPCR\Models\ClassC" is mapped with uniqueNodeType set to true, but the + * node type "nt:unstructured" is used by "Doctrine\PHPCR\Models\ClassA" as well. + */ + public function testCheckNodeTypeMappingsWithDuplicate() + { + $metadataA = new ClassMetadata('Doctrine\PHPCR\Models\ClassA'); + $metadataA->setNodeType('nt:unstructured'); + + $metadataB = new ClassMetadata('Doctrine\PHPCR\Models\ClassB'); + $metadataB->setNodeType('custom:type'); + $metadataB->setUniqueNodeType(true); + + $metadataC = new ClassMetadata('Doctrine\PHPCR\Models\ClassC'); + $metadataC->setNodeType('nt:unstructured'); + $metadataC->setUniqueNodeType(true); + + $documentManager = $this->configureDocumentManager(array( + $metadataA, + $metadataB, + $metadataC + )); + + UniqueNodeTypeHelper::checkNodeTypeMappings($documentManager); + } + + /** + * Verify that no exception results from a correctly-mapped set + * of documents. + */ + public function testCheckNodeTypeMappingsWithoutDuplicate() + { + $metadataA = new ClassMetadata('Doctrine\PHPCR\Models\ClassA'); + $metadataA->setNodeType('nt:unstructured'); + + $metadataB = new ClassMetadata('Doctrine\PHPCR\Models\ClassB'); + $metadataB->setNodeType('custom:type'); + $metadataB->setUniqueNodeType(true); + + $metadataC = new ClassMetadata('Doctrine\PHPCR\Models\ClassC'); + $metadataA->setNodeType('nt:unstructured'); + + $documentManager = $this->configureDocumentManager(array( + $metadataA, + $metadataB, + $metadataC + )); + + UniqueNodeTypeHelper::checkNodeTypeMappings($documentManager); + } +} \ No newline at end of file