diff --git a/README.md b/README.md index 774737c540..74b096b12d 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,9 @@ flushed in a behavioral way. composer require gedmo/doctrine-extensions -* [Symfony](/doc/symfony.md) -* [Laravel 5](https://www.laraveldoctrine.org/docs/1.3/extensions) -* [Laminas](/doc/laminas.md) +* [Symfony](/doc/frameworks/symfony.md) +* [Laravel](/doc/frameworks/laravel.md) +* [Laminas](/doc/frameworks/laminas.md) ### Upgrading diff --git a/doc/annotations.md b/doc/annotations.md index 92010c305c..f77ae52d5f 100644 --- a/doc/annotations.md +++ b/doc/annotations.md @@ -1,7 +1,7 @@ # Annotations Reference > [!IMPORTANT] -> To use annotations, you will need the deprecated [`doctrine/annotations`](https://www.doctrine-project.org/projects/annotations.html) library. PHP 8 users are encouraged to migrate and use [attributes](./attributes.md) as annotation support is deprecated in all supported Doctrine object managers. +> Support for annotations is deprecated and will be removed in 4.0. PHP 8 users are encouraged to migrate and use [attributes](./attributes.md) instead of annotations. To use annotations, you will need the [`doctrine/annotations`](https://www.doctrine-project.org/projects/annotations.html) library. Below you will a reference for annotations supported in this extensions library. There will be introduction on usage with examples. For more detailed usage of each diff --git a/doc/frameworks/laminas.md b/doc/frameworks/laminas.md new file mode 100644 index 0000000000..562e6ad280 --- /dev/null +++ b/doc/frameworks/laminas.md @@ -0,0 +1,383 @@ +# Integrate the Doctrine Extensions in Laminas + +This guide will demonstrate how to integrate the Doctrine Extensions library into a Laminas application. + +## Index + +- [Getting Started](#getting-started) +- [Registering Extension Listeners](#registering-extension-listeners) +- [Registering Mapping Configuration](#registering-mapping-configuration) +- [Registering Filters](#registering-filters) +- [Configuring Extensions via Event Listeners](#configuring-extensions-via-event-listeners) + +## Getting Started + +> [!TIP] +> This guide is written using the Laminas MVC quick start as the foundation. + +Assuming you have already [created your Laminas application](https://docs.laminas.dev/laminas-mvc/quick-start/), +the next step will be to ensure you've installed this library and the Doctrine libraries you will need. + +For Doctrine MongoDB ODM users, this Composer command will install all required dependencies: + +```shell +composer require doctrine/doctrine-module doctrine/doctrine-mongo-odm-module doctrine/mongodb-odm gedmo/doctrine-extensions +``` + +For Doctrine ORM users, this Composer command will install all required dependencies: + +```shell +composer require doctrine/dbal doctrine/doctrine-module doctrine/doctrine-orm-module doctrine/orm gedmo/doctrine-extensions +``` + +## Registering Extension Listeners + +At the heart of the Doctrine Extensions library are the listeners which enable each extension. The below example demonstrates +how to register and enable all listeners provided by this library. + +### Extensions Compatible with all Managers + +```php + [ + 'invokables' => [ + 'gedmo.mapping.driver.attribute' => AttributeReader::class, + ], + 'factories' => [ + 'gedmo.listener.blameable' => function (ContainerInterface $container, string $requestedName): BlameableListener { + $listener = new BlameableListener(); + + // This call configures the listener to use the attribute driver service created above; if using annotations, you will need to provide the appropriate service instead + $listener->setAnnotationReader($container->get('gedmo.mapping.driver.attribute')); + + return $listener; + }, + 'gedmo.listener.ip_traceable' => function (ContainerInterface $container, string $requestedName): IpTraceableListener { + $listener = new IpTraceableListener(); + + // This call configures the listener to use the attribute driver service created above; if using annotations, you will need to provide the appropriate service instead + $listener->setAnnotationReader($container->get('gedmo.mapping.driver.attribute')); + + return $listener; + }, + 'gedmo.listener.loggable' => function (ContainerInterface $container, string $requestedName): LoggableListener { + $listener = new LoggableListener(); + + // This call configures the listener to use the attribute driver service created above; if using annotations, you will need to provide the appropriate service instead + $listener->setAnnotationReader($container->get('gedmo.mapping.driver.attribute')); + + return $listener; + }, + 'gedmo.listener.sluggable' => function (ContainerInterface $container, string $requestedName): SluggableListener { + $listener = new SluggableListener(); + + // This call configures the listener to use the attribute driver service created above; if using annotations, you will need to provide the appropriate service instead + $listener->setAnnotationReader($container->get('gedmo.mapping.driver.attribute')); + + return $listener; + }, + 'gedmo.listener.soft_deleteable' => function (ContainerInterface $container, string $requestedName): SoftDeleteableListener { + $listener = new SoftDeleteableListener(); + + // This call configures the listener to use the attribute driver service created above; if using annotations, you will need to provide the appropriate service instead + $listener->setAnnotationReader($container->get('gedmo.mapping.driver.attribute')); + + // If your application uses a PSR-20 clock, you can provide it to this listener by uncommenting the below line + // $listener->setClock($container->get(ClockInterface::class)); + + return $listener; + }, + 'gedmo.listener.sortable' => function (ContainerInterface $container, string $requestedName): SortableListener { + $listener = new SortableListener(); + + // This call configures the listener to use the attribute driver service created above; if using annotations, you will need to provide the appropriate service instead + $listener->setAnnotationReader($container->get('gedmo.mapping.driver.attribute')); + + return $listener; + }, + 'gedmo.listener.timestampable' => function (ContainerInterface $container, string $requestedName): TimestampableListener { + $listener = new TimestampableListener(); + + // This call configures the listener to use the attribute driver service created above; if using annotations, you will need to provide the appropriate service instead + $listener->setAnnotationReader($container->get('gedmo.mapping.driver.attribute')); + + // If your application uses a PSR-20 clock, you can provide it to this listener by uncommenting the below line + // $listener->setClock($container->get(ClockInterface::class)); + + return $listener; + }, + 'gedmo.listener.translatable' => function (ContainerInterface $container, string $requestedName): TranslatableListener { + $listener = new TranslatableListener(); + + // This call configures the listener to use the attribute driver service created above; if using annotations, you will need to provide the appropriate service instead + $listener->setAnnotationReader($container->get('gedmo.mapping.driver.attribute')); + + // If your application uses a PSR-20 clock, you can provide it to this listener by uncommenting the below line + // $listener->setClock($container->get(ClockInterface::class)); + + return $listener; + }, + ], + ], + 'doctrine' => [ + 'eventmanager' => [ + 'orm_default' => [ + 'subscribers' => [ + 'gedmo.listener.blameable', + 'gedmo.listener.ip_traceable', + 'gedmo.listener.loggable', + 'gedmo.listener.sluggable', + 'gedmo.listener.soft_deleteable', + 'gedmo.listener.sortable', + 'gedmo.listener.timestampable', + 'gedmo.listener.translatable', + 'gedmo.listener.tree', + ], + ], + ], + ], +]; +``` + +### Extensions Compatible with MongoDB ODM Only + +```php + [ + 'factories' => [ + 'gedmo.listener.reference_integrity' => function (ContainerInterface $container, string $requestedName): ReferenceIntegrityListener { + $listener = new ReferenceIntegrityListener(); + + // This call configures the listener to use the attribute driver service created above; if using annotations, you will need to provide the appropriate service instead + $listener->setAnnotationReader($container->get('gedmo.mapping.driver.attribute')); + + return $listener; + }, + 'gedmo.listener.references' => function (ContainerInterface $container, string $requestedName): ReferencesListener { + $listener = new ReferencesListener(); + + // This call configures the listener to use the attribute driver service created above; if using annotations, you will need to provide the appropriate service instead + $listener->setAnnotationReader($container->get('gedmo.mapping.driver.attribute')); + + return $listener; + }, + ], + ], + 'doctrine' => [ + 'eventmanager' => [ + 'odm_default' => [ + 'subscribers' => [ + 'gedmo.listener.reference_integrity', + 'gedmo.listener.references', + ], + ], + ], + ], +]; +``` + +### Extensions Compatible with ORM Only + +```php + [ + 'factories' => [ + 'gedmo.listener.uploadable' => function (ContainerInterface $container, string $requestedName): UploadableListener { + $listener = new UploadableListener(); + + // This call configures the listener to use the attribute driver service created above; if using annotations, you will need to provide the appropriate service instead + $listener->setAnnotationReader($container->get('gedmo.mapping.driver.attribute')); + + return $listener; + }, + ], + ], + 'doctrine' => [ + 'eventmanager' => [ + 'orm_default' => [ + 'subscribers' => [ + 'gedmo.listener.uploadable', + ], + ], + ], + ], +]; +``` + +## Registering Mapping Configuration + +When using the [Loggable](../loggable.md), [Translatable](../translatable.md), or [Tree](../tree.md) extensions, you will +need to register the mappings for these extensions to your object managers. + +> [!NOTE] +> These extensions only provide mappings through annotations or attributes, with support for annotations being deprecated. If using annotations, you will need to ensure the [`doctrine/annotations`](https://www.doctrine-project.org/projects/annotations.html) library is installed and configured. + +### MongoDB ODM Mapping + +> [!IMPORTANT] +> The tree extension does NOT have any objects to map when using the MongoDB ODM. + +The below example shows a configuration adding all available mappings to the default document manager. + +```php + [ + 'driver' => [ + 'gedmo.odm_driver' => [ + 'class' => AttributeDriver::class, // If your application is using annotations, use the AnnotationDriver class instead + 'paths' => [ + '/path/to/vendor/gedmo/doctrine-extensions/src/Loggable/Document', + '/path/to/vendor/gedmo/doctrine-extensions/src/Translatable/Document', + ], + ], + 'odm_default' => [ + 'drivers' => [ + 'gedmo.odm_driver', // Adds the mapping driver created above to the default mapping chain + ], + ], + ], + ], +]; +``` + +### ORM Mapping + +The below example shows a configuration adding all available mappings to the default entity manager. + +```php + [ + 'driver' => [ + 'gedmo.orm_driver' => [ + 'class' => AttributeDriver::class, // If your application is using annotations, use the AnnotationDriver class instead + 'paths' => [ + '/path/to/vendor/gedmo/doctrine-extensions/src/Loggable/Entity', + '/path/to/vendor/gedmo/doctrine-extensions/src/Translatable/Entity', + '/path/to/vendor/gedmo/doctrine-extensions/src/Tree/Entity', + ], + ], + 'orm_default' => [ + 'drivers' => [ + 'gedmo.orm_driver', // Adds the mapping driver created above to the default mapping chain + ], + ], + ], + ], +]; +``` + +To verify your configuration, you can use the `orm:info` command from the `doctrine-module` CLI tool to make sure the entities are registered. + +```sh +$ vendor/bin/doctrine-module orm:info + Found X mapped entities: + + [OK] Gedmo\Loggable\Entity\LogEntry + [OK] Gedmo\Loggable\Entity\MappedSuperclass\AbstractLogEntry + [OK] Gedmo\Translatable\Entity\Translation + [OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractTranslation + [OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation + [OK] Gedmo\Tree\Entity\MappedSuperclass\AbstractClosure +``` + +## Registering Filters + +### Soft Deleteable Filter + +When using the [Soft Deleteable](../softdeleteable.md) extension, a filter is available which allows configuring whether +soft-deleted objects are included in query results. + +> [!NOTE] +> The default configuration in the Laminas modules does not enable the filters. To use these filters, you will need to enable them separately. + +#### MongoDB ODM Filter Configuration + +The below example shows a configuration adding the filter to the default document manager. To enable the filter, +you can follow the [Filters documentation guide](https://www.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/filters.html#disabling-enabling-filters-and-setting-parameters). + +```php + [ + 'configuration' => [ + 'odm_default' => [ + 'filters' => [ + 'soft-deleteable' => SoftDeleteableFilter::class, + ], + ], + ], + ], +]; +``` + +#### ORM Filter Configuration + +The below example shows a configuration adding the filter to the default entity manager. To enable the filter, +you can follow the [Filters documentation guide](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/filters.html#disabling-enabling-filters-and-setting-parameters). + +```php + [ + 'configuration' => [ + 'orm_default' => [ + 'filters' => [ + 'soft-deleteable' => SoftDeleteableFilter::class, + ], + ], + ], + ], +]; +``` + +## Configuring Extensions via Event Listeners + +When using the [Blameable](../blameable.md), [IP Traceable](../ip_traceable.md), [Loggable](../loggable.md), or +[Translatable](../translatable.md) extensions, to work correctly, they require extra information that must be set +at runtime. + +**Help Improve This Documentation** + +Pull requests are welcome to expand this section of the documentation. diff --git a/doc/frameworks/laravel.md b/doc/frameworks/laravel.md new file mode 100644 index 0000000000..0afe9e9834 --- /dev/null +++ b/doc/frameworks/laravel.md @@ -0,0 +1,5 @@ +# Integrate the Doctrine Extensions in Laravel + +When using the Laravel Doctrine package with the Doctrine ORM, you can use the [Laravel Doctrine Extensions](https://www.laraveldoctrine.org/docs/current/extensions) +package to add this library to your application. Please review the extensions package documentation linked earlier for +detailed instructions. diff --git a/doc/frameworks/symfony.md b/doc/frameworks/symfony.md new file mode 100644 index 0000000000..dd910318a8 --- /dev/null +++ b/doc/frameworks/symfony.md @@ -0,0 +1,481 @@ +# Integrate the Doctrine Extensions in Symfony + +This guide will demonstrate how to integrate the Doctrine Extensions library into a Symfony application. + +> [!TIP] +> We recommend using the [`StofDoctrineExtensionsBundle`](https://symfony.com/bundles/StofDoctrineExtensionsBundle/current/index.html) which handles this integration for you. + +## Index + +- [Getting Started](#getting-started) +- [Registering Extension Listeners](#registering-extension-listeners) +- [Registering Mapping Configuration](#registering-mapping-configuration) +- [Registering Filters](#registering-filters) +- [Configuring Extensions via Event Subscribers](#configuring-extensions-via-event-subscribers) + +## Getting Started + +Assuming you have already [created your Symfony application](https://symfony.com/doc/current/getting_started/index.html), +the next step will be to ensure you've installed this library and the Doctrine libraries you will need. + +For Doctrine MongoDB ODM users, this Composer command will install all required dependencies: + +```shell +composer require doctrine/mongodb-odm doctrine/mongodb-odm-bundle gedmo/doctrine-extensions +``` + +For Doctrine ORM users, this Composer command will install all required dependencies: + +```shell +composer require doctrine/dbal doctrine/doctrine-bundle doctrine/orm gedmo/doctrine-extensions +``` + +## Registering Extension Listeners + +At the heart of the Doctrine Extensions library are the listeners which enable each extension. The below example demonstrates +how to register and enable all listeners provided by this library. + +### Extensions Compatible with all Managers + +> [!NOTE] +> This example shows the configuration when using the ORM and `DoctrineBundle` with a single default entity manager. When using the MongoDB ODM and `DoctrineMongoDBBundle`, the tag name should be `doctrine_mongodb.odm.event_listener` instead of `doctrine.event_listener`. When using an application with multiple managers, a separate tag is needed with the `connection` attribute for each connection. + +```yaml +services: + # Attribute mapping driver for the Doctrine Extension listeners + gedmo.mapping.driver.attribute: + class: Gedmo\Mapping\Driver\AttributeReader + + # Gedmo Blameable Extension Listener + gedmo.listener.blameable: + class: Gedmo\Blameable\BlameableListener + tags: + - { name: doctrine.event_listener, event: 'prePersist' } + - { name: doctrine.event_listener, event: 'onFlush' } + - { name: doctrine.event_listener, event: 'loadClassMetadata' } + calls: + # Uncomment the below call if using attributes, and comment the call for the annotation reader + # - [ setAnnotationReader, [ '@gedmo.mapping.driver.attribute' ] ] + # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 + - [ setAnnotationReader, [ '@annotation_reader' ] ] + + # Gedmo IP Traceable Extension Listener + gedmo.listener.ip_traceable: + class: Gedmo\IpTraceable\IpTraceableListener + tags: + - { name: doctrine.event_listener, event: 'prePersist' } + - { name: doctrine.event_listener, event: 'onFlush' } + - { name: doctrine.event_listener, event: 'loadClassMetadata' } + calls: + # Uncomment the below call if using attributes, and comment the call for the annotation reader + # - [ setAnnotationReader, [ '@gedmo.mapping.driver.attribute' ] ] + # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 + - [ setAnnotationReader, [ '@annotation_reader' ] ] + + # Gedmo Loggable Extension Listener + gedmo.listener.loggable: + class: Gedmo\Loggable\LoggableListener + tags: + - { name: doctrine.event_listener, event: 'onFlush' } + - { name: doctrine.event_listener, event: 'loadClassMetadata' } + - { name: doctrine.event_listener, event: 'postPersist' } + calls: + # Uncomment the below call if using attributes, and comment the call for the annotation reader + # - [ setAnnotationReader, [ '@gedmo.mapping.driver.attribute' ] ] + # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 + - [ setAnnotationReader, [ '@annotation_reader' ] ] + + # Gedmo Sluggable Extension Listener + gedmo.listener.sluggable: + class: Gedmo\Sluggable\SluggableListener + tags: + - { name: doctrine.event_listener, event: 'onFlush' } + - { name: doctrine.event_listener, event: 'loadClassMetadata' } + - { name: doctrine.event_listener, event: 'prePersist' } + calls: + # Uncomment the below call if using attributes, and comment the call for the annotation reader + # - [ setAnnotationReader, [ '@gedmo.mapping.driver.attribute' ] ] + # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 + - [ setAnnotationReader, [ '@annotation_reader' ] ] + + # Gedmo Soft Deleteable Extension listener + gedmo.listener.soft_deleteable: + class: Gedmo\SoftDeleteable\SoftDeleteableListener + tags: + - { name: doctrine.event_listener, event: 'loadClassMetadata' } + - { name: doctrine.event_listener, event: 'onFlush' } + calls: + # Uncomment the below call if using attributes, and comment the call for the annotation reader + # - [ setAnnotationReader, [ '@gedmo.mapping.driver.attribute' ] ] + # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 + - [ setAnnotationReader, [ '@annotation_reader' ] ] + # The `clock` service was introduced in Symfony 6.2; if using an older Symfony version, you can either comment this call or provide your own PSR-20 Clock implementation + - [ setClock, [ '@clock' ] ] + + # Gedmo Sortable Extension listener + gedmo.listener.sortable: + class: Gedmo\Sortable\SortableListener + tags: + - { name: doctrine.event_listener, event: 'onFlush' } + - { name: doctrine.event_listener, event: 'loadClassMetadata' } + - { name: doctrine.event_listener, event: 'prePersist' } + - { name: doctrine.event_listener, event: 'postPersist' } + - { name: doctrine.event_listener, event: 'preUpdate' } + - { name: doctrine.event_listener, event: 'postRemove' } + - { name: doctrine.event_listener, event: 'postFlush' } + calls: + # Uncomment the below call if using attributes, and comment the call for the annotation reader + # - [ setAnnotationReader, [ '@gedmo.mapping.driver.attribute' ] ] + # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 + - [ setAnnotationReader, [ '@annotation_reader' ] ] + + # Gedmo Timestampable Extension Listener + gedmo.listener.timestampable: + class: Gedmo\Timestampable\TimestampableListener + tags: + - { name: doctrine.event_listener, event: 'prePersist' } + - { name: doctrine.event_listener, event: 'onFlush' } + - { name: doctrine.event_listener, event: 'loadClassMetadata' } + calls: + # Uncomment the below call if using attributes, and comment the call for the annotation reader + # - [ setAnnotationReader, [ '@gedmo.mapping.driver.attribute' ] ] + # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 + - [ setAnnotationReader, [ '@annotation_reader' ] ] + # The `clock` service was introduced in Symfony 6.2; if using an older Symfony version, you can either comment this call or provide your own PSR-20 Clock implementation + - [ setClock, [ '@clock' ] ] + + # Gedmo Translatable Extension Listener + gedmo.listener.translatable: + class: Gedmo\Translatable\TranslatableListener + tags: + - { name: doctrine.event_listener, event: 'postLoad' } + - { name: doctrine.event_listener, event: 'postPersist' } + - { name: doctrine.event_listener, event: 'preFlush' } + - { name: doctrine.event_listener, event: 'onFlush' } + - { name: doctrine.event_listener, event: 'loadClassMetadata' } + calls: + # Uncomment the below call if using attributes, and comment the call for the annotation reader + # - [ setAnnotationReader, [ '@gedmo.mapping.driver.attribute' ] ] + # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 + - [ setAnnotationReader, [ '@annotation_reader' ] ] + # The Kernel's `locale` parameter is used to configure the default locale for the extension + - [ setDefaultLocale, [ '%locale%' ] ] + + # Gedmo Tree Extension Listener + gedmo.listener.tree: + class: Gedmo\Tree\TreeListener + tags: + - { name: doctrine.event_listener, event: 'prePersist'} + - { name: doctrine.event_listener, event: 'preUpdate'} + - { name: doctrine.event_listener, event: 'preRemove'} + - { name: doctrine.event_listener, event: 'onFlush'} + - { name: doctrine.event_listener, event: 'loadClassMetadata'} + - { name: doctrine.event_listener, event: 'postPersist'} + - { name: doctrine.event_listener, event: 'postUpdate'} + - { name: doctrine.event_listener, event: 'postRemove'} + calls: + # Uncomment the below call if using attributes, and comment the call for the annotation reader + # - [ setAnnotationReader, [ '@gedmo.mapping.driver.attribute' ] ] + # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 + - [ setAnnotationReader, [ '@annotation_reader' ] ] +``` + +### Extensions Compatible with MongoDB ODM Only + +> [!NOTE] +> This example shows the configuration when using the MongoDB ODM and `DoctrineMongoDBBundle` with a single default document manager. When using an application with multiple managers, a separate tag is needed with the `connection` attribute for each connection. + +```yaml +services: + # Gedmo Reference Integrity Extension Listener + gedmo.listener.reference_integrity: + class: Gedmo\ReferenceIntegrity\ReferenceIntegrityListener + tags: + - { name: doctrine_mongodb.odm.event_listener, event: 'loadClassMetadata' } + - { name: doctrine_mongodb.odm.event_listener, event: 'preRemove' } + calls: + # Uncomment the below call if using attributes, and comment the call for the annotation reader + # - [ setAnnotationReader, [ '@gedmo.mapping.driver.attribute' ] ] + # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 + - [ setAnnotationReader, [ '@annotation_reader' ] ] + + # Gedmo References Extension Listener + gedmo.listener.references: + class: Gedmo\References\ReferencesListener + tags: + - { name: doctrine_mongodb.odm.event_listener, event: 'postLoad' } + - { name: doctrine_mongodb.odm.event_listener, event: 'loadClassMetadata' } + - { name: doctrine_mongodb.odm.event_listener, event: 'prePersist' } + - { name: doctrine_mongodb.odm.event_listener, event: 'preUpdate' } + calls: + # Uncomment the below call if using attributes, and comment the call for the annotation reader + # - [ setAnnotationReader, [ '@gedmo.mapping.driver.attribute' ] ] + # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 + - [ setAnnotationReader, [ '@annotation_reader' ] ] +``` + +### Extensions Compatible with ORM Only + +> [!NOTE] +> This example shows the configuration when using the ORM and `DoctrineBundle` with a single default entity manager. When using an application with multiple managers, a separate tag is needed with the `connection` attribute for each connection. + +```yaml +services: + # Gedmo Uploadable Extension Listener + gedmo.listener.uploadable: + class: Gedmo\Uploadable\UploadableListener + tags: + - { name: doctrine.event_listener, event: 'loadClassMetadata'} + - { name: doctrine.event_listener, event: 'preFlush'} + - { name: doctrine.event_listener, event: 'onFlush'} + - { name: doctrine.event_listener, event: 'postFlush'} + calls: + # Uncomment the below call if using attributes, and comment the call for the annotation reader + # - [ setAnnotationReader, [ '@gedmo.mapping.driver.attribute' ] ] + # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 + - [ setAnnotationReader, [ '@annotation_reader' ] ] +``` + +## Registering Mapping Configuration + +When using the [Loggable](../loggable.md), [Translatable](../translatable.md), or [Tree](../tree.md) extensions, you will +need to register the mappings for these extensions to your object managers. + +> [!NOTE] +> These extensions only provide mappings through annotations or attributes, with support for annotations being deprecated. If using annotations, you will need to ensure the [`doctrine/annotations`](https://www.doctrine-project.org/projects/annotations.html) library is installed and configured. + +### MongoDB ODM Mapping + +> [!IMPORTANT] +> The tree extension does NOT have any objects to map when using the MongoDB ODM. + +The below example shows a configuration adding all available mappings to the default document manager. + +```yaml +doctrine_mongodb: + document_managers: + default: + mappings: + loggable: + type: attribute # or annotation + alias: GedmoLoggable + prefix: Gedmo\Loggable\Document + dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Loggable/Document" + is_bundle: false + translatable: + type: attribute # or annotation + alias: GedmoTranslatable + prefix: Gedmo\Translatable\Document + dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Translatable/Document" + is_bundle: false +``` + +To verify your configuration, you can use the `doctrine:mongodb:mapping:info` command to make sure the entities are registered. + +```shell +$ bin/console doctrine:mongodb:mapping:info + Found X documents mapped in document manager default: + [OK] Gedmo\Loggable\Document\LogEntry + [OK] Gedmo\Loggable\Document\MappedSuperclass\AbstractLogEntry + [OK] Gedmo\Translatable\Document\MappedSuperclass\AbstractPersonalTranslation + [OK] Gedmo\Translatable\Document\MappedSuperclass\AbstractTranslation + [OK] Gedmo\Translatable\Document\Translation +``` + +### ORM Mapping + +The below example shows a configuration adding all available mappings to the default entity manager. + +```yaml +doctrine: + orm: + default: + mappings: + loggable: + type: attribute # or annotation + alias: GedmoLoggable + prefix: Gedmo\Loggable\Entity + dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Loggable/Entity" + is_bundle: false + translatable: + type: attribute # or annotation + alias: GedmoTranslatable + prefix: Gedmo\Translatable\Entity + dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Translatable/Entity" + is_bundle: false + tree: + type: attribute # or annotation + alias: GedmoTree + prefix: Gedmo\Tree\Entity + dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Tree/Entity" + is_bundle: false +``` + +To verify your configuration, you can use the `doctrine:mapping:info` command to make sure the entities are registered. + +```shell +$ bin/console doctrine:mapping:info + Found X mapped entities: + [OK] Gedmo\Loggable\Entity\LogEntry + [OK] Gedmo\Loggable\Entity\MappedSuperclass\AbstractLogEntry + [OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation + [OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractTranslation + [OK] Gedmo\Translatable\Entity\Translation + [OK] Gedmo\Tree\Entity\MappedSuperclass\AbstractClosure +``` + +## Registering Filters + +### Soft Deleteable Filter + +When using the [Soft Deleteable](../softdeleteable.md) extension, a filter is available which allows configuring whether +soft-deleted objects are included in query results. + +> [!NOTE] +> The default configuration in the Symfony bundles does not enable the filters. These examples show how to globally enable them. + +#### MongoDB ODM Filter Configuration + +The below example shows a configuration adding the filter to the default document manager. + +```yaml +doctrine_mongodb: + document_managers: + default: + filters: + 'soft-deleteable': + class: Gedmo\SoftDeleteable\Filter\ODM\SoftDeleteableFilter + enabled: true +``` + +#### ORM Filter Configuration + +The below example shows a configuration adding the filter to the default entity manager. + +```yaml +doctrine: + orm: + default: + filters: + 'soft-deleteable': + class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter + enabled: true +``` + +## Configuring Extensions via Event Subscribers + +When using the [Blameable](../blameable.md), [IP Traceable](../ip_traceable.md), [Loggable](../loggable.md), or +[Translatable](../translatable.md) extensions, to work correctly, they require extra information that must be set +at runtime, typically during the `kernel.request` event. The below example is an event subscriber class which configures +all of these extensions. + +```php + [ + ['configureBlameableListener'], // Must run after the user is authenticated + ['configureIpTraceableListener', 512], // Runs early since this only requires the Request object + ['configureLoggableListener'], // Must run after the user is authenticated + ['configureTranslatableListener'], // Must run after the locale is configured + ], + ]; + } + + /** + * Configures the blameable listener using the currently authenticated user + */ + public function configureBlameableListener(RequestEvent $event): void + { + // Only applies to the main request + if (!$event->isMainRequest()) { + return; + } + + // If the required security component services weren't provided, there's nothing we can do + if (null === $this->authorizationChecker || null === $this->tokenStorage) { + return; + } + + $token = $this->tokenStorage->getToken(); + + // Only set the user information if there is a token in storage and it represents an authenticated user + if (null !== $token && $this->authorizationChecker->isGranted('IS_AUTHENTICATED')) { + $this->blameableListener->setUserValue($token->getUser()); + } + } + + /** + * Configures the IP traceable listener using the current request + */ + public function configureIpTraceableListener(RequestEvent $event): void + { + // Only applies to the main request + if (!$event->isMainRequest()) { + return; + } + + $ip = $event->getRequest()->getClientIp(); + + // Only set the IP address if available + if (null !== $ip) { + $this->ipTraceableListener->setIpValue($ip); + } + } + + /** + * Configures the loggable listener using the currently authenticated user + */ + public function configureLoggableListener(RequestEvent $event): void + { + // Only applies to the main request + if (!$event->isMainRequest()) { + return; + } + + // If the required security component services weren't provided, there's nothing we can do + if (null === $this->authorizationChecker || null === $this->tokenStorage) { + return; + } + + $token = $this->tokenStorage->getToken(); + + // Only set the user information if there is a token in storage and it represents an authenticated user + if (null !== $token && $this->authorizationChecker->isGranted('IS_AUTHENTICATED')) { + $this->loggableListener->setUsername($token->getUser()); + } + } + + /** + * Configures the translatable listener using the request locale + */ + public function configureTranslatableListener(RequestEvent $event): void + { + $this->translatableListener->setTranslatableLocale($event->getRequest()->getLocale()); + } +} +``` diff --git a/doc/laminas.md b/doc/laminas.md deleted file mode 100644 index 6f02884923..0000000000 --- a/doc/laminas.md +++ /dev/null @@ -1,85 +0,0 @@ -## Using Gedmo Doctrine Extensions in Laminas - -Assuming you are familiar with [DoctrineModule](https://github.com/doctrine/DoctrineModule) (if not, you should definitely start there!), integrating Doctrine Extensions with Laminas application is super-easy. - -### Composer - -Add `doctrine/doctrine-module`, `doctrine/doctrine-orm-module` or `doctrine/doctrine-mongo-odm-module` to composer.json file - -Then run `composer.phar update`. - -### Configuration - -Once libraries are installed, you can tell Doctrine which behaviors you want to use, by declaring appropriate subscribers in Event Manager settings. Together with [entity mapping options](https://github.com/doctrine/DoctrineORMModule#entities-settings), your module configuration file should look like following: - -```php -return array( - 'doctrine' => array( - 'eventmanager' => array( - 'orm_default' => array( - 'subscribers' => array( - - // pick any listeners you need - 'Gedmo\Tree\TreeListener', - 'Gedmo\Timestampable\TimestampableListener', - 'Gedmo\Sluggable\SluggableListener', - 'Gedmo\Loggable\LoggableListener', - 'Gedmo\Sortable\SortableListener' - ), - ), - ), - 'driver' => array( - 'my_driver' => array( - 'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver', - 'cache' => 'array', - 'paths' => array(__DIR__ . '/../src/MyModule/Entity') - ), - 'orm_default' => array( - 'drivers' => array( - 'MyModule\Entity' => 'my_driver' - ), - ), - ), - ), -); -``` - -That's it! From now on you can use Gedmo annotations, just as it is described in [documentation](./annotations.md). - -#### Note: You may need to provide additional settings for some of the available listeners. - -For instance, `Translatable` requires additional metadata driver in order to manage translation tables: - -```php -return array( - 'doctrine' => array( - 'eventmanager' => array( - 'orm_default' => array( - 'subscribers' => array( - 'Gedmo\Translatable\TranslatableListener', - ), - ), - ), - 'driver' => array( - 'my_driver' => array( - 'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver', - 'cache' => 'array', - 'paths' => array(__DIR__ . '/../src/MyModule/Entity') - ), - 'translatable_metadata_driver' => array( - 'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver', - 'cache' => 'array', - 'paths' => array( - 'vendor/gedmo/doctrine-extensions/src/Translatable/Entity', - ), - ), - 'orm_default' => array( - 'drivers' => array( - 'MyModule\Entity' => 'my_driver', - 'Gedmo\Translatable\Entity' => 'translatable_metadata_driver', - ), - ), - ), - ), -); -``` diff --git a/doc/symfony.md b/doc/symfony.md deleted file mode 100644 index 40acf432fb..0000000000 --- a/doc/symfony.md +++ /dev/null @@ -1,562 +0,0 @@ -# Install Gedmo Doctrine extensions in Symfony - -Configure full featured [Doctrine extensions](https://github.com/doctrine-extensions/DoctrineExtensions) for your Symfony project. -This post will show you - how to create a simple configuration file to manage extensions with -ability to use all features it provides. -Interested? then bear with me! and don't be afraid, we're not diving into security component :) - -This post will put some light over the shed of extension installation and mapping configuration -of Doctrine. It does not require any additional dependencies and gives you full power -over management of extensions. - -Content: - -- [Symfony](#sf-app) application -- Extensions metadata [mapping](#ext-mapping) -- Extensions filters [filtering](#ext-filtering) -- Extension [listeners](#ext-listeners) -- Usage [example](#ext-example) -- Some [tips](#more-tips) -- [Alternative](#alternative) over configuration - - - -## Symfony application - -First of all, we will need a symfony startup application, let's say [symfony-standard edition -with composer](https://symfony.com/doc/current/best_practices/creating-the-project.html) - -- `composer create-project symfony/skeleton [project name]` - -Now let's add the **gedmo/doctrine-extensions** - -You can find the doctrine-extensions project on packagist: https://packagist.org/packages/gedmo/doctrine-extensions - -To add it to your project: -- `composer require gedmo/doctrine-extensions` - - - -## Mapping - -Let's start from the mapping. In case you use the **translatable**, **tree** or **loggable** -extension you will need to map those abstract mapped superclasses for your ORM to be aware of. -To do so, add some mapping info to your **doctrine.orm** configuration, edit **config/doctrine.yaml**: - -```yaml -doctrine: - dbal: - # your dbal config here - - orm: - auto_generate_proxy_classes: '%kernel.debug%' - auto_mapping: true - # only these lines are added additionally - mappings: - translatable: - type: attribute # or annotation or xml - alias: Gedmo - prefix: Gedmo\Translatable\Entity - # make sure vendor library location is correct - dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Translatable/Entity" -``` - -After that, running **php bin/console doctrine:mapping:info** you should see the output: - -``` -Found 3 entities mapped in entity manager default: -[OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation -[OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractTranslation -[OK] Gedmo\Translatable\Entity\Translation -``` -Well, we mapped only **translatable** for now, it really depends on your needs, which extensions -your application uses. - -**Note:** there is **Gedmo\Translatable\Entity\Translation** which is not a super class, in that case -if you create a doctrine schema, it will add **ext_translations** table, which might not be useful -to you also. To skip mapping of these entities, you can map **only superclasses** - -```yaml -mappings: - translatable: - type: attribute # or annotation or xml - alias: Gedmo - prefix: Gedmo\Translatable\Entity - # make sure vendor library location is correct - dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Translatable/Entity/MappedSuperclass" -``` - -The configuration above, adds a **/MappedSuperclass** into directory depth, after running -**php bin/console doctrine:mapping:info** you should only see now: - -``` -Found 2 entities mapped in entity manager default: -[OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation -[OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractTranslation -``` - -This is very useful for advanced requirements and quite simple to understand. So now let's map -everything the extensions provide: - -```yaml -# only orm config branch of doctrine -orm: - auto_generate_proxy_classes: '%kernel.debug%' - auto_mapping: true - # only these lines are added additionally - mappings: - translatable: - type: attribute # or annotation or xml - alias: Gedmo - prefix: Gedmo\Translatable\Entity - # make sure vendor library location is correct - dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Translatable/Entity" - loggable: - type: attribute # or annotation or xml - alias: Gedmo - prefix: Gedmo\Loggable\Entity - dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Loggable/Entity" - tree: - type: attribute # or annotation or xml - alias: Gedmo - prefix: Gedmo\Tree\Entity - dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Tree/Entity" -``` - -## Filters - -The **softdeleteable** ORM filter also needs to be configured, so that soft deleted records are filtered when querying. -To do so, add this filter info to your **doctrine.orm** configuration, edit **config/doctrine.yaml**: -```yaml -doctrine: - dbal: - # your dbal config here - orm: - auto_generate_proxy_classes: '%kernel.debug%' - auto_mapping: true - # only these lines are added additionally - filters: - softdeleteable: - class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter -``` - - -## Doctrine extension listener services - -Next, the heart of extensions are behavioral listeners which pours all the sugar. We will -create a **yaml** service file in our config directory. The setup can be different, your config could be located -in the bundle, it depends on your preferences. Edit **config/packages/doctrine_extensions.yaml** - -```yaml -# services to handle doctrine extensions -# import it in config/packages/doctrine_extensions.yaml -services: - # Attribute mapping driver for the Doctrine Extension listeners - gedmo.mapping.driver.attribute: - class: Gedmo\Mapping\Driver\AttributeReader - - # Doctrine Extension listeners to handle behaviors - gedmo.listener.tree: - class: Gedmo\Tree\TreeListener - tags: - - { name: doctrine.event_listener, event: 'prePersist'} - - { name: doctrine.event_listener, event: 'preUpdate'} - - { name: doctrine.event_listener, event: 'preRemove'} - - { name: doctrine.event_listener, event: 'onFlush'} - - { name: doctrine.event_listener, event: 'loadClassMetadata'} - - { name: doctrine.event_listener, event: 'postPersist'} - - { name: doctrine.event_listener, event: 'postUpdate'} - - { name: doctrine.event_listener, event: 'postRemove'} - calls: - # Uncomment the below call if using attributes, and comment the call for the annotation reader - # - [ setAnnotationReader, [ "@gedmo.mapping.driver.attribute" ] ] - # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 - - [ setAnnotationReader, [ "@annotation_reader" ] ] - - Gedmo\Translatable\TranslatableListener: - tags: - - { name: doctrine.event_listener, event: 'postLoad' } - - { name: doctrine.event_listener, event: 'postPersist' } - - { name: doctrine.event_listener, event: 'preFlush' } - - { name: doctrine.event_listener, event: 'onFlush' } - - { name: doctrine.event_listener, event: 'loadClassMetadata' } - calls: - # Uncomment the below call if using attributes, and comment the call for the annotation reader - # - [ setAnnotationReader, [ "@gedmo.mapping.driver.attribute" ] ] - # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 - - [ setAnnotationReader, [ "@annotation_reader" ] ] - - - [ setDefaultLocale, [ "%locale%" ] ] - - [ setTranslationFallback, [ false ] ] - - gedmo.listener.timestampable: - class: Gedmo\Timestampable\TimestampableListener - tags: - - { name: doctrine.event_listener, event: 'prePersist' } - - { name: doctrine.event_listener, event: 'onFlush' } - - { name: doctrine.event_listener, event: 'loadClassMetadata' } - calls: - # Uncomment the below call if using attributes, and comment the call for the annotation reader - # - [ setAnnotationReader, [ "@gedmo.mapping.driver.attribute" ] ] - # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 - - [ setAnnotationReader, [ "@annotation_reader" ] ] - - gedmo.listener.sluggable: - class: Gedmo\Sluggable\SluggableListener - tags: - - { name: doctrine.event_listener, event: 'prePersist' } - - { name: doctrine.event_listener, event: 'onFlush' } - - { name: doctrine.event_listener, event: 'loadClassMetadata' } - calls: - # Uncomment the below call if using attributes, and comment the call for the annotation reader - # - [ setAnnotationReader, [ "@gedmo.mapping.driver.attribute" ] ] - # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 - - [ setAnnotationReader, [ "@annotation_reader" ] ] - - gedmo.listener.sortable: - class: Gedmo\Sortable\SortableListener - tags: - - { name: doctrine.event_listener, event: 'onFlush' } - - { name: doctrine.event_listener, event: 'loadClassMetadata' } - - { name: doctrine.event_listener, event: 'prePersist' } - - { name: doctrine.event_listener, event: 'postPersist' } - - { name: doctrine.event_listener, event: 'preUpdate' } - - { name: doctrine.event_listener, event: 'postRemove' } - - { name: doctrine.event_listener, event: 'postFlush' } - calls: - # Uncomment the below call if using attributes, and comment the call for the annotation reader - # - [ setAnnotationReader, [ "@gedmo.mapping.driver.attribute" ] ] - # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 - - [ setAnnotationReader, [ "@annotation_reader" ] ] - - gedmo.listener.softdeleteable: - class: Gedmo\SoftDeleteable\SoftDeleteableListener - tags: - - { name: doctrine.event_listener, event: 'onFlush' } - - { name: doctrine.event_listener, event: 'loadClassMetadata' } - calls: - # Uncomment the below call if using attributes, and comment the call for the annotation reader - # - [ setAnnotationReader, [ "@gedmo.mapping.driver.attribute" ] ] - # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 - - [ setAnnotationReader, [ "@annotation_reader" ] ] - - Gedmo\Loggable\LoggableListener: - tags: - - { name: doctrine.event_listener, event: 'onFlush' } - - { name: doctrine.event_listener, event: 'loadClassMetadata' } - - { name: doctrine.event_listener, event: 'postPersist' } - calls: - # Uncomment the below call if using attributes, and comment the call for the annotation reader - # - [ setAnnotationReader, [ "@gedmo.mapping.driver.attribute" ] ] - # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 - - [ setAnnotationReader, [ "@annotation_reader" ] ] - - Gedmo\Blameable\BlameableListener: - tags: - - { name: doctrine.event_listener, event: 'prePersist' } - - { name: doctrine.event_listener, event: 'onFlush' } - - { name: doctrine.event_listener, event: 'loadClassMetadata' } - calls: - # Uncomment the below call if using attributes, and comment the call for the annotation reader - # - [ setAnnotationReader, [ "@gedmo.mapping.driver.attribute" ] ] - # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 - - [ setAnnotationReader, [ "@annotation_reader" ] ] - - Gedmo\IpTraceable\IpTraceableListener: - tags: - - { name: doctrine.event_listener, event: 'prePersist' } - - { name: doctrine.event_listener, event: 'onFlush' } - - { name: doctrine.event_listener, event: 'loadClassMetadata' } - calls: - # Uncomment the below call if using attributes, and comment the call for the annotation reader - # - [ setAnnotationReader, [ "@gedmo.mapping.driver.attribute" ] ] - # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 - - [ setAnnotationReader, [ "@annotation_reader" ] ] - -``` - -So what does it include in general? Well, it creates services for all extension listeners. -You can remove some which you do not use, or change them as you need. **Translatable** for instance, -sets the default locale to the value of your `%locale%` parameter, you can configure it differently. - -**Note:** In case you noticed, there is **EventSubscriber\DoctrineExtensionSubscriber**. -You will need to create this subscriber class if you use **loggable** , **translatable** or **blameable** -behaviors. This listener will set the **locale used** from request and **username** to -loggable and blameable. So, to finish the setup create **EventSubscriber\DoctrineExtensionSubscriber** - -## Register event listener for [Symfony Doctrine MongoDB Bundle](https://github.com/doctrine/DoctrineMongoDBBundle) - -You also need to manually tag the listeners. Otherwise, the listeners will not be listening to the triggered events -of Doctrine. - -```yaml -services: - Gedmo\Loggable\LoggableListener: - tags: - - { name: doctrine_mongodb.odm.event_listener, event: 'onFlush' } - - { name: doctrine_mongodb.odm.event_listener, event: 'loadClassMetadata' } - - { name: doctrine_mongodb.odm.event_listener, event: 'postPersist' } - calls: - # Uncomment the below call if using attributes, and comment the call for the annotation reader - # - [ setAnnotationReader, [ "@gedmo.mapping.driver.attribute" ] ] - # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0 - - [ setAnnotationReader, [ "@annotation_reader" ] ] -``` - -```php -blameableListener = $blameableListener; - $this->tokenStorage = $tokenStorage; - $this->translatableListener = $translatableListener; - $this->loggableListener = $loggableListener; - } - - - public static function getSubscribedEvents(): array - { - return [ - KernelEvents::REQUEST => 'onKernelRequest', - KernelEvents::FINISH_REQUEST => 'onLateKernelRequest' - ]; - } - public function onKernelRequest(): void - { - if ( - $this->tokenStorage->getToken() !== null && - $this->tokenStorage->getToken()->getUser() !== null - ) { - $this->blameableListener->setUserValue($this->tokenStorage->getToken()->getUser()); - } - } - - public function onLateKernelRequest(FinishRequestEvent $event): void - { - $this->translatableListener->setTranslatableLocale($event->getRequest()->getLocale()); - } -} -``` - - - -## Example - -After that, you have your extensions set up and ready to be used! Too easy right? Well, -if you do not believe me, let's create a simple entity in our project: - -```php - -id; - } - - public function setTitle(?string $title): void - { - $this->title = $title; - } - - public function getTitle(): ?string - { - return $this->title; - } - - public function getCreated(): ?DateTimeImmutable - { - return $this->created; - } - - public function getUpdated(): ?DateTimeImmutable - { - return $this->updated; - } - - public function getDeletedAt(): ?DateTimeImmutable - { - return $this->deletedAt; - } - - public function setDeletedAt(?DateTimeImmutable $deletedAt): void - { - $this->deletedAt = $deletedAt; - } -} -``` - -Now, let's have some fun: - -- if you have not created the database yet, run `php bin/console doctrine:database:create` -- create the schema `php bin/console doctrine:schema:create` - -Everything will work just fine, you can modify the **App\Controller\DemoController** -and add an action to test how it works: - -```php -// file: src/Controller/DemoController.php -// include this code portion - -/** - * @Route("/posts", name="_demo_posts") - */ -public function postsAction(EntityManagerInterface $em): Response -{ - $repository = $em->getRepository(App\Entity\BlogPost::class); - // create some posts in case if there aren't any - if (!$repository->find('hello_world')) { - $post = new App\Entity\BlogPost(); - $post->setTitle('Hello world'); - - $next = new App\Entity\BlogPost(); - $next->setTitle('Doctrine extensions'); - - $em->persist($post); - $em->persist($next); - $em->flush(); - } - $posts = $repository->findAll(); - dd($posts); -} -``` - -Now if you follow the url: **http://your_virtual_host/demo/posts** you -should see a print of posts, this is only an extension demo, we will not create a template. - - - -## More tips - -Regarding, the setup, I do not think it's too complicated to use, in general it is simple -enough, and lets you understand at least small parts on how you can hook mappings into doctrine, and -how easily extension services are added. This configuration does not hide anything behind -curtains and allows you to modify the configuration as you require. - -### Multiple entity managers - -If you use more than one entity manager, you can simply tag the subscriber -with other the manager name: - - -Regarding, mapping of ODM mongodb, it's basically the same: - -```yaml -doctrine_mongodb: - default_database: 'my_database' - default_connection: 'default' - default_document_manager: 'default' - connections: - default: ~ - document_managers: - default: - connection: 'default' - auto_mapping: true - mappings: - translatable: - type: attribute # or annotation or xml - alias: GedmoDocument - prefix: Gedmo\Translatable\Document - # make sure vendor library location is correct - dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Translatable/Document" -``` - -This also shows, how to make mappings based on single manager. All what differs is that **Document** -instead of **Entity** is used. I haven't tested it with mongo though. - -**Note:** [extension repository](https://github.com/doctrine-extensions/DoctrineExtensions) contains all -[documentation](../doc) you may need -to understand how you can use it in your projects. - - - -## Alternative over configuration - -You can use [StofDoctrineExtensionsBundle](https://github.com/stof/StofDoctrineExtensionsBundle) which is a wrapper of these extensions - -## Troubleshooting - -- Make sure there are no *.orm.yml or *.orm.xml files for your Entities in your bundles Resources/config/doctrine directory. With those files in place the annotations won't be taken into account.