├── LICENSE ├── README.markdown ├── UPGRADE-5.0.md ├── composer.json ├── config ├── cache_warmer.php ├── command.php ├── form.php ├── logger.php ├── messenger.php ├── mongodb.php ├── profiler.php ├── schema │ └── mongodb-1.0.xsd ├── validator.php └── value_resolver.php ├── docs ├── .doctor-rst.yaml ├── config.rst ├── console.rst ├── cookbook │ └── registration_form.rst ├── events.rst ├── first_steps.rst ├── form_validation.rst ├── index.rst ├── installation.rst ├── messenger.rst └── security_bundle.rst ├── phpstan-baseline.neon ├── phpstan.neon.dist ├── src ├── APM │ ├── CommandLoggerRegistry.php │ ├── PSRCommandLogger.php │ └── StopwatchCommandLogger.php ├── ArgumentResolver │ └── DocumentValueResolver.php ├── Attribute │ ├── AsDocumentListener.php │ └── MapDocument.php ├── CacheWarmer │ ├── HydratorCacheWarmer.php │ ├── PersistentCollectionCacheWarmer.php │ └── ProxyCacheWarmer.php ├── Command │ ├── ClearMetadataCacheDoctrineODMCommand.php │ ├── CreateSchemaDoctrineODMCommand.php │ ├── DoctrineODMCommand.php │ ├── DropSchemaDoctrineODMCommand.php │ ├── GenerateHydratorsDoctrineODMCommand.php │ ├── GenerateProxiesDoctrineODMCommand.php │ ├── InfoDoctrineODMCommand.php │ ├── LoadDataFixturesDoctrineODMCommand.php │ ├── QueryDoctrineODMCommand.php │ ├── ShardDoctrineODMCommand.php │ └── UpdateSchemaDoctrineODMCommand.php ├── DataCollector │ └── CommandDataCollector.php ├── DependencyInjection │ ├── Compiler │ │ ├── CreateHydratorDirectoryPass.php │ │ ├── CreateProxyDirectoryPass.php │ │ ├── DoctrineMongoDBMappingsPass.php │ │ ├── FixturesCompilerPass.php │ │ └── ServiceRepositoryCompilerPass.php │ ├── Configuration.php │ └── DoctrineMongoDBExtension.php ├── DoctrineMongoDBBundle.php ├── Fixture │ ├── Fixture.php │ ├── FixtureGroupInterface.php │ └── ODMFixtureInterface.php ├── Form │ ├── ChoiceList │ │ └── MongoDBQueryBuilderLoader.php │ ├── DoctrineMongoDBExtension.php │ ├── DoctrineMongoDBTypeGuesser.php │ └── Type │ │ └── DocumentType.php ├── Loader │ ├── SymfonyFixturesLoader.php │ └── SymfonyFixturesLoaderInterface.php ├── ManagerConfigurator.php ├── ManagerRegistry.php ├── Mapping │ └── Driver │ │ └── XmlDriver.php ├── Repository │ ├── ContainerRepositoryFactory.php │ ├── ServiceDocumentRepository.php │ ├── ServiceDocumentRepositoryInterface.php │ ├── ServiceGridFSRepository.php │ └── ServiceRepositoryTrait.php └── Validator │ └── Constraints │ └── Unique.php ├── templates └── Collector │ ├── icon.svg │ └── mongodb.html.twig └── tests ├── APM └── StopwatchCommandLoggerTest.php ├── CacheWarmer ├── HydratorCacheWarmerTest.php ├── PersistentCollectionCacheWarmerTest.php └── ProxyCacheWarmerTest.php ├── Command ├── CommandTestKernel.php ├── DoctrineODMCommandTest.php ├── InfoDoctrineODMCommandTest.php └── LoadDataFixturesDoctrineODMCommandTest.php ├── ContainerTest.php ├── DataCollector └── CommandDataCollectorTest.php ├── DependencyInjection ├── AbstractMongoDBExtensionTestCase.php ├── ConfigurationTest.php ├── DoctrineMongoDBExtensionTest.php ├── Fixtures │ ├── Bundles │ │ ├── AttributesBundle │ │ │ ├── AttributesBundle.php │ │ │ └── Document │ │ │ │ └── TestDocument.php │ │ ├── DocumentListenerBundle │ │ │ ├── Document │ │ │ │ └── TestDocument.php │ │ │ ├── DocumentListenerBundle.php │ │ │ └── EventListener │ │ │ │ └── TestAttributeListener.php │ │ ├── NewXmlBundle │ │ │ ├── config │ │ │ │ └── doctrine │ │ │ │ │ └── mapping.mongodb.xml │ │ │ └── src │ │ │ │ ├── Document │ │ │ │ └── TestDocument.php │ │ │ │ └── NewXmlBundle.php │ │ ├── OtherXmlBundle │ │ │ ├── Document │ │ │ │ └── TestDocument.php │ │ │ ├── OtherXmlBundle.php │ │ │ └── Resources │ │ │ │ └── config │ │ │ │ └── doctrine │ │ │ │ └── mapping.mongodb.xml │ │ ├── RepositoryServiceBundle │ │ │ ├── Document │ │ │ │ ├── TestCustomClassRepoDocument.php │ │ │ │ ├── TestCustomServiceRepoDocument.php │ │ │ │ ├── TestCustomServiceRepoFile.php │ │ │ │ ├── TestDefaultRepoDocument.php │ │ │ │ ├── TestDefaultRepoFile.php │ │ │ │ └── TestUnmappedDocument.php │ │ │ ├── Repository │ │ │ │ ├── TestCustomClassRepoRepository.php │ │ │ │ ├── TestCustomServiceRepoDocumentRepository.php │ │ │ │ ├── TestCustomServiceRepoGridFSRepository.php │ │ │ │ └── TestUnmappedDocumentRepository.php │ │ │ └── RepositoryServiceBundle.php │ │ └── XmlBundle │ │ │ ├── Document │ │ │ └── TestDocument.php │ │ │ ├── Resources │ │ │ └── config │ │ │ │ └── doctrine │ │ │ │ └── mapping.mongodb.xml │ │ │ └── XmlBundle.php │ ├── TestKernel.php │ └── config │ │ ├── xml │ │ ├── full.xml │ │ ├── mongodb_service_multiple_connections.xml │ │ ├── mongodb_service_simple_single_connection.xml │ │ ├── mongodb_service_single_connection.xml │ │ ├── odm_filters.xml │ │ ├── odm_imports.xml │ │ ├── odm_imports_import.xml │ │ ├── odm_resolve_target_document.xml │ │ └── odm_types.xml │ │ └── yml │ │ ├── full.yml │ │ ├── mongodb_service_multiple_connections.yml │ │ ├── mongodb_service_simple_single_connection.yml │ │ ├── mongodb_service_single_connection.yml │ │ ├── odm_filters.yml │ │ ├── odm_imports.yml │ │ ├── odm_imports_import.yml │ │ ├── odm_resolve_target_document.yml │ │ └── odm_types.yml ├── XmlMongoDBExtensionTest.php └── YamlMongoDBExtensionTest.php ├── DocumentValueResolverFunctionalTest.php ├── FixtureIntegrationTest.php ├── Fixtures ├── Cache │ └── Collections.php ├── CommandBundle │ ├── CommandBundle.php │ ├── DataFixtures │ │ ├── OtherFixtures.php │ │ └── UserFixtures.php │ └── Document │ │ └── User.php ├── DataCollector │ └── Category.php ├── Filter │ ├── BasicFilter.php │ ├── ComplexFilter.php │ └── DisabledFilter.php ├── FooBundle │ ├── Controller │ │ └── DocumentValueResolverController.php │ ├── DataFixtures │ │ ├── DependentOnRequiredConstructorArgsFixtures.php │ │ ├── OtherFixtures.php │ │ ├── RequiredConstructorArgsFixtures.php │ │ └── WithDependenciesFixtures.php │ ├── Document │ │ └── User.php │ ├── FooBundle.php │ └── config │ │ └── services.php ├── Form │ ├── Category.php │ ├── Document.php │ └── Guesser.php ├── Repository │ ├── CustomGridFSRepository.php │ └── CustomRepository.php ├── Security │ └── User.php └── Validator │ └── Document.php ├── Form └── Type │ ├── DocumentTypeTest.php │ ├── GuesserTestType.php │ └── TypeGuesserTest.php ├── ManagerRegistryTest.php ├── Mapping └── Driver │ ├── AbstractDriverTestCase.php │ ├── Fixtures │ └── xml │ │ ├── Foo.Bar.mongodb.xml │ │ └── Foo.mongodb.xml │ └── XmlDriverTest.php ├── Repository └── ContainerRepositoryFactoryTest.php ├── ServiceRepositoryTest.php ├── TestCase.php ├── Validator └── Constraints │ └── UniqueTest.php └── bootstrap.php /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006-2016 Doctrine Project 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | DoctrineMongoDBBundle 2 | ===================== 3 | 4 | This bundle integrates the [Doctrine2 MongoDB Object Document Mapper (ODM) library](https://github.com/doctrine/mongodb-odm) 5 | into Symfony so that you can persist and retrieve objects to and from MongoDB. 6 | 7 | [![Build Status](https://github.com/doctrine/DoctrineMongoDBBundle/workflows/Continuous%20Integration/badge.svg)](https://github.com/doctrine/DoctrineMongoDBBundle/actions) 8 | 9 | Documentation on how to install and use this bundle is available in the 10 | Symfony [documentation](http://symfony.com/doc/current/bundles/DoctrineMongoDBBundle/index.html). 11 | 12 | Compatibility 13 | ============= 14 | 15 | The current version of this bundle has the following requirements: 16 | * PHP 8.1 or newer is required 17 | * `ext-mongodb` 1.16 or newer 18 | * Symfony 6.4 or newer is required 19 | 20 | Support for older Symfony, PHP and MongoDB versions is provided via the `4.7.x` 21 | releases (tracked in the `4.7.x` branch). This version sees bug and security fixes 22 | only. 23 | -------------------------------------------------------------------------------- /UPGRADE-5.0.md: -------------------------------------------------------------------------------- 1 | UPGRADE FROM 4.x to 5.0 2 | ======================= 3 | 4 | ## PHP version and dependencies 5 | 6 | * Add support for Symfony 7.0 and require at least Symfony 6.4 7 | * `doctrine/mongodb-odm` 2.6 and `doctrine/persistence` 3.0 are required 8 | 9 | ## Annotations 10 | 11 | * Remove support of Annotation mapping, you should use Attributes or XML instead. 12 | 13 | ## Commands 14 | 15 | * All command and compiler pass classes are internal and final. They cannot be 16 | used directly or extended. 17 | * The `doctrine:mongodb:tail-cursor` command and 18 | `Doctrine\Bundle\MongoDBBundle\Cursor\TailableCursorProcessorInterface` 19 | interface have been dropped. You should use 20 | [change streams](https://docs.mongodb.com/manual/changeStreams/) instead. 21 | * The `setContainer`, `getContainer`, `getDoctrineDocumentManagers`, 22 | `findBundle` and `findBasePathForBundle` methods from 23 | `Doctrine\Bundle\MongoDBBundle\Command\DoctrineODMCommand` have been 24 | removed without replacement. 25 | 26 | ## Configuration 27 | 28 | Remove all `doctrine_mongodb.odm.*` parameters. 29 | 30 | Deprecated options have been removed: 31 | 32 | | namespace | removed | replaced by | 33 | |--------------------------|-------------|--------------------| 34 | | `default_commit_options` | `fsync` | `j` | 35 | | `default_commit_options` | `safe` | `w` | 36 | | `connections.*.options` | `fsync` | `journal` | 37 | | `connections.*.options` | `slaveOkay` | `readPreference` | 38 | | `connections.*.options` | `timeout` | `connectTimeoutMS` | 39 | | `connections.*.options` | `wTimeout` | `wTimeoutMS` | 40 | 41 | ## Event Subscriber 42 | 43 | * Remove `Doctrine\Bundle\MongoDBBundle\EventSubscriber\EventSubscriberInterface`. 44 | Use the `#[AsDocumentListener]` attribute instead. 45 | * Remove parameters `$method` and `$lazy` of `#[AsDocumentListener]`, they are 46 | not used. 47 | 48 | ## Fixtures 49 | 50 | * Remove `--service` option from `doctrine:mongodb:fixtures:load` command 51 | * Remove automatic injection of the container in fixtures classes implementing 52 | `ContainerAwareInterface`. You should use dependency injection instead. 53 | * Remove the `fixture_loader` configuration 54 | 55 | ## Cache 56 | 57 | The `Doctrine\Common\Cache\` providers are not supported anymore. The configuration 58 | uses `Symfony\Component\Cache\Adapter\` providers instead, for PSR-6 compatibility. 59 | 60 | If you want to use redis or memcached, the configuration of `host`, `port` and `instance_class` 61 | must be done for each document manager. The parameters `doctrine_mongodb.odm.cache.memcached_*` 62 | and `doctrine_mongodb.odm.cache.redis_*` are not read anymore. 63 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "doctrine/mongodb-odm-bundle", 3 | "description": "Symfony Doctrine MongoDB Bundle", 4 | "license": "MIT", 5 | "type": "symfony-bundle", 6 | "keywords": [ 7 | "persistence", 8 | "mongodb", 9 | "symfony" 10 | ], 11 | "authors": [ 12 | { 13 | "name": "Bulat Shakirzyanov", 14 | "email": "mallluhuct@gmail.com" 15 | }, 16 | { 17 | "name": "Kris Wallsmith", 18 | "email": "kris@symfony.com" 19 | }, 20 | { 21 | "name": "Jonathan H. Wage", 22 | "email": "jonwage@gmail.com" 23 | } 24 | ], 25 | "homepage": "http://www.doctrine-project.org", 26 | "require": { 27 | "php": "^8.1", 28 | "ext-mongodb": "^1.16 || ^2", 29 | "composer-runtime-api": "^2.0", 30 | "doctrine/mongodb-odm": "^2.6", 31 | "doctrine/persistence": "^3.0 || ^4.0", 32 | "psr/log": "^1.0 || ^2.0 || ^3.0", 33 | "symfony/config": "^6.4 || ^7.0", 34 | "symfony/console": "^6.4 || ^7.0", 35 | "symfony/dependency-injection": "^6.4 || ^7.0", 36 | "symfony/doctrine-bridge": "^6.4 || ^7.0", 37 | "symfony/framework-bundle": "^6.4 || ^7.0", 38 | "symfony/http-kernel": "^6.4 || ^7.0", 39 | "symfony/options-resolver": "^6.4 || ^7.0" 40 | }, 41 | "require-dev": { 42 | "composer/semver": "^3.4", 43 | "doctrine/coding-standard": "^11.0", 44 | "doctrine/data-fixtures": "^1.8 || ^2.0", 45 | "phpstan/phpstan": "^2.0", 46 | "phpunit/phpunit": "^9.5.5", 47 | "symfony/browser-kit": "^6.4 || ^7.0", 48 | "symfony/form": "^6.4 || ^7.0", 49 | "symfony/phpunit-bridge": "^6.4.1 || ^7.0.1", 50 | "symfony/security-bundle": "^6.4 || ^7.0", 51 | "symfony/stopwatch": "^6.4 || ^7.0", 52 | "symfony/validator": "^6.4 || ^7.0", 53 | "symfony/yaml": "^6.4 || ^7.0" 54 | }, 55 | "conflict": { 56 | "doctrine/data-fixtures": "<1.8 || >=3" 57 | }, 58 | "suggest": { 59 | "doctrine/data-fixtures": "Load data fixtures" 60 | }, 61 | "minimum-stability": "stable", 62 | "autoload": { 63 | "psr-4": { 64 | "Doctrine\\Bundle\\MongoDBBundle\\": "src" 65 | } 66 | }, 67 | "autoload-dev": { 68 | "psr-4": { 69 | "Doctrine\\Bundle\\MongoDBBundle\\Tests\\": "tests" 70 | } 71 | }, 72 | "config": { 73 | "allow-plugins": { 74 | "dealerdirect/phpcodesniffer-composer-installer": true 75 | }, 76 | "sort-packages": true 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /config/cache_warmer.php: -------------------------------------------------------------------------------- 1 | services() 14 | 15 | ->set('doctrine_mongodb.odm.proxy_cache_warmer', ProxyCacheWarmer::class) 16 | ->tag('kernel.cache_warmer', ['priority' => 25]) 17 | ->args([ 18 | service('service_container'), 19 | ]) 20 | 21 | ->set('doctrine_mongodb.odm.hydrator_cache_warmer', HydratorCacheWarmer::class) 22 | ->tag('kernel.cache_warmer', ['priority' => 25]) 23 | ->args([ 24 | service('service_container'), 25 | ]) 26 | 27 | ->set('doctrine_mongodb.odm.persistent_collection_cache_warmer', PersistentCollectionCacheWarmer::class) 28 | ->tag('kernel.cache_warmer', ['priority' => 25]) 29 | ->args([ 30 | service('service_container'), 31 | ]); 32 | }; 33 | -------------------------------------------------------------------------------- /config/command.php: -------------------------------------------------------------------------------- 1 | services() 21 | ->set('doctrine_mongodb.odm.command.clear_metadata_cache', ClearMetadataCacheDoctrineODMCommand::class) 22 | ->tag('console.command', ['command' => 'doctrine:mongodb:cache:clear-metadata']) 23 | 24 | ->set('doctrine_mongodb.odm.command.create_schema', CreateSchemaDoctrineODMCommand::class) 25 | ->tag('console.command', ['command' => 'doctrine:mongodb:schema:create']) 26 | 27 | ->set('doctrine_mongodb.odm.command.drop_schema', DropSchemaDoctrineODMCommand::class) 28 | ->tag('console.command', ['command' => 'doctrine:mongodb:schema:drop']) 29 | 30 | ->set('doctrine_mongodb.odm.command.generate_hydrators', GenerateHydratorsDoctrineODMCommand::class) 31 | ->tag('console.command', ['command' => 'doctrine:mongodb:generate:hydrators']) 32 | 33 | ->set('doctrine_mongodb.odm.command.generate_proxies', GenerateProxiesDoctrineODMCommand::class) 34 | ->tag('console.command', ['command' => 'doctrine:mongodb:generate:proxies']) 35 | 36 | ->set('doctrine_mongodb.odm.command.info', InfoDoctrineODMCommand::class) 37 | ->tag('console.command', ['command' => 'doctrine:mongodb:mapping:info']) 38 | ->args([ 39 | service('doctrine_mongodb'), 40 | ]) 41 | 42 | ->set('doctrine_mongodb.odm.command.load_data_fixtures', LoadDataFixturesDoctrineODMCommand::class) 43 | ->tag('console.command', ['command' => 'doctrine:mongodb:fixtures:load']) 44 | ->args([ 45 | service('doctrine_mongodb'), 46 | service('doctrine_mongodb.odm.symfony.fixtures.loader'), 47 | ]) 48 | 49 | ->set('doctrine_mongodb.odm.command.query', QueryDoctrineODMCommand::class) 50 | ->tag('console.command', ['command' => 'doctrine:mongodb:query']) 51 | 52 | ->set('doctrine_mongodb.odm.command.shard', ShardDoctrineODMCommand::class) 53 | ->tag('console.command', ['command' => 'doctrine:mongodb:schema:shard']) 54 | 55 | ->set('doctrine_mongodb.odm.command.update_schema', UpdateSchemaDoctrineODMCommand::class) 56 | ->tag('console.command', ['command' => 'doctrine:mongodb:schema:update']); 57 | }; 58 | -------------------------------------------------------------------------------- /config/form.php: -------------------------------------------------------------------------------- 1 | services() 13 | 14 | ->set('form.type.mongodb_document', DocumentType::class) 15 | ->tag('form.type', ['alias' => 'document']) 16 | ->args([ 17 | service('doctrine_mongodb'), 18 | ]) 19 | 20 | ->set('form.type_guesser.doctrine.mongodb', DoctrineMongoDBTypeGuesser::class) 21 | ->tag('form.type_guesser') 22 | ->args([ 23 | service('doctrine_mongodb'), 24 | ]); 25 | }; 26 | -------------------------------------------------------------------------------- /config/logger.php: -------------------------------------------------------------------------------- 1 | services() 15 | 16 | ->set('doctrine_mongodb.odm.command_logger_registry', CommandLoggerRegistry::class) 17 | ->public() 18 | ->args([ 19 | tagged_iterator('doctrine_mongodb.odm.command_logger'), 20 | ]) 21 | 22 | ->set('doctrine_mongodb.odm.stopwatch_command_logger', StopwatchCommandLogger::class) 23 | ->args([ 24 | service('debug.stopwatch')->nullOnInvalid(), 25 | ]) 26 | 27 | ->set('doctrine_mongodb.odm.psr_command_logger', PSRCommandLogger::class) 28 | ->tag('monolog.logger', ['channel' => 'doctrine']) 29 | ->args([ 30 | service('logger')->nullOnInvalid(), 31 | ]); 32 | }; 33 | -------------------------------------------------------------------------------- /config/messenger.php: -------------------------------------------------------------------------------- 1 | services(); 12 | 13 | $services->set('doctrine_mongodb.messenger.event_subscriber.doctrine_clear_document_manager', DoctrineClearEntityManagerWorkerSubscriber::class) 14 | ->tag('kernel.event_subscriber') 15 | ->args([ 16 | service('doctrine_mongodb'), 17 | ]); 18 | }; 19 | -------------------------------------------------------------------------------- /config/mongodb.php: -------------------------------------------------------------------------------- 1 | services() 20 | 21 | ->alias(DocumentManager::class, 'doctrine_mongodb.odm.document_manager') 22 | 23 | ->alias(ManagerRegistry::class, 'doctrine_mongodb') 24 | 25 | ->set('doctrine_mongodb.odm.connection.event_manager', ContainerAwareEventManager::class) 26 | ->abstract() 27 | ->args([ 28 | service('service_container'), 29 | ]) 30 | 31 | ->set('doctrine_mongodb.odm.container_repository_factory', ContainerRepositoryFactory::class) 32 | ->args([ 33 | abstract_arg('service locator'), 34 | ]) 35 | 36 | ->set('doctrine_mongodb.odm.manager_configurator.abstract', ManagerConfigurator::class) 37 | ->abstract() 38 | ->args([ 39 | abstract_arg('enabled filters'), 40 | ]) 41 | 42 | ->set('doctrine_mongodb.odm.security.user.provider', EntityUserProvider::class) 43 | ->abstract() 44 | ->args([ 45 | service('doctrine_mongodb'), 46 | ]) 47 | 48 | ->set('doctrine_mongodb', ManagerRegistry::class) 49 | ->public() 50 | ->args([ 51 | 'MongoDB', 52 | '%doctrine_mongodb.odm.connections%', 53 | '%doctrine_mongodb.odm.document_managers%', 54 | '%doctrine_mongodb.odm.default_connection%', 55 | '%doctrine_mongodb.odm.default_document_manager%', 56 | abstract_arg('Proxy Interface Name'), 57 | service('service_container'), 58 | ]) 59 | 60 | ->set('doctrine_mongodb.odm.listeners.resolve_target_document', ResolveTargetDocumentListener::class) 61 | 62 | ->set('doctrine_mongodb.odm.symfony.fixtures.loader', SymfonyFixturesLoader::class); 63 | }; 64 | -------------------------------------------------------------------------------- /config/profiler.php: -------------------------------------------------------------------------------- 1 | services() 13 | ->set('doctrine_mongodb.odm.data_collector.command_logger', CommandLogger::class) 14 | 15 | ->set('doctrine_mongodb.odm.data_collector', CommandDataCollector::class) 16 | ->args([ 17 | service('doctrine_mongodb.odm.data_collector.command_logger'), 18 | ]); 19 | }; 20 | -------------------------------------------------------------------------------- /config/validator.php: -------------------------------------------------------------------------------- 1 | services() 13 | 14 | ->set('doctrine_odm.mongodb.validator.unique', UniqueEntityValidator::class) 15 | ->tag('validator.constraint_validator', ['alias' => 'doctrine_odm.mongodb.unique']) 16 | ->args([ 17 | service('doctrine_mongodb'), 18 | ]) 19 | 20 | ->set('doctrine_odm.mongodb.validator_initializer', DoctrineInitializer::class) 21 | ->tag('validator.initializer') 22 | ->args([ 23 | service('doctrine_mongodb'), 24 | ]); 25 | }; 26 | -------------------------------------------------------------------------------- /config/value_resolver.php: -------------------------------------------------------------------------------- 1 | services(); 14 | 15 | $services->set('doctrine_mongodb.odm.entity_value_resolver', EntityValueResolver::class) 16 | ->args([ 17 | service('doctrine_mongodb'), 18 | service('doctrine_mongodb.odm.document_value_resolver.expression_language')->ignoreOnInvalid(), 19 | ]); 20 | 21 | $services->set('doctrine_mongodb.odm.document_value_resolver.expression_language', ExpressionLanguage::class); 22 | 23 | $services->set('doctrine_mongodb.odm.document_value_resolver', DocumentValueResolver::class) 24 | ->tag('controller.argument_value_resolver', ['name' => DocumentValueResolver::class, 'priority' => 110]) 25 | ->args([ 26 | service('doctrine_mongodb.odm.entity_value_resolver'), 27 | ]); 28 | }; 29 | -------------------------------------------------------------------------------- /docs/.doctor-rst.yaml: -------------------------------------------------------------------------------- 1 | rules: 2 | no_inheritdoc: ~ 3 | avoid_repetetive_words: ~ 4 | blank_line_after_directive: ~ 5 | short_array_syntax: ~ 6 | typo: ~ 7 | replacement: ~ 8 | versionadded_directive_should_have_version: ~ 9 | deprecated_directive_should_have_version: ~ 10 | max_blank_lines: 11 | max: 2 12 | american_english: ~ 13 | valid_use_statements: ~ 14 | no_composer_req: ~ 15 | composer_dev_option_not_at_the_end: ~ 16 | no_php_open_tag_in_code_block_php_directive: ~ 17 | no_blank_line_after_filepath_in_php_code_block: ~ 18 | no_blank_line_after_filepath_in_yaml_code_block: ~ 19 | no_blank_line_after_filepath_in_xml_code_block: ~ 20 | no_blank_line_after_filepath_in_twig_code_block: ~ 21 | php_prefix_before_bin_console: ~ 22 | use_deprecated_directive_instead_of_versionadded: ~ 23 | space_before_self_xml_closing_tag: ~ 24 | ensure_order_of_code_blocks_in_configuration_block: ~ 25 | lowercase_as_in_use_statements: ~ 26 | ordered_use_statements: ~ 27 | no_namespace_after_use_statements: ~ 28 | use_https_xsd_urls: ~ 29 | blank_line_before_directive: ~ 30 | extension_xlf_instead_of_xliff: ~ 31 | valid_inline_highlighted_namespaces: ~ 32 | indention: ~ 33 | unused_links: ~ 34 | no_app_bundle: ~ 35 | 36 | versionadded_directive_major_version: 37 | major_version: 4 38 | 39 | versionadded_directive_min_version: 40 | min_version: '4.0' 41 | 42 | deprecated_directive_major_version: 43 | major_version: 4 44 | 45 | deprecated_directive_min_version: 46 | min_version: '4.0' 47 | -------------------------------------------------------------------------------- /docs/console.rst: -------------------------------------------------------------------------------- 1 | Console Commands 2 | ================ 3 | 4 | The Doctrine2 ODM integration offers various console commands under the 5 | ``doctrine:mongodb`` namespace. To view the command list you can run the console 6 | without any arguments: 7 | 8 | .. code-block:: bash 9 | 10 | php bin/console 11 | 12 | A list of available commands will be printed out, several of them start 13 | with the ``doctrine:mongodb`` prefix. You can find out more information about any 14 | of these commands (or any Symfony command) by running the ``help`` command. 15 | For example, to get details about the ``doctrine:mongodb:query`` task, run: 16 | 17 | .. code-block:: bash 18 | 19 | php bin/console help doctrine:mongodb:query 20 | 21 | .. note:: 22 | 23 | To be able to load data fixtures into MongoDB, you will need to have the 24 | ``DoctrineFixturesBundle`` bundle installed. To learn how to do it, read 25 | the "`DoctrineFixturesBundle`_" entry of the documentation. 26 | 27 | .. _`DoctrineFixturesBundle`: https://symfony.com/doc/master/bundles/DoctrineFixturesBundle/index.html 28 | -------------------------------------------------------------------------------- /docs/events.rst: -------------------------------------------------------------------------------- 1 | Registering Event Listeners and Subscribers 2 | =========================================== 3 | 4 | Doctrine allows you to register listeners and subscribers that are notified 5 | when different events occur inside Doctrine's ODM. For more information, 6 | see Doctrine's `Event Documentation`_. 7 | 8 | .. note:: 9 | 10 | Each connection in Doctrine has its own event manager, which is shared with 11 | document managers tied to that connection. Listeners and subscribers may be 12 | registered with all event managers or just one (using the connection name). 13 | 14 | Use the ``doctrine_mongodb.odm.event_listener`` tag to register a listener. The 15 | ``event`` attribute is required and should denote the event on which to listen. 16 | By default, listeners will be registered with event managers for all connections. 17 | To restrict a listener to a single connection, specify its name in the tag's 18 | ``connection`` attribute. 19 | 20 | 21 | The ``priority`` attribute, which defaults to ``0`` if omitted, may be used 22 | to control the order in which listeners are registered. Much like Symfony's 23 | `event dispatcher`_, greater number will result in the listener executing 24 | first and listeners with the same priority will be executed in the order that 25 | they were registered with the event manager. 26 | 27 | Lastly, the ``lazy`` attribute, which defaults to ``false`` if omitted, may 28 | be used to request that the listener be lazily loaded by the event manager 29 | when its event is dispatched. 30 | 31 | Starting with DoctrineMongoDBBundle bundle 4.7, you can use the ``#[AsDocumentListener]`` 32 | attribute to tag the service. 33 | 34 | .. configuration-block:: 35 | 36 | .. code-block:: php-attributes 37 | 38 | // src/App/EventListener/SearchIndexer.php 39 | namespace App\EventListener; 40 | 41 | use Doctrine\Bundle\MongoDBBundle\Attribute\AsDocumentListener; 42 | use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs; 43 | 44 | #[AsDocumentListener('postPersist', priority: 500, connection: 'default')] 45 | class SearchIndexer 46 | { 47 | public function postPersist(LifecycleEventArgs $event): void 48 | { 49 | // ... 50 | } 51 | } 52 | 53 | .. code-block:: yaml 54 | 55 | # config/services.yaml 56 | services: 57 | # ... 58 | 59 | App\EventListener\SearchIndexer: 60 | tags: 61 | - 62 | name: 'doctrine_mongodb.odm.event_listener' 63 | # this is the only required option for the lifecycle listener tag 64 | event: 'postPersist' 65 | 66 | # listeners can define their priority in case multiple subscribers or listeners are associated 67 | # to the same event (default priority = 0; higher numbers = listener is run earlier) 68 | priority: 500 69 | 70 | # you can also restrict listeners to a specific Doctrine connection 71 | connection: 'default' 72 | 73 | .. code-block:: xml 74 | 75 | 76 | 77 | 79 | 80 | 81 | 82 | 88 | 89 | 93 | 94 | 95 | 96 | 97 | .. note:: 98 | 99 | Unlike Symfony event listeners, Doctrine's event manager expects each 100 | listener and subscriber to have a method name corresponding to the observed 101 | event(s). For this reason, the aforementioned tags have no ``method`` 102 | attribute. 103 | 104 | .. _`event dispatcher`: https://symfony.com/doc/current/components/event_dispatcher.html 105 | .. _`Event Documentation`: https://www.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/events.html 106 | -------------------------------------------------------------------------------- /docs/form_validation.rst: -------------------------------------------------------------------------------- 1 | Form & Validation 2 | ================= 3 | 4 | DocumentType 5 | ------------ 6 | 7 | This bundle provides a ``DocumentType`` to be used with the Symfony's Form 8 | component. As ``DocumentType`` extends the `EntityType`_ provided by Symfony's 9 | Doctrine bridge you can use all options available for the ORM users. The only 10 | difference is that ``DocumentType`` expects the ``DocumentManager`` name or 11 | instance passed with the ``document_manager`` option instead of ``em``. 12 | 13 | Unique constraint 14 | ----------------- 15 | 16 | This bundle provides a ``Unique`` constraint, which extends the `UniqueEntity`_ 17 | constraint provided by Symfony's Doctrine bridge. This constraint allows you to 18 | validate the uniqueness of an document field against the database. 19 | 20 | The ``Unique`` constraint shares the same options as `UniqueEntity`_, which 21 | means that the ``em`` option should be used if you wish to specify the document 22 | manager explicitly instead of having it be inferred from the document class. 23 | 24 | .. _`EntityType`: https://symfony.com/doc/current/reference/forms/types/entity.html 25 | .. _`UniqueEntity`: https://symfony.com/doc/current/reference/constraints/UniqueEntity.html 26 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | DoctrineMongoDBBundle 2 | ===================== 3 | 4 | The `MongoDB`_ Object Document Mapper (ODM) is much like the Doctrine2 ORM 5 | in its philosophy and how it works. In other words, like the `Doctrine2 ORM`_, 6 | with the Doctrine ODM, you deal only with plain PHP objects, which are then 7 | persisted transparently to and from MongoDB. 8 | 9 | .. tip:: 10 | 11 | You can read more about the Doctrine MongoDB ODM via the project's `documentation`_. 12 | 13 | The bundle integrates the Doctrine MongoDB ODM into Symfony, 14 | helping you to configure and use it in your application. 15 | 16 | .. note:: 17 | 18 | This documentation will feel a lot like the `Doctrine2 ORM chapter`_, 19 | which talks about how the Doctrine ORM can be used to persist data to 20 | relational databases (e.g. MySQL). This is on purpose - whether you persist 21 | to a relational database via the ORM or to MongoDB via the ODM, the philosophies 22 | are very much the same. 23 | 24 | .. toctree:: 25 | 26 | installation 27 | config 28 | first_steps 29 | form_validation 30 | security_bundle 31 | messenger 32 | events 33 | console 34 | cookbook/registration_form 35 | 36 | Doctrine Extensions: Timestampable, Sluggable, etc. 37 | --------------------------------------------------- 38 | 39 | Doctrine is quite flexible, and a number of third-party extensions are available 40 | that allow you to perform repeated and common tasks on your entities. 41 | These include things such as *Sluggable*, *Timestampable*, *Loggable*, *Translatable*, 42 | and *Tree*. 43 | 44 | For more information about them, see `available Doctrine extensions`_ 45 | and use the `StofDoctrineExtensionsBundle`_ to integrate them in your application. 46 | 47 | .. _`MongoDB`: https://www.mongodb.com 48 | .. _`Doctrine2 ORM`: https://symfony.com/doc/current/book/doctrine.html 49 | .. _`documentation`: https://www.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/ 50 | .. _`Doctrine2 ORM chapter`: https://symfony.com/doc/current/book/doctrine.html 51 | .. _`available Doctrine extensions`: https://github.com/Atlantic18/DoctrineExtensions 52 | .. _`StofDoctrineExtensionsBundle`: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html 53 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | This chapter assumes you have Composer installed globally, as explained 5 | in the `installation chapter`_ of the Composer documentation. 6 | 7 | .. note:: 8 | 9 | The ODM requires the `MongoDB driver`_ (``mongodb``). 10 | 11 | Install the bundle with Symfony Flex 12 | ------------------------------------ 13 | 14 | A Flex recipe for the DoctrineMongoDBBundle is provided as a Contrib Recipe. 15 | You need to allow its usage first: 16 | 17 | .. code-block:: bash 18 | 19 | composer config extra.symfony.allow-contrib true 20 | 21 | .. code-block:: bash 22 | 23 | composer require doctrine/mongodb-odm-bundle 24 | 25 | Install the bundle with Composer 26 | -------------------------------- 27 | 28 | To install DoctrineMongoDBBundle with Composer run the following command: 29 | 30 | .. code-block:: bash 31 | 32 | composer require doctrine/mongodb-odm-bundle 33 | 34 | 35 | Enable the Bundle 36 | ----------------- 37 | 38 | The bundle should be automatically enabled if you use Flex. 39 | Otherwise, you'll need to manually enable the bundle by adding the 40 | following line in the ``config/bundles.php`` file of your project: 41 | 42 | .. code-block:: php 43 | 44 | // config/bundles.php 45 | return [ 46 | // ... 47 | Doctrine\Bundle\MongoDBBundle\DoctrineMongoDBBundle::class => ['all' => true], 48 | ]; 49 | 50 | Configuration 51 | ------------- 52 | 53 | Flex recipe will automatically create the ``config/packages/doctrine_mongodb.yaml`` 54 | file with default configuration. Without Flex you need to create the file 55 | manually and fill it with some basic configuration that sets up the document manager. 56 | The recommended way is to enable ``auto_mapping``, which will activate 57 | the MongoDB ODM across your application: 58 | 59 | .. code-block:: yaml 60 | 61 | # config/services.yaml 62 | parameters: 63 | mongodb_server: "mongodb://localhost:27017" 64 | 65 | .. code-block:: yaml 66 | 67 | # config/packages/doctrine_mongodb.yaml 68 | doctrine_mongodb: 69 | connections: 70 | default: 71 | server: "%mongodb_server%" 72 | options: {} 73 | default_database: test_database 74 | document_managers: 75 | default: 76 | auto_mapping: true 77 | 78 | .. note:: 79 | 80 | Please also make sure that the MongoDB server is running in the background. 81 | For more details, see the MongoDB `Installation Tutorials`_. 82 | 83 | .. tip:: 84 | 85 | You can configure bundle options that depend on where your application 86 | is run (e.g. during tests or development) with `Environment Variables`_. 87 | 88 | Authentication 89 | -------------- 90 | 91 | If you use authentication on your MongoDB database, then you can provide username, 92 | password, and authentication database in the following way: 93 | 94 | .. code-block:: yaml 95 | 96 | # config/services.yaml 97 | parameters: 98 | mongodb_server: "mongodb://username:password@localhost:27017/?authSource=auth-db" 99 | 100 | .. note:: 101 | 102 | The authentication database is different from the default database used by MongoDB. 103 | 104 | .. _`installation chapter`: https://getcomposer.org/doc/00-intro.md 105 | .. _`MongoDB driver`: https://docs.mongodb.com/ecosystem/drivers/php/ 106 | .. _`Installation Tutorials`: https://docs.mongodb.com/manual/installation/ 107 | .. _`Environment Variables`: https://symfony.com/doc/current/configuration.html#configuration-based-on-environment-variables 108 | -------------------------------------------------------------------------------- /docs/messenger.rst: -------------------------------------------------------------------------------- 1 | Messenger Integration 2 | ===================== 3 | 4 | When `symfony/messenger` package is installed, the bundle automatically 5 | registers a `Messenger`_ event subscriber that clears all document managers 6 | after handling messages, which helps to isolate each handler and guard 7 | against reading out-of-date document data. 8 | 9 | .. _`Messenger`: https://symfony.com/doc/current/components/messenger.html 10 | -------------------------------------------------------------------------------- /docs/security_bundle.rst: -------------------------------------------------------------------------------- 1 | SecurityBundle integration 2 | ========================== 3 | 4 | A user provider is available for your MongoDB projects if you use 5 | Symfony `SecurityBundle`_. It works exactly the same way as 6 | the ``entity`` user provider described in `the documentation`_: 7 | 8 | .. configuration-block:: 9 | 10 | .. code-block:: yaml 11 | 12 | # config/packages/security.yaml 13 | security: 14 | providers: 15 | my_mongo_provider: 16 | mongodb: {class: App\Document\User, property: username} 17 | 18 | .. code-block:: xml 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | .. _`SecurityBundle`: https://symfony.com/doc/current/security.html 28 | .. _`the documentation`: https://symfony.com/doc/current/security/user_provider.html 29 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | includes: 2 | - phpstan-baseline.neon 3 | 4 | parameters: 5 | phpVersion: 80400 6 | level: 7 7 | paths: 8 | - config 9 | - src 10 | - tests 11 | -------------------------------------------------------------------------------- /src/APM/CommandLoggerRegistry.php: -------------------------------------------------------------------------------- 1 | addLogger($commandLogger); 20 | } 21 | } 22 | 23 | public function register(): void 24 | { 25 | array_map(static function (CommandLoggerInterface $commandLogger): void { 26 | $commandLogger->register(); 27 | }, $this->commandLoggers); 28 | } 29 | 30 | public function unregister(): void 31 | { 32 | array_map(static function (CommandLoggerInterface $commandLogger): void { 33 | $commandLogger->unregister(); 34 | }, $this->commandLoggers); 35 | } 36 | 37 | private function addLogger(CommandLoggerInterface $logger): void 38 | { 39 | $this->commandLoggers[] = $logger; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/APM/PSRCommandLogger.php: -------------------------------------------------------------------------------- 1 | logger === null || $this->registered) { 28 | return; 29 | } 30 | 31 | $this->registered = true; 32 | addSubscriber($this); 33 | } 34 | 35 | public function unregister(): void 36 | { 37 | if (! $this->registered) { 38 | return; 39 | } 40 | 41 | removeSubscriber($this); 42 | $this->registered = false; 43 | } 44 | 45 | public function commandStarted(CommandStartedEvent $event): void 46 | { 47 | if (! $this->logger) { 48 | return; 49 | } 50 | 51 | $this->logger->debug($this->prefix . json_encode($event->getCommand())); 52 | } 53 | 54 | public function commandSucceeded(CommandSucceededEvent $event): void 55 | { 56 | } 57 | 58 | public function commandFailed(CommandFailedEvent $event): void 59 | { 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/APM/StopwatchCommandLogger.php: -------------------------------------------------------------------------------- 1 | stopwatch === null || $this->registered) { 28 | return; 29 | } 30 | 31 | $this->registered = true; 32 | addSubscriber($this); 33 | } 34 | 35 | public function unregister(): void 36 | { 37 | if (! $this->registered) { 38 | return; 39 | } 40 | 41 | removeSubscriber($this); 42 | $this->registered = false; 43 | } 44 | 45 | public function commandStarted(CommandStartedEvent $event): void 46 | { 47 | if (! $this->stopwatch) { 48 | return; 49 | } 50 | 51 | $this->stopwatch->start(sprintf('mongodb_%s', $event->getRequestId()), 'doctrine_mongodb'); 52 | } 53 | 54 | public function commandSucceeded(CommandSucceededEvent $event): void 55 | { 56 | if (! $this->stopwatch) { 57 | return; 58 | } 59 | 60 | $this->stopwatch->stop(sprintf('mongodb_%s', $event->getRequestId())); 61 | } 62 | 63 | public function commandFailed(CommandFailedEvent $event): void 64 | { 65 | if (! $this->stopwatch) { 66 | return; 67 | } 68 | 69 | $this->stopwatch->stop(sprintf('mongodb_%s', $event->getRequestId())); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/ArgumentResolver/DocumentValueResolver.php: -------------------------------------------------------------------------------- 1 | entityValueResolver->resolve($request, $argument); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Attribute/AsDocumentListener.php: -------------------------------------------------------------------------------- 1 | message)) { 31 | parent::__construct($class, $objectManager, $expr, $mapping, $exclude, $stripNull, $id, null, $disabled, $resolver); 32 | 33 | return; 34 | } 35 | 36 | if (! property_exists(MapEntity::class, 'message')) { 37 | throw new RuntimeException( 38 | 'The "message" options is not supported at "MapDocument". Please upgrade "symfony/doctrine-bridge" to "^7.1".', 39 | ); 40 | } 41 | 42 | parent::__construct($class, $objectManager, $expr, $mapping, $exclude, $stripNull, $id, null, $disabled, $resolver, $message); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/CacheWarmer/HydratorCacheWarmer.php: -------------------------------------------------------------------------------- 1 | container->getParameter('doctrine_mongodb.odm.hydrator_dir'); 51 | if (! file_exists($hydratorCacheDir)) { 52 | if (@mkdir($hydratorCacheDir, 0775, true) === false && ! is_dir($hydratorCacheDir)) { 53 | throw new RuntimeException(sprintf('Unable to create the Doctrine Hydrator directory (%s)', dirname($hydratorCacheDir))); 54 | } 55 | } elseif (! is_writable($hydratorCacheDir)) { 56 | throw new RuntimeException(sprintf('Doctrine Hydrator directory (%s) is not writable for the current system user.', $hydratorCacheDir)); 57 | } 58 | 59 | if ($this->container->getParameter('doctrine_mongodb.odm.auto_generate_hydrator_classes') !== Configuration::AUTOGENERATE_NEVER) { 60 | return []; 61 | } 62 | 63 | $registry = $this->container->get('doctrine_mongodb'); 64 | assert($registry instanceof ManagerRegistry); 65 | foreach ($registry->getManagers() as $dm) { 66 | /** @var DocumentManager $dm */ 67 | $classes = $dm->getMetadataFactory()->getAllMetadata(); 68 | $dm->getHydratorFactory()->generateHydratorClasses($classes); 69 | } 70 | 71 | return []; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/CacheWarmer/PersistentCollectionCacheWarmer.php: -------------------------------------------------------------------------------- 1 | container->getParameter('doctrine_mongodb.odm.persistent_collection_dir'); 52 | if (! file_exists($collCacheDir)) { 53 | if (@mkdir($collCacheDir, 0775, true) === false && ! is_dir($collCacheDir)) { 54 | throw new RuntimeException(sprintf('Unable to create the Doctrine persistent collection directory (%s)', dirname($collCacheDir))); 55 | } 56 | } elseif (! is_writable($collCacheDir)) { 57 | throw new RuntimeException(sprintf('Doctrine persistent collection directory (%s) is not writable for the current system user.', $collCacheDir)); 58 | } 59 | 60 | // if persistent collection are autogenerated we don't need to generate them in the cache warmer. 61 | if ($this->container->getParameter('doctrine_mongodb.odm.auto_generate_persistent_collection_classes') !== Configuration::AUTOGENERATE_NEVER) { 62 | return []; 63 | } 64 | 65 | $generated = []; 66 | $registry = $this->container->get('doctrine_mongodb'); 67 | assert($registry instanceof ManagerRegistry); 68 | foreach ($registry->getManagers() as $dm) { 69 | /** @var DocumentManager $dm */ 70 | $collectionGenerator = $dm->getConfiguration()->getPersistentCollectionGenerator(); 71 | $classes = $dm->getMetadataFactory()->getAllMetadata(); 72 | foreach ($classes as $metadata) { 73 | foreach ($metadata->getAssociationNames() as $fieldName) { 74 | $mapping = $metadata->getFieldMapping($fieldName); 75 | if (empty($mapping['collectionClass']) || in_array($mapping['collectionClass'], $generated)) { 76 | continue; 77 | } 78 | 79 | $generated[] = $mapping['collectionClass']; 80 | $collectionGenerator->generateClass($mapping['collectionClass'], $collCacheDir); 81 | } 82 | } 83 | } 84 | 85 | return []; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/CacheWarmer/ProxyCacheWarmer.php: -------------------------------------------------------------------------------- 1 | container->getParameter('doctrine_mongodb.odm.proxy_dir'); 53 | if (! file_exists($proxyCacheDir)) { 54 | if (@mkdir($proxyCacheDir, 0775, true) === false && ! is_dir($proxyCacheDir)) { 55 | throw new RuntimeException(sprintf('Unable to create the Doctrine Proxy directory (%s)', dirname($proxyCacheDir))); 56 | } 57 | } elseif (! is_writable($proxyCacheDir)) { 58 | throw new RuntimeException(sprintf('Doctrine Proxy directory (%s) is not writable for the current system user.', $proxyCacheDir)); 59 | } 60 | 61 | if ($this->container->getParameter('doctrine_mongodb.odm.auto_generate_proxy_classes') === Configuration::AUTOGENERATE_EVAL) { 62 | return []; 63 | } 64 | 65 | $registry = $this->container->get('doctrine_mongodb'); 66 | assert($registry instanceof ManagerRegistry); 67 | foreach ($registry->getManagers() as $dm) { 68 | /** @var DocumentManager $dm */ 69 | $classes = $this->getClassesForProxyGeneration($dm); 70 | $dm->getProxyFactory()->generateProxyClasses($classes); 71 | } 72 | 73 | return []; 74 | } 75 | 76 | /** @return ClassMetadata[] */ 77 | private function getClassesForProxyGeneration(DocumentManager $dm): array 78 | { 79 | return array_filter($dm->getMetadataFactory()->getAllMetadata(), static fn (ClassMetadata $metadata) => ! $metadata->isEmbeddedDocument && ! $metadata->isMappedSuperclass); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Command/ClearMetadataCacheDoctrineODMCommand.php: -------------------------------------------------------------------------------- 1 | setName('doctrine:mongodb:cache:clear-metadata') 25 | ->setDescription('Clear all metadata cache for a document manager.') 26 | ->addOption('dm', null, InputOption::VALUE_OPTIONAL, 'The document manager to use for this command.') 27 | ->setHelp(<<<'EOT' 28 | The doctrine:mongodb:cache:clear-metadata command clears all metadata cache for the default document manager: 29 | 30 | ./app/console doctrine:mongodb:cache:clear-metadata 31 | 32 | You can also optionally specify the --dm option to specify which document manager to clear the cache for: 33 | 34 | ./app/console doctrine:mongodb:cache:clear-metadata --dm=default 35 | EOT 36 | ); 37 | } 38 | 39 | protected function execute(InputInterface $input, OutputInterface $output): int 40 | { 41 | DoctrineODMCommand::setApplicationDocumentManager($this->getApplication(), $input->getOption('dm')); 42 | 43 | return parent::execute($input, $output); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Command/CreateSchemaDoctrineODMCommand.php: -------------------------------------------------------------------------------- 1 | setName('doctrine:mongodb:schema:create') 26 | ->addOption('dm', null, InputOption::VALUE_REQUIRED, 'The document manager to use for this command.') 27 | ->setHelp(<<<'EOT' 28 | The doctrine:mongodb:schema:create command creates the default document manager's schema: 29 | 30 | ./app/console doctrine:mongodb:schema:create 31 | 32 | You can also optionally specify the name of a document manager to create the schema for: 33 | 34 | ./app/console doctrine:mongodb:schema:create --dm=default 35 | EOT 36 | ); 37 | } 38 | 39 | protected function execute(InputInterface $input, OutputInterface $output): int 40 | { 41 | DoctrineODMCommand::setApplicationDocumentManager($this->getApplication(), $input->getOption('dm')); 42 | 43 | return parent::execute($input, $output); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Command/DoctrineODMCommand.php: -------------------------------------------------------------------------------- 1 | getKernel()->getContainer()->get('doctrine_mongodb')->getManager($dmName); 27 | $helperSet = $application->getHelperSet(); 28 | $helperSet->set(new DocumentManagerHelper($dm), 'dm'); 29 | } 30 | 31 | protected function getManagerRegistry(): ManagerRegistry 32 | { 33 | return $this->registry; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Command/DropSchemaDoctrineODMCommand.php: -------------------------------------------------------------------------------- 1 | setName('doctrine:mongodb:schema:drop') 26 | ->addOption('dm', null, InputOption::VALUE_REQUIRED, 'The document manager to use for this command.') 27 | ->setHelp(<<<'EOT' 28 | The doctrine:mongodb:schema:drop command drops the default document manager's schema: 29 | 30 | ./app/console doctrine:mongodb:schema:drop 31 | 32 | You can also optionally specify the name of a document manager to drop the schema for: 33 | 34 | ./app/console doctrine:mongodb:schema:drop --dm=default 35 | EOT 36 | ); 37 | } 38 | 39 | protected function execute(InputInterface $input, OutputInterface $output): int 40 | { 41 | DoctrineODMCommand::setApplicationDocumentManager($this->getApplication(), $input->getOption('dm')); 42 | 43 | return parent::execute($input, $output); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Command/GenerateHydratorsDoctrineODMCommand.php: -------------------------------------------------------------------------------- 1 | setName('doctrine:mongodb:generate:hydrators') 25 | ->addOption('dm', null, InputOption::VALUE_OPTIONAL, 'The document manager to use for this command.') 26 | ->setHelp(<<<'EOT' 27 | The doctrine:mongodb:generate:hydrators command generates hydrator classes for your documents: 28 | 29 | ./app/console doctrine:mongodb:generate:hydrators 30 | 31 | You can specify the document manager you want to generate the hydrators for: 32 | 33 | ./app/console doctrine:mongodb:generate:hydrators --dm=name 34 | EOT 35 | ); 36 | } 37 | 38 | protected function execute(InputInterface $input, OutputInterface $output): int 39 | { 40 | DoctrineODMCommand::setApplicationDocumentManager($this->getApplication(), $input->getOption('dm')); 41 | 42 | return parent::execute($input, $output); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Command/GenerateProxiesDoctrineODMCommand.php: -------------------------------------------------------------------------------- 1 | setName('doctrine:mongodb:generate:proxies') 25 | ->addOption('dm', null, InputOption::VALUE_OPTIONAL, 'The document manager to use for this command.') 26 | ->setHelp(<<<'EOT' 27 | The doctrine:mongodb:generate:proxies command generates proxy classes for your default document manager: 28 | 29 | ./app/console doctrine:mongodb:generate:proxies 30 | 31 | You can specify the document manager you want to generate the proxies for: 32 | 33 | ./app/console doctrine:mongodb:generate:proxies --dm=name 34 | EOT 35 | ); 36 | } 37 | 38 | protected function execute(InputInterface $input, OutputInterface $output): int 39 | { 40 | DoctrineODMCommand::setApplicationDocumentManager($this->getApplication(), $input->getOption('dm')); 41 | 42 | return parent::execute($input, $output); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Command/InfoDoctrineODMCommand.php: -------------------------------------------------------------------------------- 1 | setName('doctrine:mongodb:mapping:info') 29 | ->addOption('dm', null, InputOption::VALUE_OPTIONAL, 'The document manager to use for this command.') 30 | ->setDescription('Show basic information about all mapped documents.') 31 | ->setHelp(<<<'EOT' 32 | The doctrine:mongodb:mapping:info shows basic information about which 33 | documents exist and possibly if their mapping information contains errors or not. 34 | 35 | ./bin/console doctrine:mongodb:mapping:info 36 | 37 | If you are using multiple document managers you can pick your choice with the --dm option: 38 | 39 | ./bin/console doctrine:mongodb:mapping:info --dm=default 40 | EOT 41 | ); 42 | } 43 | 44 | protected function execute(InputInterface $input, OutputInterface $output): int 45 | { 46 | $documentManagerName = $input->hasOption('dm') ? $input->getOption('dm') : $this->getManagerRegistry()->getDefaultManagerName(); 47 | 48 | $documentManager = $this->getManagerRegistry()->getManager($documentManagerName); 49 | assert($documentManager instanceof DocumentManager); 50 | 51 | $documentClassNames = $documentManager->getConfiguration() 52 | ->getMetadataDriverImpl() 53 | ->getAllClassNames(); 54 | 55 | if (! $documentClassNames) { 56 | throw new Exception( 57 | 'You do not have any mapped Doctrine MongoDB ODM documents for any of your bundles. ' . 58 | 'Create a class inside the Document namespace of any of your bundles and provide ' . 59 | 'mapping information for it with Attributes directly in the classes doc blocks ' . 60 | 'or with XML in your bundles Resources/config/doctrine/metadata/mongodb directory.' 61 | ); 62 | } 63 | 64 | $output->write(sprintf( 65 | "Found %d documents mapped in document manager %s:\n", 66 | count($documentClassNames), 67 | $documentManagerName 68 | ), true); 69 | 70 | foreach ($documentClassNames as $documentClassName) { 71 | try { 72 | $cm = $documentManager->getClassMetadata($documentClassName); 73 | $output->write('[OK] ' . $documentClassName, true); 74 | } catch (Throwable $e) { 75 | $output->write('[FAIL] ' . $documentClassName, true); 76 | $output->write('' . $e->getMessage() . '', true); 77 | $output->write('', true); 78 | } 79 | } 80 | 81 | return 0; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Command/LoadDataFixturesDoctrineODMCommand.php: -------------------------------------------------------------------------------- 1 | setName('doctrine:mongodb:fixtures:load') 37 | ->setDescription('Load data fixtures to your database.') 38 | ->addOption('group', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Only load fixtures that belong to this group (use with --services)') 39 | ->addOption('append', null, InputOption::VALUE_NONE, 'Append the data fixtures instead of flushing the database first.') 40 | ->addOption('dm', null, InputOption::VALUE_REQUIRED, 'The document manager to use for this command.') 41 | ->setHelp(<<<'EOT' 42 | The doctrine:mongodb:fixtures:load command loads data fixtures from your application: 43 | 44 | php %command.full_name% 45 | 46 | If you want to append the fixtures instead of flushing the database first you can use the --append option: 47 | 48 | php %command.full_name% --append 49 | 50 | You can also choose to load only fixtures that live in a certain group: 51 | 52 | php %command.full_name% --group=group1 53 | EOT 54 | ); 55 | } 56 | 57 | protected function execute(InputInterface $input, OutputInterface $output): int 58 | { 59 | $dm = $this->getManagerRegistry()->getManager($input->getOption('dm')); 60 | $ui = new SymfonyStyle($input, $output); 61 | 62 | if ($input->isInteractive() && ! $input->getOption('append')) { 63 | $helper = $this->getHelper('question'); 64 | $question = new ConfirmationQuestion('Careful, database will be purged. Do you want to continue (y/N) ?', false); 65 | 66 | if (! $helper->ask($input, $output, $question)) { 67 | return 0; 68 | } 69 | } 70 | 71 | $groups = $input->getOption('group'); 72 | $fixtures = $this->fixturesLoader->getFixtures($groups); 73 | if (! $fixtures) { 74 | $message = 'Could not find any fixture services to load'; 75 | 76 | if (! empty($groups)) { 77 | $message .= sprintf(' in the groups (%s)', implode(', ', $groups)); 78 | } 79 | 80 | $ui->error($message . '.'); 81 | 82 | return 1; 83 | } 84 | 85 | $purger = new MongoDBPurger($dm); 86 | $executor = new MongoDBExecutor($dm, $purger); 87 | 88 | $executor->setLogger(new class ($output) extends AbstractLogger { 89 | public function __construct(private OutputInterface $output) 90 | { 91 | } 92 | 93 | /** {@inheritDoc} */ 94 | public function log($level, $message, array $context = []): void 95 | { 96 | $this->output->writeln(sprintf(' > %s', $message)); 97 | } 98 | }); 99 | $executor->execute($fixtures, $input->getOption('append')); 100 | 101 | return 0; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Command/QueryDoctrineODMCommand.php: -------------------------------------------------------------------------------- 1 | setName('doctrine:mongodb:query') 25 | ->addOption('dm', null, InputOption::VALUE_OPTIONAL, 'The document manager to use for this command.'); 26 | } 27 | 28 | protected function execute(InputInterface $input, OutputInterface $output): int 29 | { 30 | DoctrineODMCommand::setApplicationDocumentManager($this->getApplication(), $input->getOption('dm')); 31 | 32 | return parent::execute($input, $output); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Command/ShardDoctrineODMCommand.php: -------------------------------------------------------------------------------- 1 | setName('doctrine:mongodb:schema:shard') 26 | ->addOption('dm', null, InputOption::VALUE_REQUIRED, 'The document manager to use for this command.') 27 | ->setHelp(<<<'EOT' 28 | The doctrine:mongodb:schema:shard command shards collections based on their metadata: 29 | 30 | ./app/console doctrine:mongodb:schema:shard 31 | 32 | You can also optionally specify the name of a document manager to shard collections for: 33 | 34 | ./app/console doctrine:mongodb:schema:shard --dm=default 35 | EOT 36 | ); 37 | } 38 | 39 | protected function execute(InputInterface $input, OutputInterface $output): int 40 | { 41 | DoctrineODMCommand::setApplicationDocumentManager($this->getApplication(), $input->getOption('dm')); 42 | 43 | return parent::execute($input, $output); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Command/UpdateSchemaDoctrineODMCommand.php: -------------------------------------------------------------------------------- 1 | setName('doctrine:mongodb:schema:update') 26 | ->addOption('dm', null, InputOption::VALUE_REQUIRED, 'The document manager to use for this command.') 27 | ->setHelp(<<<'EOT' 28 | The doctrine:mongodb:schema:update command updates the default document manager's schema: 29 | 30 | ./app/console doctrine:mongodb:schema:update 31 | 32 | You can also optionally specify the name of a document manager to update the schema for: 33 | 34 | ./app/console doctrine:mongodb:schema:update --dm=default 35 | EOT 36 | ); 37 | } 38 | 39 | protected function execute(InputInterface $input, OutputInterface $output): int 40 | { 41 | DoctrineODMCommand::setApplicationDocumentManager($this->getApplication(), $input->getOption('dm')); 42 | 43 | return parent::execute($input, $output); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/DataCollector/CommandDataCollector.php: -------------------------------------------------------------------------------- 1 | data = [ 31 | 'num_commands' => count($this->commandLogger), 32 | 'commands' => array_map( 33 | static function (Command $command): array { 34 | $dbProperty = '$db'; 35 | $document = Document::fromPHP($command->getCommand()); 36 | 37 | return [ 38 | 'database' => $command->getCommand()->$dbProperty ?? '', 39 | 'command' => json_decode($document->toCanonicalExtendedJSON()), 40 | 'durationMicros' => $command->getDurationMicros(), 41 | ]; 42 | }, 43 | $this->commandLogger->getAll(), 44 | ), 45 | 'time' => array_reduce( 46 | $this->commandLogger->getAll(), 47 | static fn (int $total, Command $command): int => $total + $command->getDurationMicros(), 48 | 0, 49 | ), 50 | ]; 51 | } 52 | 53 | public function reset(): void 54 | { 55 | $this->commandLogger->clear(); 56 | $this->data = [ 57 | 'num_commands' => 0, 58 | 'commands' => [], 59 | ]; 60 | } 61 | 62 | public function getCommandCount(): int 63 | { 64 | return $this->data['num_commands']; 65 | } 66 | 67 | public function getTime(): int 68 | { 69 | return $this->data['time']; 70 | } 71 | 72 | /** @return array */ 73 | public function getCommands(): array 74 | { 75 | return $this->data['commands']; 76 | } 77 | 78 | public function getName(): string 79 | { 80 | return 'mongodb'; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/CreateHydratorDirectoryPass.php: -------------------------------------------------------------------------------- 1 | hasParameter('doctrine_mongodb.odm.hydrator_dir')) { 23 | return; 24 | } 25 | 26 | // Don't do anything if auto_generate_hydrator_classes is false 27 | if (! $container->getParameter('doctrine_mongodb.odm.auto_generate_hydrator_classes')) { 28 | return; 29 | } 30 | 31 | // Create document proxy directory 32 | $hydratorCacheDir = (string) $container->getParameter('doctrine_mongodb.odm.hydrator_dir'); 33 | if (! is_dir($hydratorCacheDir)) { 34 | if (@mkdir($hydratorCacheDir, 0775, true) === false && ! is_dir($hydratorCacheDir)) { 35 | throw new RuntimeException( 36 | sprintf('Unable to create the Doctrine Hydrator directory (%s)', dirname($hydratorCacheDir)), 37 | ); 38 | } 39 | } elseif (! is_writable($hydratorCacheDir)) { 40 | throw new RuntimeException( 41 | sprintf('Unable to write in the Doctrine Hydrator directory (%s)', $hydratorCacheDir), 42 | ); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/CreateProxyDirectoryPass.php: -------------------------------------------------------------------------------- 1 | hasParameter('doctrine_mongodb.odm.proxy_dir')) { 23 | return; 24 | } 25 | 26 | // Don't do anything if auto_generate_proxy_classes is false 27 | if (! $container->getParameter('doctrine_mongodb.odm.auto_generate_proxy_classes')) { 28 | return; 29 | } 30 | 31 | // Create document proxy directory 32 | $proxyCacheDir = (string) $container->getParameter('doctrine_mongodb.odm.proxy_dir'); 33 | if (! is_dir($proxyCacheDir)) { 34 | if (@mkdir($proxyCacheDir, 0775, true) === false && ! is_dir($proxyCacheDir)) { 35 | throw new RuntimeException( 36 | sprintf('Unable to create the Doctrine Proxy directory (%s)', dirname($proxyCacheDir)), 37 | ); 38 | } 39 | } elseif (! is_writable($proxyCacheDir)) { 40 | throw new RuntimeException( 41 | sprintf('Unable to write in the Doctrine Proxy directory (%s)', $proxyCacheDir), 42 | ); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/FixturesCompilerPass.php: -------------------------------------------------------------------------------- 1 | hasDefinition('doctrine_mongodb.odm.symfony.fixtures.loader')) { 19 | return; 20 | } 21 | 22 | $definition = $container->getDefinition('doctrine_mongodb.odm.symfony.fixtures.loader'); 23 | $taggedServices = $container->findTaggedServiceIds(self::FIXTURE_TAG); 24 | 25 | $fixtures = []; 26 | foreach ($taggedServices as $serviceId => $tags) { 27 | $groups = []; 28 | foreach ($tags as $tagData) { 29 | if (isset($tagData['group'])) { 30 | $groups[] = $tagData['group']; 31 | } 32 | } 33 | 34 | $fixtures[] = [ 35 | 'fixture' => new Reference($serviceId), 36 | 'groups' => $groups, 37 | ]; 38 | } 39 | 40 | $definition->addMethodCall('addFixtures', [$fixtures]); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/ServiceRepositoryCompilerPass.php: -------------------------------------------------------------------------------- 1 | hasDefinition('doctrine_mongodb.odm.container_repository_factory')) { 25 | return; 26 | } 27 | 28 | $locatorDef = $container->getDefinition('doctrine_mongodb.odm.container_repository_factory'); 29 | 30 | $repoServiceIds = array_keys($container->findTaggedServiceIds(self::REPOSITORY_SERVICE_TAG)); 31 | 32 | $repoReferences = array_map(static fn ($id) => new Reference($id), $repoServiceIds); 33 | 34 | $locatorDef->replaceArgument(0, ServiceLocatorTagPass::register($container, array_combine($repoServiceIds, $repoReferences))); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/DoctrineMongoDBBundle.php: -------------------------------------------------------------------------------- 1 | addCompilerPass(new RegisterEventListenersAndSubscribersPass('doctrine_mongodb.odm.connections', 'doctrine_mongodb.odm.%s_connection.event_manager', 'doctrine_mongodb.odm'), PassConfig::TYPE_BEFORE_OPTIMIZATION); 39 | $container->addCompilerPass(new CreateProxyDirectoryPass(), PassConfig::TYPE_BEFORE_REMOVING); 40 | $container->addCompilerPass(new CreateHydratorDirectoryPass(), PassConfig::TYPE_BEFORE_REMOVING); 41 | $container->addCompilerPass(new DoctrineValidationPass('mongodb')); 42 | $container->addCompilerPass(new ServiceRepositoryCompilerPass()); 43 | $container->addCompilerPass(new FixturesCompilerPass()); 44 | 45 | if (! $container->hasExtension('security')) { 46 | return; 47 | } 48 | 49 | $security = $container->getExtension('security'); 50 | 51 | if (! ($security instanceof SecurityExtension)) { 52 | return; 53 | } 54 | 55 | $security->addUserProviderFactory(new EntityFactory('mongodb', 'doctrine_mongodb.odm.security.user.provider')); 56 | } 57 | 58 | public function getContainerExtension(): ?ExtensionInterface 59 | { 60 | return new DoctrineMongoDBExtension(); 61 | } 62 | 63 | public function getPath(): string 64 | { 65 | return dirname(__DIR__); 66 | } 67 | 68 | public function boot(): void 69 | { 70 | $registry = $this->container->get('doctrine_mongodb'); 71 | assert($registry instanceof ManagerRegistry); 72 | 73 | $this->registerAutoloader($registry->getManager()); 74 | $this->registerCommandLoggers(); 75 | } 76 | 77 | private function registerAutoloader(DocumentManager $documentManager): void 78 | { 79 | $configuration = $documentManager->getConfiguration(); 80 | if ($configuration->getAutoGenerateProxyClasses() !== Configuration::AUTOGENERATE_FILE_NOT_EXISTS) { 81 | return; 82 | } 83 | 84 | $this->autoloader = $configuration->getProxyManagerConfiguration()->getProxyAutoloader(); 85 | 86 | spl_autoload_register($this->autoloader); 87 | } 88 | 89 | private function unregisterAutoloader(): void 90 | { 91 | if ($this->autoloader === null) { 92 | return; 93 | } 94 | 95 | spl_autoload_unregister($this->autoloader); 96 | $this->autoloader = null; 97 | } 98 | 99 | private function registerCommandLoggers(): void 100 | { 101 | $commandLoggerRegistry = $this->container->get('doctrine_mongodb.odm.command_logger_registry'); 102 | $commandLoggerRegistry->register(); 103 | } 104 | 105 | private function unregisterCommandLoggers(): void 106 | { 107 | $commandLoggerRegistry = $this->container->get('doctrine_mongodb.odm.command_logger_registry'); 108 | $commandLoggerRegistry->unregister(); 109 | } 110 | 111 | public function shutdown(): void 112 | { 113 | $this->unregisterAutoloader(); 114 | $this->unregisterCommandLoggers(); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Fixture/Fixture.php: -------------------------------------------------------------------------------- 1 | getRepository($class)); 35 | 36 | if (! $queryBuilder instanceof Builder) { 37 | throw new UnexpectedTypeException($queryBuilder, Builder::class); 38 | } 39 | } 40 | 41 | $this->queryBuilder = $queryBuilder; 42 | } 43 | 44 | /** @return object[] */ 45 | public function getEntities(): array 46 | { 47 | return array_values($this->queryBuilder->getQuery()->execute()->toArray()); 48 | } 49 | 50 | /** @return object[] */ 51 | public function getEntitiesByIds(string $identifier, array $values): array 52 | { 53 | $qb = clone $this->queryBuilder; 54 | 55 | return array_values($qb 56 | ->field($identifier)->in($values) 57 | ->getQuery() 58 | ->execute() 59 | ->toArray()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Form/DoctrineMongoDBExtension.php: -------------------------------------------------------------------------------- 1 | registry), 26 | ]; 27 | } 28 | 29 | protected function loadTypeGuesser(): ?FormTypeGuesserInterface 30 | { 31 | return new DoctrineMongoDBTypeGuesser($this->registry); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Form/Type/DocumentType.php: -------------------------------------------------------------------------------- 1 | setDefaults(['document_manager' => null]); 35 | 36 | $registry = $this->registry; 37 | $normalizer = static function (Options $options, $manager) use ($registry) { 38 | if (isset($options['document_manager']) && $manager) { 39 | throw new InvalidArgumentException('You cannot set both an "em" and "document_manager" option.'); 40 | } 41 | 42 | $manager = $options['document_manager'] ?: $manager; 43 | 44 | if ($manager === null) { 45 | return $registry->getManagerForClass($options['class']); 46 | } 47 | 48 | if ($manager instanceof ObjectManager) { 49 | return $manager; 50 | } 51 | 52 | return $registry->getManager($manager); 53 | }; 54 | 55 | $resolver->setNormalizer('em', $normalizer); 56 | 57 | $resolver->setAllowedTypes('document_manager', ['null', 'string', DocumentManager::class]); 58 | } 59 | 60 | public function getBlockPrefix(): string 61 | { 62 | return 'document'; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Loader/SymfonyFixturesLoaderInterface.php: -------------------------------------------------------------------------------- 1 | $fixtures 17 | */ 18 | public function addFixtures(array $fixtures): void; 19 | 20 | /** 21 | * Add a single fixture 22 | */ 23 | public function addFixture(FixtureInterface $fixture): void; 24 | 25 | /** 26 | * Returns the array of data fixtures to execute. 27 | * 28 | * @param string[] $groups 29 | * 30 | * @return FixtureInterface[] 31 | */ 32 | public function getFixtures(array $groups = []): array; 33 | } 34 | -------------------------------------------------------------------------------- /src/ManagerConfigurator.php: -------------------------------------------------------------------------------- 1 | enableFilters($documentManager); 29 | } 30 | 31 | /** 32 | * Enable filters for an given document manager 33 | */ 34 | private function enableFilters(DocumentManager $documentManager): void 35 | { 36 | if (empty($this->enabledFilters)) { 37 | return; 38 | } 39 | 40 | $filterCollection = $documentManager->getFilterCollection(); 41 | foreach ($this->enabledFilters as $filter) { 42 | $filterCollection->enable($filter); 43 | } 44 | } 45 | 46 | /** 47 | * Loads custom types. 48 | * 49 | * @throws MappingException 50 | */ 51 | public static function loadTypes(array $types): void 52 | { 53 | foreach ($types as $typeName => $typeConfig) { 54 | if (Type::hasType($typeName)) { 55 | Type::overrideType($typeName, $typeConfig['class']); 56 | } else { 57 | Type::addType($typeName, $typeConfig['class']); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/ManagerRegistry.php: -------------------------------------------------------------------------------- 1 | container = $container; 23 | 24 | parent::__construct($name, $connections, $managers, $defaultConnection, $defaultManager, $proxyInterfaceName); 25 | } 26 | 27 | /** 28 | * Resolves a registered namespace alias to the full namespace. 29 | * 30 | * @throws MongoDBException 31 | */ 32 | public function getAliasNamespace(string $alias): string 33 | { 34 | foreach (array_keys($this->getManagers()) as $name) { 35 | $objectManager = $this->getManager($name); 36 | 37 | if (! $objectManager instanceof DocumentManager) { 38 | continue; 39 | } 40 | 41 | try { 42 | return $objectManager->getConfiguration()->getDocumentNamespace($alias); 43 | } catch (MongoDBException) { 44 | } 45 | } 46 | 47 | throw MongoDBException::unknownDocumentNamespace($alias); 48 | } 49 | 50 | /** 51 | * Clears all document managers. 52 | */ 53 | public function reset(): void 54 | { 55 | foreach ($this->getManagerNames() as $managerName => $serviceId) { 56 | $this->resetOrClearManager($managerName, $serviceId); 57 | } 58 | } 59 | 60 | private function resetOrClearManager(string $managerName, string $serviceId): void 61 | { 62 | if (! $this->container->initialized($serviceId)) { 63 | return; 64 | } 65 | 66 | $manager = $this->container->get($serviceId); 67 | 68 | if ($manager instanceof LazyLoadingInterface || $manager instanceof LazyObjectInterface) { 69 | $this->resetManager($managerName); 70 | 71 | return; 72 | } 73 | 74 | assert($manager instanceof DocumentManager); 75 | 76 | if (! $manager->isOpen()) { 77 | return; 78 | } 79 | 80 | $manager->clear(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Mapping/Driver/XmlDriver.php: -------------------------------------------------------------------------------- 1 | */ 28 | private array $managedRepositories = []; 29 | 30 | /** @param ContainerInterface $container A service locator containing the repositories */ 31 | public function __construct(private ContainerInterface $container) 32 | { 33 | } 34 | 35 | /** 36 | * @phpstan-param class-string $documentName 37 | * 38 | * @phpstan-return ObjectRepository 39 | * 40 | * @template T of object 41 | */ 42 | public function getRepository(DocumentManager $documentManager, string $documentName): ObjectRepository 43 | { 44 | $metadata = $documentManager->getClassMetadata($documentName); 45 | $customRepositoryName = $metadata->customRepositoryClassName; 46 | 47 | if ($customRepositoryName !== null) { 48 | // fetch from the container 49 | if ($this->container->has($customRepositoryName)) { 50 | /** @var ObjectRepository $repository */ 51 | $repository = $this->container->get($customRepositoryName); 52 | 53 | if (! $repository instanceof DocumentRepository) { 54 | throw new RuntimeException(sprintf('The service "%s" must extend DocumentRepository (or a base class, like ServiceDocumentRepository).', $customRepositoryName)); 55 | } 56 | 57 | return $repository; 58 | } 59 | 60 | // if not in the container but the class/id implements the interface, throw an error 61 | if (is_a($customRepositoryName, ServiceDocumentRepositoryInterface::class, true)) { 62 | throw new RuntimeException(sprintf('The "%s" document repository implements "%s", but its service could not be found. Make sure the service exists and is tagged with "%s".', $customRepositoryName, ServiceDocumentRepositoryInterface::class, ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG)); 63 | } 64 | 65 | if (! class_exists($customRepositoryName)) { 66 | throw new RuntimeException(sprintf('The "%s" document has a repositoryClass set to "%s", but this is not a valid class. Check your class naming. If this is meant to be a service id, make sure this service exists and is tagged with "%s".', $metadata->name, $customRepositoryName, ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG)); 67 | } 68 | 69 | // allow the repository to be created below 70 | } 71 | 72 | return $this->getOrCreateRepository($documentManager, $metadata); 73 | } 74 | 75 | /** 76 | * @phpstan-param ClassMetadata $metadata 77 | * 78 | * @phpstan-return ObjectRepository 79 | * 80 | * @template T of object 81 | */ 82 | private function getOrCreateRepository(DocumentManager $documentManager, ClassMetadata $metadata): ObjectRepository 83 | { 84 | $repositoryHash = $metadata->getName() . spl_object_hash($documentManager); 85 | if (isset($this->managedRepositories[$repositoryHash])) { 86 | /** @phpstan-var ObjectRepository */ 87 | return $this->managedRepositories[$repositoryHash]; 88 | } 89 | 90 | if ($metadata->customRepositoryClassName) { 91 | $repositoryClassName = $metadata->customRepositoryClassName; 92 | } elseif ($metadata->isFile) { 93 | /** @phpstan-var class-string> $repositoryClassName */ 94 | $repositoryClassName = $documentManager->getConfiguration()->getDefaultGridFSRepositoryClassName(); 95 | } else { 96 | /** @phpstan-var class-string> $repositoryClassName */ 97 | $repositoryClassName = $documentManager->getConfiguration()->getDefaultDocumentRepositoryClassName(); 98 | } 99 | 100 | return $this->managedRepositories[$repositoryHash] = new $repositoryClassName($documentManager, $documentManager->getUnitOfWork(), $metadata); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Repository/ServiceDocumentRepository.php: -------------------------------------------------------------------------------- 1 | 31 | */ 32 | class ServiceDocumentRepository extends DocumentRepository implements ServiceDocumentRepositoryInterface 33 | { 34 | /** @use ServiceRepositoryTrait */ 35 | use ServiceRepositoryTrait; 36 | } 37 | -------------------------------------------------------------------------------- /src/Repository/ServiceDocumentRepositoryInterface.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | class ServiceGridFSRepository extends DefaultGridFSRepository implements ServiceDocumentRepositoryInterface 27 | { 28 | /** @use ServiceRepositoryTrait */ 29 | use ServiceRepositoryTrait; 30 | } 31 | -------------------------------------------------------------------------------- /src/Repository/ServiceRepositoryTrait.php: -------------------------------------------------------------------------------- 1 | $documentClass 21 | */ 22 | public function __construct(ManagerRegistry $registry, string $documentClass) 23 | { 24 | $manager = $registry->getManagerForClass($documentClass); 25 | assert($manager instanceof DocumentManager || $manager === null); 26 | 27 | if ($manager === null) { 28 | throw new LogicException(sprintf( 29 | 'Could not find the document manager for class "%s". Check your Doctrine configuration to make sure it is configured to load this document’s metadata.', 30 | $documentClass, 31 | )); 32 | } 33 | 34 | parent::__construct($manager, $manager->getUnitOfWork(), $manager->getClassMetadata($documentClass)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Validator/Constraints/Unique.php: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /templates/Collector/mongodb.html.twig: -------------------------------------------------------------------------------- 1 | {% extends '@WebProfiler/Profiler/layout.html.twig' %} 2 | 3 | {% block toolbar %} 4 | {% if collector.commandCount > 0 %} 5 | {% set icon %} 6 | {{ include('@DoctrineMongoDB/Collector/icon.svg') }} 7 | {{ collector.commandCount }} 8 | 9 | in 10 | {{ '%0.2f'|format(collector.time / 1000) }} 11 | ms 12 | 13 | {% endset %} 14 | {% set text %} 15 |
16 | Database commands 17 | {{ collector.commandCount }} 18 |
19 |
20 | Command time 21 | {{ '%0.2f'|format(collector.time / 1000) }} ms 22 |
23 | {% endset %} 24 | {% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %} 25 | {% endif %} 26 | {% endblock %} 27 | 28 | {% block menu %} 29 | 30 | Mongo 31 | Doctrine MongoDB 32 | 33 | {{ collector.commandCount }} 34 | 35 | 36 | {% endblock %} 37 | 38 | {% block panel %} 39 |

Command metrics

40 |
41 |
42 | {{ collector.commandCount }} 43 | Database commands 44 |
45 | 46 |
47 | {{ '%0.3f'|format(collector.time / 1000) }} ms 48 | Command time 49 |
50 |
51 | 52 |

Commands

53 | {% if collector.commands is empty %} 54 |
55 | No commands were performed. 56 |
57 | {% endif %} 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | {% for command in collector.commands %} 69 | 70 | 71 | 72 | 73 | 82 | 83 | {% endfor %} 84 | 85 |
#TimeDatabaseInfo
{{ loop.index }}{{ '%0.3f'|format(command.durationMicros / 1000) }} ms{{ command.database }} 74 | {{ command.command|json_encode() }} 75 | 78 | 81 |
86 | {% endblock %} 87 | -------------------------------------------------------------------------------- /tests/APM/StopwatchCommandLoggerTest.php: -------------------------------------------------------------------------------- 1 | dm = TestCase::createTestDocumentManager(); 22 | 23 | $this->stopwatch = new Stopwatch(true); 24 | $this->commandLogger = new StopwatchCommandLogger($this->stopwatch); 25 | $this->commandLogger->register(); 26 | 27 | parent::setUp(); 28 | } 29 | 30 | protected function tearDown(): void 31 | { 32 | $this->commandLogger->unregister(); 33 | 34 | $this->dm->getDocumentCollection(Category::class)->drop(); 35 | 36 | parent::tearDown(); 37 | } 38 | 39 | public function testItLogsStopwatchEvents(): void 40 | { 41 | $category = new Category('one'); 42 | 43 | $this->dm->persist($category); 44 | $this->dm->flush(); 45 | 46 | $this->dm->remove($category); 47 | $this->dm->flush(); 48 | 49 | $this->dm->getRepository(Category::class)->findAll(); 50 | $events = $this->stopwatch->getSectionEvents('__root__'); 51 | 52 | self::assertCount(3, $events); 53 | 54 | foreach ($events as $eventName => $stopwatchEvent) { 55 | self::assertMatchesRegularExpression('/mongodb_\d+/', $eventName); 56 | self::assertGreaterThan(0, $stopwatchEvent->getDuration()); 57 | self::assertSame('doctrine_mongodb', $stopwatchEvent->getCategory()); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/CacheWarmer/HydratorCacheWarmerTest.php: -------------------------------------------------------------------------------- 1 | container = new Container(); 28 | $this->container->setParameter('doctrine_mongodb.odm.hydrator_dir', sys_get_temp_dir()); 29 | $this->container->setParameter('doctrine_mongodb.odm.auto_generate_hydrator_classes', Configuration::AUTOGENERATE_NEVER); 30 | 31 | $dm = $this->createTestDocumentManager([__DIR__ . '/../Fixtures/Validator']); 32 | 33 | $registryStub = $this->getMockBuilder(ManagerRegistry::class)->disableOriginalConstructor()->getMock(); 34 | $registryStub->method('getManagers')->willReturn([$dm]); 35 | $this->container->set('doctrine_mongodb', $registryStub); 36 | 37 | $this->warmer = new HydratorCacheWarmer($this->container); 38 | } 39 | 40 | public function testWarmerNotOptional(): void 41 | { 42 | $this->assertFalse($this->warmer->isOptional()); 43 | } 44 | 45 | public function testWarmerExecuted(): void 46 | { 47 | $hydratorFilename = $this->getHydratorFilename(); 48 | 49 | try { 50 | $this->warmer->warmUp('meh'); 51 | $this->assertFileExists($hydratorFilename); 52 | } finally { 53 | @unlink($hydratorFilename); 54 | } 55 | } 56 | 57 | /** @dataProvider provideWarmerNotExecuted */ 58 | public function testWarmerNotExecuted(int $autoGenerate): void 59 | { 60 | $this->container->setParameter('doctrine_mongodb.odm.auto_generate_hydrator_classes', $autoGenerate); 61 | $hydratorFilename = $this->getHydratorFilename(); 62 | 63 | try { 64 | $this->warmer->warmUp('meh'); 65 | $this->assertFileDoesNotExist($hydratorFilename); 66 | } finally { 67 | @unlink($hydratorFilename); 68 | } 69 | } 70 | 71 | /** @return array */ 72 | public static function provideWarmerNotExecuted(): array 73 | { 74 | return [ 75 | [ Configuration::AUTOGENERATE_ALWAYS ], 76 | [ Configuration::AUTOGENERATE_EVAL ], 77 | [ Configuration::AUTOGENERATE_FILE_NOT_EXISTS ], 78 | ]; 79 | } 80 | 81 | private function getHydratorFilename(): string 82 | { 83 | return sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'DoctrineBundleMongoDBBundleTestsFixturesValidatorDocumentHydrator.php'; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tests/CacheWarmer/PersistentCollectionCacheWarmerTest.php: -------------------------------------------------------------------------------- 1 | container = new Container(); 30 | $this->container->setParameter('doctrine_mongodb.odm.persistent_collection_dir', sys_get_temp_dir()); 31 | $this->container->setParameter('doctrine_mongodb.odm.auto_generate_persistent_collection_classes', Configuration::AUTOGENERATE_NEVER); 32 | 33 | $this->generatorMock = $this->getMockBuilder(PersistentCollectionGenerator::class)->getMock(); 34 | 35 | $dm = $this->createTestDocumentManager([__DIR__ . '/../Fixtures/Cache']); 36 | $dm->getConfiguration()->setPersistentCollectionGenerator($this->generatorMock); 37 | 38 | $registryStub = $this->getMockBuilder(ManagerRegistry::class)->getMock(); 39 | $registryStub->method('getManagers')->willReturn([$dm]); 40 | $this->container->set('doctrine_mongodb', $registryStub); 41 | 42 | $this->warmer = new PersistentCollectionCacheWarmer($this->container); 43 | } 44 | 45 | public function testWarmerNotOptional(): void 46 | { 47 | $this->assertFalse($this->warmer->isOptional()); 48 | } 49 | 50 | public function testWarmerExecuted(): void 51 | { 52 | $this->generatorMock->expects($this->exactly(2))->method('generateClass'); 53 | $this->warmer->warmUp('meh'); 54 | } 55 | 56 | /** @dataProvider provideWarmerNotExecuted */ 57 | public function testWarmerNotExecuted(int $autoGenerate): void 58 | { 59 | $this->container->setParameter('doctrine_mongodb.odm.auto_generate_persistent_collection_classes', $autoGenerate); 60 | $this->generatorMock->expects($this->exactly(0))->method('generateClass'); 61 | $this->warmer->warmUp('meh'); 62 | } 63 | 64 | public static function provideWarmerNotExecuted(): array 65 | { 66 | return [ 67 | [ Configuration::AUTOGENERATE_ALWAYS ], 68 | [ Configuration::AUTOGENERATE_EVAL ], 69 | [ Configuration::AUTOGENERATE_FILE_NOT_EXISTS ], 70 | ]; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/CacheWarmer/ProxyCacheWarmerTest.php: -------------------------------------------------------------------------------- 1 | container = new Container(); 31 | $this->container->setParameter('doctrine_mongodb.odm.proxy_dir', sys_get_temp_dir()); 32 | $this->container->setParameter('doctrine_mongodb.odm.auto_generate_proxy_classes', Configuration::AUTOGENERATE_EVAL); 33 | 34 | $this->proxyMock = $this->getMockBuilder(ProxyFactory::class)->disableOriginalConstructor()->getMock(); 35 | 36 | $dm = $this->createTestDocumentManager([__DIR__ . '/../Fixtures/Validator']); 37 | $r = new ReflectionObject($dm); 38 | $p = $r->getProperty('proxyFactory'); 39 | $p->setAccessible(true); 40 | $p->setValue($dm, $this->proxyMock); 41 | 42 | $registryStub = $this->getMockBuilder(ManagerRegistry::class)->disableOriginalConstructor()->getMock(); 43 | $registryStub->method('getManagers')->willReturn([$dm]); 44 | $this->container->set('doctrine_mongodb', $registryStub); 45 | 46 | $this->warmer = new ProxyCacheWarmer($this->container); 47 | } 48 | 49 | public function testWarmerNotOptional(): void 50 | { 51 | $this->assertFalse($this->warmer->isOptional()); 52 | } 53 | 54 | public function testWarmerExecuted(): void 55 | { 56 | $this->container->setParameter('doctrine_mongodb.odm.auto_generate_proxy_classes', Configuration::AUTOGENERATE_FILE_NOT_EXISTS); 57 | 58 | $this->proxyMock 59 | ->expects($this->once()) 60 | ->method('generateProxyClasses') 61 | ->with($this->countOf(1)); 62 | $this->warmer->warmUp('meh'); 63 | } 64 | 65 | public function testWarmerNotExecuted(): void 66 | { 67 | $this->container->setParameter('doctrine_mongodb.odm.auto_generate_proxy_classes', Configuration::AUTOGENERATE_EVAL); 68 | $this->proxyMock->expects($this->exactly(0))->method('generateProxyClasses'); 69 | $this->warmer->warmUp('meh'); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tests/Command/CommandTestKernel.php: -------------------------------------------------------------------------------- 1 | loadFromExtension('framework', [ 41 | 'secret' => 'foo', 42 | 'router' => ['utf8' => false], 43 | 'http_method_override' => false, 44 | ]); 45 | 46 | $container->loadFromExtension('doctrine_mongodb', [ 47 | 'connections' => ['default' => []], 48 | 'document_managers' => [ 49 | 'command_test' => [ 50 | 'connection' => 'default', 51 | 'mappings' => ['CommandBundle' => null], 52 | ], 53 | 'command_test_without_documents' => ['connection' => 'default'], 54 | ], 55 | ]); 56 | 57 | $container 58 | ->autowire(UserFixtures::class) 59 | ->addTag(FixturesCompilerPass::FIXTURE_TAG, ['group' => 'test_group']); 60 | 61 | $container 62 | ->autowire(OtherFixtures::class) 63 | ->addTag(FixturesCompilerPass::FIXTURE_TAG); 64 | } 65 | 66 | public function getCacheDir(): string 67 | { 68 | return sys_get_temp_dir() . '/doctrine_mongodb_odm_bundle'; 69 | } 70 | 71 | public function getLogDir(): string 72 | { 73 | return sys_get_temp_dir(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tests/Command/DoctrineODMCommandTest.php: -------------------------------------------------------------------------------- 1 | boot(); 19 | $application = new Application($kernel); 20 | 21 | DoctrineODMCommand::setApplicationDocumentManager($application, $dmName); 22 | 23 | $this->assertInstanceOf(DocumentManagerHelper::class, $application->getHelperSet()->get('dm')); 24 | } 25 | 26 | public static function provideDmName(): iterable 27 | { 28 | yield ['command_test']; 29 | yield [null]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Command/InfoDoctrineODMCommandTest.php: -------------------------------------------------------------------------------- 1 | find('doctrine:mongodb:mapping:info'); 21 | $commandTester = new CommandTester($command); 22 | $commandTester->execute(['--dm' => 'command_test']); 23 | 24 | $output = $commandTester->getDisplay(); 25 | $this->assertStringContainsString('Found 1 documents mapped in document manager command_test', $output); 26 | $this->assertStringContainsString(User::class, $output); 27 | } 28 | 29 | public function testExecuteWithDocumentManagerWithoutDocuments(): void 30 | { 31 | $kernel = new CommandTestKernel('test', false); 32 | $application = new Application($kernel); 33 | 34 | $command = $application->find('doctrine:mongodb:mapping:info'); 35 | $commandTester = new CommandTester($command); 36 | 37 | $this->expectException(Throwable::class); 38 | $this->expectExceptionMessage('You do not have any mapped Doctrine MongoDB ODM documents for any of your bundles. Create a class inside the Document namespace of any of your bundles and provide mapping information for it with Attributes directly in the classes doc blocks or with XML in your bundles Resources/config/doctrine/metadata/mongodb directory'); 39 | 40 | $commandTester->execute(['--dm' => 'command_test_without_documents']); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/Command/LoadDataFixturesDoctrineODMCommandTest.php: -------------------------------------------------------------------------------- 1 | command = $application->find('doctrine:mongodb:fixtures:load'); 22 | } 23 | 24 | public function testIsInteractiveByDefault(): void 25 | { 26 | $commandTester = new CommandTester($this->command); 27 | $commandTester->execute([]); 28 | 29 | $output = $commandTester->getDisplay(); 30 | $this->assertStringContainsString('Careful, database will be purged. Do you want to continue (y/N) ?', $output); 31 | } 32 | 33 | public function testGroup(): void 34 | { 35 | $commandTester = new CommandTester($this->command); 36 | $commandTester->execute([ 37 | '--group' => ['test_group'], 38 | ], ['interactive' => false]); 39 | 40 | $output = $commandTester->getDisplay(); 41 | $this->assertStringContainsString('loading Doctrine\Bundle\MongoDBBundle\Tests\Fixtures\CommandBundle\DataFixtures\UserFixtures', $output); 42 | $this->assertStringNotContainsString('loading Doctrine\Bundle\MongoDBBundle\Tests\Fixtures\CommandBundle\DataFixtures\OtherFixtures', $output); 43 | } 44 | 45 | public function testNonExistingGroup(): void 46 | { 47 | $commandTester = new CommandTester($this->command); 48 | $commandTester->execute([ 49 | '--group' => ['non_existing_group'], 50 | ], ['interactive' => false]); 51 | 52 | $output = $commandTester->getDisplay(); 53 | $this->assertStringContainsString('Could not find any fixture services to load in the groups', $output); 54 | $this->assertStringContainsString('(non_existing_group)', $output); 55 | } 56 | 57 | public function testExecute(): void 58 | { 59 | $commandTester = new CommandTester($this->command); 60 | $commandTester->execute([], ['interactive' => false]); 61 | 62 | $output = $commandTester->getDisplay(); 63 | $this->assertStringContainsString('loading Doctrine\Bundle\MongoDBBundle\Tests\Fixtures\CommandBundle\DataFixtures\UserFixtures', $output); 64 | $this->assertStringContainsString('loading Doctrine\Bundle\MongoDBBundle\Tests\Fixtures\CommandBundle\DataFixtures\OtherFixtures', $output); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/DataCollector/CommandDataCollectorTest.php: -------------------------------------------------------------------------------- 1 | dm = TestCase::createTestDocumentManager(); 23 | 24 | $this->commandLogger = new CommandLogger(); 25 | $this->commandLogger->register(); 26 | 27 | parent::setUp(); 28 | } 29 | 30 | protected function tearDown(): void 31 | { 32 | $this->commandLogger->unregister(); 33 | 34 | $this->dm->getDocumentCollection(Category::class)->drop(); 35 | 36 | parent::tearDown(); 37 | } 38 | 39 | public function testCollector(): void 40 | { 41 | $category = new Category('one'); 42 | 43 | $this->dm->persist($category); 44 | $this->dm->flush(); 45 | 46 | $this->dm->remove($category); 47 | $this->dm->flush(); 48 | 49 | $this->dm->getRepository(Category::class)->findAll(); 50 | 51 | $collector = new CommandDataCollector($this->commandLogger); 52 | $collector->collect(new Request(), new Response()); 53 | 54 | self::assertSame(3, $collector->getCommandCount()); 55 | self::assertGreaterThan(0, $collector->getTime()); 56 | self::assertSame('Category', $collector->getCommands()[0]['command']->insert); 57 | self::assertGreaterThan(0, $collector->getCommands()[0]['durationMicros']); 58 | self::assertSame('Category', $collector->getCommands()[1]['command']->delete); 59 | self::assertGreaterThan(0, $collector->getCommands()[1]['durationMicros']); 60 | self::assertSame('Category', $collector->getCommands()[2]['command']->find); 61 | self::assertGreaterThan(0, $collector->getCommands()[2]['durationMicros']); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/Bundles/AttributesBundle/AttributesBundle.php: -------------------------------------------------------------------------------- 1 | */ 10 | class TestCustomClassRepoRepository extends DocumentRepository 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/Bundles/RepositoryServiceBundle/Repository/TestCustomServiceRepoDocumentRepository.php: -------------------------------------------------------------------------------- 1 | */ 12 | class TestCustomServiceRepoDocumentRepository extends ServiceDocumentRepository 13 | { 14 | public function __construct(ManagerRegistry $registry) 15 | { 16 | parent::__construct($registry, TestCustomServiceRepoDocument::class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/Bundles/RepositoryServiceBundle/Repository/TestCustomServiceRepoGridFSRepository.php: -------------------------------------------------------------------------------- 1 | */ 12 | class TestCustomServiceRepoGridFSRepository extends ServiceDocumentRepository 13 | { 14 | public function __construct(ManagerRegistry $registry) 15 | { 16 | parent::__construct($registry, TestCustomServiceRepoFile::class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/Bundles/RepositoryServiceBundle/Repository/TestUnmappedDocumentRepository.php: -------------------------------------------------------------------------------- 1 | */ 12 | class TestUnmappedDocumentRepository extends ServiceDocumentRepository 13 | { 14 | public function __construct(ManagerRegistry $registry) 15 | { 16 | parent::__construct($registry, TestUnmappedDocument::class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/Bundles/RepositoryServiceBundle/RepositoryServiceBundle.php: -------------------------------------------------------------------------------- 1 | */ 29 | public function registerBundles(): iterable 30 | { 31 | return [ 32 | new FrameworkBundle(), 33 | new DoctrineMongoDBBundle(), 34 | ]; 35 | } 36 | 37 | public function registerContainerConfiguration(LoaderInterface $loader): void 38 | { 39 | $loader->load(static function (ContainerBuilder $container): void { 40 | $container->loadFromExtension('framework', ['secret' => 'F00']); 41 | 42 | $container->loadFromExtension('doctrine_mongodb', [ 43 | 'connections' => ['default' => []], 44 | 'document_managers' => [ 45 | 'default' => [ 46 | 'mappings' => [ 47 | 'RepositoryServiceBundle' => [ 48 | 'type' => 'attribute', 49 | 'dir' => __DIR__ . '/Bundles/RepositoryServiceBundle/Document', 50 | 'prefix' => 'Fixtures\Bundles\RepositoryServiceBundle\Document', 51 | ], 52 | ], 53 | ], 54 | ], 55 | ]); 56 | 57 | // Register a NullLogger to avoid getting the stderr default logger of FrameworkBundle 58 | $container->register('logger', NullLogger::class); 59 | }); 60 | } 61 | 62 | public function getProjectDir(): string 63 | { 64 | if ($this->projectDir === null) { 65 | $this->projectDir = sys_get_temp_dir() . '/sf_kernel_' . md5((string) mt_rand()); 66 | } 67 | 68 | return $this->projectDir; 69 | } 70 | 71 | public function getRootDir(): string 72 | { 73 | return $this->getProjectDir(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/xml/mongodb_service_multiple_connections.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/xml/mongodb_service_simple_single_connection.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Symfony\Component\Cache\Adapter\MemcachedAdapter 15 | localhost 16 | 11211 17 | Memcached 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/xml/mongodb_service_single_connection.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Doctrine\Common\Cache\MemcacheCache 15 | localhost 16 | 11211 17 | Memcached 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/xml/odm_filters.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 1 17 | foo 18 | {"key":"value"} 19 | [1,2,3] 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/xml/odm_imports.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/xml/odm_imports_import.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/xml/odm_resolve_target_document.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | MyUserClass 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/xml/odm_types.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/yml/full.yml: -------------------------------------------------------------------------------- 1 | doctrine_mongodb: 2 | auto_generate_proxy_classes: 2 3 | auto_generate_hydrator_classes: true 4 | auto_generate_persistent_collection_classes: 3 5 | default_connection: conn1 6 | default_database: default_db_name 7 | default_document_manager: default_dm_name 8 | hydrator_dir: "%kernel.cache_dir%/doctrine/odm/mongodb/Test_Hydrators" 9 | hydrator_namespace: Test_Hydrators 10 | proxy_dir: "%kernel.cache_dir%/doctrine/odm/mongodb/Test_Proxies" 11 | proxy_namespace: Test_Proxies 12 | persistent_collection_dir: "%kernel.cache_dir%/doctrine/odm/mongodb/Test_Pcolls" 13 | persistent_collection_namespace: Test_Pcolls 14 | 15 | resolve_target_documents: 16 | Foo\BarInterface: Bar\FooClass 17 | 18 | default_commit_options: 19 | j: false 20 | timeout: 10 21 | w: majority 22 | wtimeout: 10 23 | 24 | connections: 25 | conn1: 26 | server: mongodb://localhost 27 | options: 28 | connectTimeoutMS: 500 29 | authSource: some_db 30 | db: database_val 31 | journal: true 32 | password: password_val 33 | readPreference: secondaryPreferred 34 | readPreferenceTags: 35 | - { dc: east, use: reporting } 36 | - { dc: west } 37 | - { } 38 | replicaSet: foo 39 | socketTimeoutMS: 1000 40 | ssl: true 41 | tls: true 42 | tlsAllowInvalidCertificates: false 43 | tlsAllowInvalidHostnames: false 44 | tlsCAFile: '/path/to/cert.pem' 45 | tlsCertificateKeyFile: '/path/to/key.crt' 46 | tlsCertificateKeyFilePassword: 'secret' 47 | tlsDisableCertificateRevocationCheck: false 48 | tlsDisableOCSPEndpointCheck: false 49 | tlsInsecure: false 50 | authMechanism: MONGODB-X509 51 | username: username_val 52 | retryReads: false 53 | retryWrites: false 54 | w: majority 55 | wTimeoutMS: 1000 56 | driver_options: 57 | context: conn1_context_service 58 | conn2: 59 | server: mongodb://otherhost 60 | 61 | document_managers: 62 | dm1: 63 | repository_factory: doctrine_mongodb.odm.container_repository_factory 64 | persistent_collection_factory: ~ 65 | mappings: 66 | FooBundle: attribute 67 | metadata_cache_driver: 68 | type: memcached 69 | class: fooClass 70 | host: host_val 71 | port: 1234 72 | instance_class: instance_val 73 | profiler: 74 | enabled: true 75 | pretty: false 76 | filters: 77 | disabled_filter: 78 | class: Doctrine\Bundle\MongoDBBundle\Tests\Fixtures\Filter\DisabledFilter 79 | enabled: false 80 | basic_filter: 81 | class: Doctrine\Bundle\MongoDBBundle\Tests\Fixtures\Filter\BasicFilter 82 | enabled: true 83 | complex_filter: 84 | class: Doctrine\Bundle\MongoDBBundle\Tests\Fixtures\Filter\ComplexFilter 85 | enabled: true 86 | parameters: 87 | integer: 1 88 | string: foo 89 | object: { key: value } 90 | array: [ 1, 2, 3 ] 91 | dm2: 92 | connection: dm2_connection 93 | database: db1 94 | default_document_repository_class: Doctrine\Bundle\MongoDBBundle\Tests\Fixtures\Repository\CustomRepository 95 | default_gridfs_repository_class: Doctrine\Bundle\MongoDBBundle\Tests\Fixtures\Repository\CustomGridFSRepository 96 | repository_factory: doctrine_mongodb.odm.container_repository_factory 97 | persistent_collection_factory: ~ 98 | mappings: 99 | BarBundle: 100 | type: xml 101 | dir: "%kernel.cache_dir%" 102 | prefix: prefix_val 103 | alias: alias_val 104 | is_bundle: false 105 | metadata_cache_driver: apcu 106 | logging: true 107 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/yml/mongodb_service_multiple_connections.yml: -------------------------------------------------------------------------------- 1 | doctrine_mongodb: 2 | default_document_manager: dm2 3 | default_connection: conn2 4 | connections: 5 | conn1: 6 | server: mongodb://localhost:27017 7 | conn2: 8 | server: mongodb://localhost:27017 9 | document_managers: 10 | dm1: 11 | connection: conn1 12 | metadata_cache_driver: array 13 | dm2: 14 | connection: conn2 15 | metadata_cache_driver: apcu 16 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/yml/mongodb_service_simple_single_connection.yml: -------------------------------------------------------------------------------- 1 | doctrine_mongodb: 2 | connections: 3 | default: 4 | server: mongodb://localhost:27017 5 | default_database: mydb 6 | document_managers: 7 | default: 8 | metadata_cache_driver: 9 | type: memcached 10 | class: Symfony\Component\Cache\Adapter\MemcachedAdapter 11 | host: localhost 12 | port: 11211 13 | instance_class: Memcached 14 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/yml/mongodb_service_single_connection.yml: -------------------------------------------------------------------------------- 1 | doctrine_mongodb: 2 | connections: 3 | default: 4 | server: mongodb://localhost:27017 5 | document_managers: 6 | default: 7 | connection: default 8 | metadata_cache_driver: 9 | type: memcached 10 | class: Symfony\Component\Cache\Adapter\MemcachedAdapter 11 | host: localhost 12 | port: 11211 13 | instance_class: Memcached 14 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/yml/odm_filters.yml: -------------------------------------------------------------------------------- 1 | doctrine_mongodb: 2 | connections: 3 | default: 4 | server: mongodb://localhost:27017 5 | document_managers: 6 | default: 7 | filters: 8 | disabled_filter: 9 | class: Doctrine\Bundle\MongoDBBundle\Tests\Fixtures\Filter\DisabledFilter 10 | enabled: false 11 | basic_filter: 12 | class: Doctrine\Bundle\MongoDBBundle\Tests\Fixtures\Filter\BasicFilter 13 | enabled: true 14 | complex_filter: 15 | class: Doctrine\Bundle\MongoDBBundle\Tests\Fixtures\Filter\ComplexFilter 16 | enabled: true 17 | parameters: 18 | integer: 1 19 | string: foo 20 | object: { key: value } 21 | array: [ 1, 2, 3 ] 22 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/yml/odm_imports.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: odm_imports_import.yml } 3 | 4 | doctrine_mongodb: 5 | auto_generate_proxy_classes: true 6 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/yml/odm_imports_import.yml: -------------------------------------------------------------------------------- 1 | doctrine_mongodb: 2 | auto_generate_proxy_classes: false 3 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/yml/odm_resolve_target_document.yml: -------------------------------------------------------------------------------- 1 | doctrine_mongodb: 2 | resolve_target_documents: 3 | Symfony\Component\Security\Core\User\UserInterface: MyUserClass 4 | -------------------------------------------------------------------------------- /tests/DependencyInjection/Fixtures/config/yml/odm_types.yml: -------------------------------------------------------------------------------- 1 | doctrine_mongodb: 2 | connections: 3 | default: 4 | server: mongodb://localhost:27017 5 | types: 6 | custom_type_shortcut: Vendor\Type\CustomTypeShortcut 7 | custom_type: 8 | class: Vendor\Type\CustomType 9 | -------------------------------------------------------------------------------- /tests/DependencyInjection/XmlMongoDBExtensionTest.php: -------------------------------------------------------------------------------- 1 | load($file . '.xml'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/DependencyInjection/YamlMongoDBExtensionTest.php: -------------------------------------------------------------------------------- 1 | load($file . '.yml'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/DocumentValueResolverFunctionalTest.php: -------------------------------------------------------------------------------- 1 | get(DocumentManager::class); 31 | $user = new User('user-identifier'); 32 | 33 | $dm->persist($user); 34 | $dm->flush(); 35 | 36 | $client->request('GET', '/user/user-identifier'); 37 | 38 | $this->assertResponseIsSuccessful(); 39 | $this->assertSame('user-identifier', $client->getResponse()->getContent()); 40 | 41 | $dm->remove($user); 42 | } 43 | 44 | public function testWithConfiguration(): void 45 | { 46 | $client = static::createClient(); 47 | 48 | $dm = static::getContainer()->get(DocumentManager::class); 49 | $user = new User('user-identifier'); 50 | 51 | $dm->persist($user); 52 | $dm->flush(); 53 | 54 | $client->request('GET', '/user_with_mapping/user-identifier'); 55 | 56 | $this->assertResponseIsSuccessful(); 57 | $this->assertSame('user-identifier', $client->getResponse()->getContent()); 58 | 59 | $dm->remove($user); 60 | } 61 | 62 | protected static function getKernelClass(): string 63 | { 64 | return FooTestKernel::class; 65 | } 66 | } 67 | 68 | class FooTestKernel extends Kernel 69 | { 70 | use MicroKernelTrait; 71 | 72 | private string $randomKey; 73 | 74 | public function __construct() 75 | { 76 | $this->randomKey = uniqid(''); 77 | 78 | parent::__construct('test', false); 79 | } 80 | 81 | protected function getContainerClass(): string 82 | { 83 | return 'test' . $this->randomKey . parent::getContainerClass(); 84 | } 85 | 86 | public function registerBundles(): array 87 | { 88 | return [ 89 | new FrameworkBundle(), 90 | new DoctrineMongoDBBundle(), 91 | new FooBundle(), 92 | ]; 93 | } 94 | 95 | protected function configureRoutes(RoutingConfigurator $routes): void 96 | { 97 | $routes->add('tv_user_show', '/user/{id}') 98 | ->controller([DocumentValueResolverController::class, 'showUserByDefault']); 99 | 100 | $routes->add('user_with_mapping', '/user_with_mapping/{identifier}') 101 | ->controller([DocumentValueResolverController::class, 'showUserWithMapping']); 102 | } 103 | 104 | protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader): void 105 | { 106 | $c->loadFromExtension('framework', [ 107 | 'secret' => 'foo', 108 | 'router' => ['utf8' => false], 109 | 'http_method_override' => false, 110 | 'test' => true, 111 | ]); 112 | 113 | $c->loadFromExtension('doctrine_mongodb', [ 114 | 'connections' => ['default' => []], 115 | 'document_managers' => [ 116 | 'default' => [ 117 | 'mappings' => [ 118 | 'App' => [ 119 | 'is_bundle' => false, 120 | 'type' => 'attribute', 121 | 'dir' => '%kernel.project_dir%/Document', 122 | 'prefix' => 'Doctrine\Bundle\MongoDBBundle\Tests\Fixtures\FooBundle', 123 | 'alias' => 'Doctrine\Bundle\MongoDBBundle\Tests\Fixtures\FooBundle', 124 | ], 125 | ], 126 | ], 127 | ], 128 | ]); 129 | 130 | $loader->load(__DIR__ . '/Fixtures/FooBundle/config/services.php'); 131 | } 132 | 133 | public function getProjectDir(): string 134 | { 135 | return __DIR__ . '/Fixtures/FooBundle/'; 136 | } 137 | 138 | public function getCacheDir(): string 139 | { 140 | return sys_get_temp_dir() . '/doctrine_mongodb_odm_bundle' . $this->randomKey; 141 | } 142 | 143 | public function getLogDir(): string 144 | { 145 | return sys_get_temp_dir(); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /tests/Fixtures/Cache/Collections.php: -------------------------------------------------------------------------------- 1 | */ 28 | class SomeCollection extends ArrayCollection 29 | { 30 | } 31 | 32 | /** @template-extends ArrayCollection */ 33 | class AnotherCollection extends ArrayCollection 34 | { 35 | } 36 | -------------------------------------------------------------------------------- /tests/Fixtures/CommandBundle/CommandBundle.php: -------------------------------------------------------------------------------- 1 | name = $name; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Fixtures/Filter/BasicFilter.php: -------------------------------------------------------------------------------- 1 | getId()); 21 | } 22 | 23 | #[Route(path: '/user_with_identifier/{identifier}', name: 'tv_user_show_with_identifier')] 24 | public function showUserWithMapping( 25 | #[MapDocument(mapping: ['identifier' => 'id'])] 26 | User $user, 27 | ): Response { 28 | return new Response($user->getId()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/Fixtures/FooBundle/DataFixtures/DependentOnRequiredConstructorArgsFixtures.php: -------------------------------------------------------------------------------- 1 | id; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Fixtures/FooBundle/FooBundle.php: -------------------------------------------------------------------------------- 1 | services() 9 | ->defaults() 10 | ->autowire() 11 | ->autoconfigure(); 12 | 13 | $services->load('Doctrine\\Bundle\\MongoDBBundle\\Tests\\Fixtures\\FooBundle\\', '..') 14 | ->exclude('../{config,DataFixtures,Document}'); 15 | }; 16 | -------------------------------------------------------------------------------- /tests/Fixtures/Form/Category.php: -------------------------------------------------------------------------------- 1 | */ 23 | #[ODM\ReferenceMany( 24 | targetDocument: Document::class, 25 | mappedBy: 'categories', 26 | )] 27 | public Collection $documents; 28 | 29 | public function __construct(string $name) 30 | { 31 | $this->name = $name; 32 | $this->documents = new ArrayCollection(); 33 | } 34 | 35 | public function __toString(): string 36 | { 37 | return $this->name; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/Fixtures/Form/Document.php: -------------------------------------------------------------------------------- 1 | */ 24 | #[ODM\ReferenceMany( 25 | targetDocument: Category::class, 26 | inversedBy: 'documents', 27 | strategy: ClassMetadata::STORAGE_STRATEGY_ATOMIC_SET_ARRAY, 28 | )] 29 | public Collection $categories; 30 | 31 | public function __construct(ObjectId $id, string $name) 32 | { 33 | $this->id = $id; 34 | $this->name = $name; 35 | $this->categories = new ArrayCollection(); 36 | } 37 | 38 | public function __toString(): string 39 | { 40 | return $this->name; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/Fixtures/Form/Guesser.php: -------------------------------------------------------------------------------- 1 | */ 30 | #[ODM\ReferenceMany( 31 | targetDocument: Category::class, 32 | inversedBy: 'documents', 33 | strategy: ClassMetadata::STORAGE_STRATEGY_ATOMIC_SET_ARRAY, 34 | )] 35 | public Collection $categories; 36 | 37 | #[ODM\Field(type: Type::BOOL)] 38 | public ?bool $boolField = null; 39 | 40 | #[ODM\Field(type: Type::FLOAT)] 41 | public ?float $floatField = null; 42 | 43 | #[ODM\Field(type: Type::INT)] 44 | public ?int $intField = null; 45 | 46 | #[ODM\Field(type: Type::COLLECTION)] 47 | public array $collectionField; 48 | 49 | public mixed $nonMappedField; 50 | } 51 | -------------------------------------------------------------------------------- /tests/Fixtures/Repository/CustomGridFSRepository.php: -------------------------------------------------------------------------------- 1 | */ 10 | final class CustomGridFSRepository extends DocumentRepository 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /tests/Fixtures/Repository/CustomRepository.php: -------------------------------------------------------------------------------- 1 | */ 10 | final class CustomRepository extends DocumentRepository 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /tests/Fixtures/Security/User.php: -------------------------------------------------------------------------------- 1 | id = $id; 24 | $this->name = $name; 25 | } 26 | 27 | public function getRoles(): array 28 | { 29 | return []; 30 | } 31 | 32 | public function getPassword(): void 33 | { 34 | } 35 | 36 | public function getSalt(): void 37 | { 38 | } 39 | 40 | public function getUserIdentifier(): string 41 | { 42 | return $this->name; 43 | } 44 | 45 | public function getUsername(): string 46 | { 47 | return $this->getUserIdentifier(); 48 | } 49 | 50 | public function eraseCredentials(): void 51 | { 52 | } 53 | 54 | public function equals(UserInterface $user): void 55 | { 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/Fixtures/Validator/Document.php: -------------------------------------------------------------------------------- 1 | id = $id; 39 | } 40 | } 41 | 42 | #[ODM\EmbeddedDocument] 43 | class EmbeddedDocument 44 | { 45 | #[ODM\Field(type: Type::STRING)] 46 | public string $name; 47 | } 48 | -------------------------------------------------------------------------------- /tests/Form/Type/DocumentTypeTest.php: -------------------------------------------------------------------------------- 1 | dm = TestCase::createTestDocumentManager([ 34 | __DIR__ . '/../../Fixtures/Form/Document', 35 | ]); 36 | $this->dmRegistry = $this->createRegistryMock('default', $this->dm); 37 | 38 | parent::setUp(); 39 | } 40 | 41 | protected function tearDown(): void 42 | { 43 | $documentClasses = [ 44 | Document::class, 45 | Category::class, 46 | ]; 47 | 48 | foreach ($documentClasses as $class) { 49 | $this->dm->getDocumentCollection($class)->drop(); 50 | } 51 | 52 | parent::tearDown(); 53 | } 54 | 55 | public function testDocumentManagerOptionSetsEmOption(): void 56 | { 57 | $field = $this->factory->createNamed('name', DocumentType::class, null, [ 58 | 'class' => Document::class, 59 | 'document_manager' => 'default', 60 | ]); 61 | 62 | $this->assertSame($this->dm, $field->getConfig()->getOption('em')); 63 | } 64 | 65 | public function testDocumentManagerInstancePassedAsOption(): void 66 | { 67 | $field = $this->factory->createNamed('name', DocumentType::class, null, [ 68 | 'class' => Document::class, 69 | 'document_manager' => $this->dm, 70 | ]); 71 | 72 | $this->assertSame($this->dm, $field->getConfig()->getOption('em')); 73 | } 74 | 75 | public function testSettingDocumentManagerAndEmOptionShouldThrowException(): void 76 | { 77 | $this->expectException(InvalidArgumentException::class); 78 | $this->factory->createNamed('name', DocumentType::class, null, [ 79 | 'document_manager' => 'default', 80 | 'em' => 'default', 81 | ]); 82 | } 83 | 84 | public function testManyToManyReferences(): void 85 | { 86 | $categoryOne = new Category('one'); 87 | $this->dm->persist($categoryOne); 88 | $categoryTwo = new Category('two'); 89 | $this->dm->persist($categoryTwo); 90 | 91 | $document = new Document(new ObjectId(), 'document'); 92 | $document->categories[] = $categoryOne; 93 | $this->dm->persist($document); 94 | 95 | $this->dm->flush(); 96 | 97 | $form = $this->factory->create(FormType::class, $document) 98 | ->add( 99 | 'categories', 100 | DocumentType::class, 101 | [ 102 | 'class' => Category::class, 103 | 'multiple' => true, 104 | 'expanded' => true, 105 | 'document_manager' => 'default', 106 | ], 107 | ); 108 | 109 | $view = $form->createView(); 110 | $categoryView = $view['categories']; 111 | $this->assertInstanceOf(FormView::class, $categoryView); 112 | 113 | $this->assertCount(2, $categoryView->children); 114 | $this->assertTrue($form->get('categories')->get('0')->createView()->vars['checked']); 115 | $this->assertFalse($form->get('categories')->get('1')->createView()->vars['checked']); 116 | } 117 | 118 | /** @return MockObject&ManagerRegistry */ 119 | protected function createRegistryMock(string $name, DocumentManager $dm): MockObject 120 | { 121 | $registry = $this->createMock(ManagerRegistry::class); 122 | $registry 123 | ->method('getManager') 124 | ->with($this->equalTo($name)) 125 | ->willReturn($dm); 126 | 127 | return $registry; 128 | } 129 | 130 | /** @return FormExtensionInterface[] */ 131 | protected function getExtensions(): array 132 | { 133 | return array_merge(parent::getExtensions(), [ 134 | new DoctrineMongoDBExtension($this->dmRegistry), 135 | ]); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /tests/Form/Type/GuesserTestType.php: -------------------------------------------------------------------------------- 1 | add('name') 18 | // Not setting "date_widget" is deprecated in Symfony 6.4 19 | ->add('date', null, ['date_widget' => 'single_text']) 20 | ->add('ts', null, ['date_widget' => 'single_text']) 21 | ->add('categories', null, ['document_manager' => $options['dm']]) 22 | ->add('boolField') 23 | ->add('floatField') 24 | ->add('intField') 25 | ->add('collectionField') 26 | ->add('nonMappedField'); 27 | } 28 | 29 | public function configureOptions(OptionsResolver $resolver): void 30 | { 31 | $resolver->setDefaults([ 32 | 'data_class' => Guesser::class, 33 | ])->setRequired('dm'); 34 | } 35 | 36 | public function getBlockPrefix(): string 37 | { 38 | return 'guesser_test'; 39 | } 40 | 41 | public function getName(): string 42 | { 43 | return $this->getBlockPrefix(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/Form/Type/TypeGuesserTest.php: -------------------------------------------------------------------------------- 1 | dm = TestCase::createTestDocumentManager([ 31 | __DIR__ . '/../../Fixtures/Form/Guesser', 32 | ]); 33 | $this->dmRegistry = $this->createRegistryMock('default', $this->dm); 34 | 35 | parent::setUp(); 36 | } 37 | 38 | protected function tearDown(): void 39 | { 40 | $documentClasses = [ 41 | Document::class, 42 | Category::class, 43 | Guesser::class, 44 | ]; 45 | 46 | foreach ($documentClasses as $class) { 47 | $this->dm->getDocumentCollection($class)->drop(); 48 | } 49 | 50 | parent::tearDown(); 51 | } 52 | 53 | public function testTypesShouldBeGuessedCorrectly(): void 54 | { 55 | $form = $this->factory->create(GuesserTestType::class, null, ['dm' => $this->dm]); 56 | $this->assertType('text', $form->get('name')); 57 | $this->assertType('document', $form->get('categories')); 58 | $this->assertType('datetime', $form->get('date')); 59 | $this->assertType('datetime', $form->get('ts')); 60 | $this->assertType('checkbox', $form->get('boolField')); 61 | $this->assertType('number', $form->get('floatField')); 62 | $this->assertType('integer', $form->get('intField')); 63 | $this->assertType('collection', $form->get('collectionField')); 64 | $this->assertType('text', $form->get('nonMappedField')); 65 | } 66 | 67 | protected function assertType(string $type, FormInterface $form): void 68 | { 69 | $this->assertEquals($type, $form->getConfig()->getType()->getBlockPrefix()); 70 | } 71 | 72 | /** @return MockObject&ManagerRegistry */ 73 | protected function createRegistryMock(string $name, DocumentManager $dm): MockObject 74 | { 75 | $registry = $this->createMock(ManagerRegistry::class); 76 | $registry 77 | ->method('getManager') 78 | ->with($this->equalTo($name)) 79 | ->willReturn($dm); 80 | $registry 81 | ->method('getManagers') 82 | ->willReturn(['default' => $dm]); 83 | 84 | return $registry; 85 | } 86 | 87 | /** @return FormExtensionInterface[] */ 88 | protected function getExtensions(): array 89 | { 90 | return array_merge(parent::getExtensions(), [ 91 | new DoctrineMongoDBExtension($this->dmRegistry), 92 | ]); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /tests/ManagerRegistryTest.php: -------------------------------------------------------------------------------- 1 | register('manager.default', DocumentManagerStub::class) 20 | ->setPublic(true); 21 | $container->register('manager.lazy', DocumentManagerStub::class) 22 | ->setPublic(true) 23 | ->setLazy(true) 24 | ->addTag('proxy', ['interface' => ObjectManager::class]); 25 | $container->compile(); 26 | 27 | /** @var class-string $containerClass */ 28 | $containerClass = 'MongoDBManagerRepositoryTestResetContainer'; 29 | $dumper = new PhpDumper($container); 30 | eval('?' . '>' . $dumper->dump(['class' => $containerClass])); 31 | 32 | $container = new $containerClass(); 33 | $repository = new ManagerRegistry('MongoDB', [], [ 34 | 'default' => 'manager.default', 35 | 'lazy' => 'manager.lazy', 36 | ], '', '', '', $container); 37 | 38 | DocumentManagerStub::$clearCount = 0; 39 | 40 | $repository->reset(); 41 | 42 | // Service was not initialized, so reset should not be called 43 | $this->assertSame(0, DocumentManagerStub::$clearCount); 44 | 45 | // The lazy service is reinitialized instead of being cleared 46 | $container->get('manager.lazy')->flush(); 47 | $repository->reset(); 48 | $this->assertSame(0, DocumentManagerStub::$clearCount); 49 | 50 | // The default service is cleared when initialized 51 | $container->get('manager.default')->flush(); 52 | $repository->reset(); 53 | $this->assertSame(1, DocumentManagerStub::$clearCount); 54 | } 55 | } 56 | 57 | class DocumentManagerStub extends DocumentManager 58 | { 59 | public static int $clearCount; 60 | 61 | public function __construct() 62 | { 63 | } 64 | 65 | /** {@inheritDoc} */ 66 | public function clear($objectName = null): void 67 | { 68 | self::$clearCount++; 69 | } 70 | 71 | public function flush(array $options = []): void 72 | { 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/Mapping/Driver/AbstractDriverTestCase.php: -------------------------------------------------------------------------------- 1 | getDriver([ 18 | 'foo' => 'MyNamespace\MyBundle\DocumentFoo', 19 | $this->getFixtureDir() => 'MyNamespace\MyBundle\Document', 20 | ]); 21 | 22 | $locator = $this->getDriverLocator($driver); 23 | 24 | $this->assertEquals( 25 | $this->getFixtureDir() . '/Foo' . $this->getFileExtension(), 26 | $locator->findMappingFile('MyNamespace\MyBundle\Document\Foo'), 27 | ); 28 | } 29 | 30 | public function testFindMappingFileInSubnamespace(): void 31 | { 32 | $driver = $this->getDriver([$this->getFixtureDir() => 'MyNamespace\MyBundle\Document']); 33 | 34 | $locator = $this->getDriverLocator($driver); 35 | 36 | $this->assertEquals( 37 | $this->getFixtureDir() . '/Foo.Bar' . $this->getFileExtension(), 38 | $locator->findMappingFile('MyNamespace\MyBundle\Document\Foo\Bar'), 39 | ); 40 | } 41 | 42 | public function testFindMappingFileNamespacedFoundFileNotFound(): void 43 | { 44 | $driver = $this->getDriver([$this->getFixtureDir() => 'MyNamespace\MyBundle\Document']); 45 | 46 | $locator = $this->getDriverLocator($driver); 47 | 48 | $this->expectException(MappingException::class); 49 | 50 | $locator->findMappingFile('MyNamespace\MyBundle\Document\Missing'); 51 | } 52 | 53 | public function testFindMappingNamespaceNotFound(): void 54 | { 55 | $driver = $this->getDriver([$this->getFixtureDir() => 'MyNamespace\MyBundle\Document']); 56 | 57 | $locator = $this->getDriverLocator($driver); 58 | 59 | $this->expectException(MappingException::class); 60 | 61 | $locator->findMappingFile('MyOtherNamespace\MyBundle\Document\Foo'); 62 | } 63 | 64 | abstract protected function getFileExtension(): string; 65 | 66 | abstract protected function getFixtureDir(): string; 67 | 68 | abstract protected function getDriver(array $paths = []): FileDriver; 69 | 70 | private function getDriverLocator(FileDriver $driver): FileLocator 71 | { 72 | $ref = new ReflectionProperty($driver, 'locator'); 73 | $ref->setAccessible(true); 74 | 75 | return $ref->getValue($driver); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/Mapping/Driver/Fixtures/xml/Foo.Bar.mongodb.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /tests/Mapping/Driver/Fixtures/xml/Foo.mongodb.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /tests/Mapping/Driver/XmlDriverTest.php: -------------------------------------------------------------------------------- 1 | getFileExtension()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | setAutoGenerateProxyClasses(Configuration::AUTOGENERATE_FILE_NOT_EXISTS); 22 | $config->setProxyDir(sys_get_temp_dir()); 23 | $config->setHydratorDir(sys_get_temp_dir()); 24 | $config->setProxyNamespace('SymfonyTests\Doctrine'); 25 | $config->setHydratorNamespace('SymfonyTests\Doctrine'); 26 | $config->setMetadataDriverImpl(new AttributeDriver($paths)); 27 | $config->setMetadataCache(new ArrayAdapter()); 28 | 29 | return DocumentManager::create(null, $config); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Validator/Constraints/UniqueTest.php: -------------------------------------------------------------------------------- 1 | loadClassMetadata($metadata)); 21 | 22 | [$constraint] = $metadata->getConstraints(); 23 | self::assertInstanceOf(Unique::class, $constraint); 24 | self::assertSame(['email'], $constraint->fields); 25 | self::assertSame('doctrine_odm.mongodb.unique', $constraint->validatedBy()); 26 | } 27 | } 28 | 29 | #[Unique(['email'])] 30 | class UniqueDocumentDummyOne 31 | { 32 | private string $email; 33 | } 34 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 |