diff --git a/README.md b/README.md index bbcdabcb..de2839b4 100755 --- a/README.md +++ b/README.md @@ -110,6 +110,12 @@ simple_things_entity_audit: entity_manager: custom ``` +If you need to explicitly discard the foreign keys inferred from the audited entities, you can use the `disable_foreign_keys` parameter: +```yaml +simple_things_entity_audit: + disable_foreign_keys: true +``` + ### Creating new tables Call the command below to see the new tables in the update schema queue. diff --git a/src/AuditConfiguration.php b/src/AuditConfiguration.php index 7f24530f..9a4f8c68 100644 --- a/src/AuditConfiguration.php +++ b/src/AuditConfiguration.php @@ -26,6 +26,8 @@ class AuditConfiguration */ private array $auditedEntityClasses = []; + private bool $disableForeignKeys = false; + /** * @var string[] */ @@ -79,6 +81,16 @@ public function getTableName(ClassMetadataInfo $metadata) return $this->getTablePrefix().$tableName.$this->getTableSuffix(); } + public function areForeignKeysDisabled(): bool + { + return $this->disableForeignKeys; + } + + public function setDisabledForeignKeys(bool $disabled): void + { + $this->disableForeignKeys = $disabled; + } + /** * @return string */ diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 472b4629..55225557 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -54,6 +54,7 @@ public function getConfigTreeBuilder() ->scalarNode('revision_field_name')->defaultValue('rev')->end() ->scalarNode('revision_type_field_name')->defaultValue('revtype')->end() ->scalarNode('revision_table_name')->defaultValue('revisions')->end() + ->scalarNode('disable_foreign_keys')->defaultValue(false)->end() ->scalarNode('revision_id_field_type') ->defaultValue(Types::INTEGER) // NEXT_MAJOR: Use enumNode() instead. diff --git a/src/DependencyInjection/SimpleThingsEntityAuditExtension.php b/src/DependencyInjection/SimpleThingsEntityAuditExtension.php index ce4513e0..9fecb9ee 100644 --- a/src/DependencyInjection/SimpleThingsEntityAuditExtension.php +++ b/src/DependencyInjection/SimpleThingsEntityAuditExtension.php @@ -39,6 +39,7 @@ public function load(array $configs, ContainerBuilder $container): void 'revision_table_name', 'revision_id_field_type', 'global_ignore_columns', + 'disable_foreign_keys', ]; foreach ($configurables as $key) { diff --git a/src/EventListener/CreateSchemaListener.php b/src/EventListener/CreateSchemaListener.php index 97b027ca..fbe5ea0d 100644 --- a/src/EventListener/CreateSchemaListener.php +++ b/src/EventListener/CreateSchemaListener.php @@ -119,17 +119,9 @@ public function postGenerateSchemaTable(GenerateSchemaTableEventArgs $eventArgs) } } - $revisionForeignKeyName = $this->config->getRevisionFieldName().'_'.md5($revisionTable->getName()).'_fk'; - $primaryKey = $revisionsTable->getPrimaryKey(); - \assert(null !== $primaryKey); - - $revisionTable->addForeignKeyConstraint( - $revisionsTable, - [$this->config->getRevisionFieldName()], - $primaryKey->getColumns(), - [], - $revisionForeignKeyName - ); + if (!$this->config->areForeignKeysDisabled()) { + $this->createForeignKeys($revisionTable, $revisionsTable); + } } public function postGenerateSchema(GenerateSchemaEventArgs $eventArgs): void @@ -142,6 +134,21 @@ public function postGenerateSchema(GenerateSchemaEventArgs $eventArgs): void } } + private function createForeignKeys(Table $relatedTable, Table $revisionsTable): void + { + $revisionForeignKeyName = $this->config->getRevisionFieldName().'_'.md5($relatedTable->getName()).'_fk'; + $primaryKey = $revisionsTable->getPrimaryKey(); + \assert(null !== $primaryKey); + + $relatedTable->addForeignKeyConstraint( + $revisionsTable, + [$this->config->getRevisionFieldName()], + $primaryKey->getColumns(), + [], + $revisionForeignKeyName + ); + } + /** * Copies $column to another table. All its options are copied but notnull and autoincrement which are set to false. */ diff --git a/src/Resources/config/auditable.php b/src/Resources/config/auditable.php index 53b73758..00b8b92b 100644 --- a/src/Resources/config/auditable.php +++ b/src/Resources/config/auditable.php @@ -47,7 +47,9 @@ ->set('simplethings.entityaudit.revision_table_name', null) - ->set('simplethings.entityaudit.revision_id_field_type', null); + ->set('simplethings.entityaudit.revision_id_field_type', null) + + ->set('simplethings.entityaudit.disable_foreign_keys', null); $containerConfigurator->services() ->set('simplethings_entityaudit.manager', AuditManager::class) @@ -119,6 +121,7 @@ ->set('simplethings_entityaudit.config', AuditConfiguration::class) ->public() ->call('setAuditedEntityClasses', [param('simplethings.entityaudit.audited_entities')]) + ->call('setDisabledForeignKeys', [param('simplethings.entityaudit.disable_foreign_keys')]) ->call('setGlobalIgnoreColumns', [param('simplethings.entityaudit.global_ignore_columns')]) ->call('setTablePrefix', [param('simplethings.entityaudit.table_prefix')]) ->call('setTableSuffix', [param('simplethings.entityaudit.table_suffix')]) diff --git a/tests/DependencyInjection/SimpleThingsEntityAuditExtensionTest.php b/tests/DependencyInjection/SimpleThingsEntityAuditExtensionTest.php index 28699147..ece94d08 100644 --- a/tests/DependencyInjection/SimpleThingsEntityAuditExtensionTest.php +++ b/tests/DependencyInjection/SimpleThingsEntityAuditExtensionTest.php @@ -61,6 +61,7 @@ public function testItRegistersDefaultServices(): void $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('simplethings_entityaudit.config', 'setRevisionIdFieldType', ['%simplethings.entityaudit.revision_id_field_type%']); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('simplethings_entityaudit.config', 'setRevisionFieldName', ['%simplethings.entityaudit.revision_field_name%']); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('simplethings_entityaudit.config', 'setRevisionTypeFieldName', ['%simplethings.entityaudit.revision_type_field_name%']); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('simplethings_entityaudit.config', 'setDisabledForeignKeys', ['%simplethings.entityaudit.disable_foreign_keys%']); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall('simplethings_entityaudit.config', 'setUsernameCallable', ['simplethings_entityaudit.username_callable']); } @@ -102,6 +103,7 @@ public function testItSetsDefaultParameters(): void $this->assertContainerBuilderHasParameter('simplethings.entityaudit.revision_field_name', 'rev'); $this->assertContainerBuilderHasParameter('simplethings.entityaudit.revision_type_field_name', 'revtype'); $this->assertContainerBuilderHasParameter('simplethings.entityaudit.revision_id_field_type', Types::INTEGER); + $this->assertContainerBuilderHasParameter('simplethings.entityaudit.disable_foreign_keys', false); } public function testItSetsConfiguredParameters(): void @@ -117,6 +119,7 @@ public function testItSetsConfiguredParameters(): void 'revision_id_field_type' => Types::GUID, 'revision_field_name' => 'revision', 'revision_type_field_name' => 'action', + 'disable_foreign_keys' => false, ]); $this->assertContainerBuilderHasParameter('simplethings.entityaudit.connection', 'my_custom_connection'); @@ -129,6 +132,7 @@ public function testItSetsConfiguredParameters(): void $this->assertContainerBuilderHasParameter('simplethings.entityaudit.revision_id_field_type', Types::GUID); $this->assertContainerBuilderHasParameter('simplethings.entityaudit.revision_field_name', 'revision'); $this->assertContainerBuilderHasParameter('simplethings.entityaudit.revision_type_field_name', 'action'); + $this->assertContainerBuilderHasParameter('simplethings.entityaudit.disable_foreign_keys', false); foreach ([Events::onFlush, Events::postPersist, Events::postUpdate, Events::postFlush, Events::onClear] as $event) { $this->assertContainerBuilderHasServiceDefinitionWithTag('simplethings_entityaudit.log_revisions_listener', 'doctrine.event_listener', ['event' => $event, 'connection' => 'my_custom_connection']); diff --git a/tests/NoForeignKeysTest.php b/tests/NoForeignKeysTest.php new file mode 100644 index 00000000..44687b7d --- /dev/null +++ b/tests/NoForeignKeysTest.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sonata\EntityAuditBundle\Tests; + +use Doctrine\DBAL\Exception; +use Doctrine\ORM\Exception\ORMException; +use Doctrine\ORM\OptimisticLockException; +use SimpleThings\EntityAudit\AuditConfiguration; +use SimpleThings\EntityAudit\AuditManager; +use SimpleThings\EntityAudit\Exception\NotAuditedException; +use Sonata\EntityAuditBundle\Tests\Fixtures\Core\UserAudit; + +final class NoForeignKeysTest extends BaseTest +{ + protected $schemaEntities = [ + UserAudit::class, + ]; + + protected $auditedEntities = [ + UserAudit::class, + ]; + + /** + * @throws OptimisticLockException + * @throws ORMException + * @throws NotAuditedException + * @throws Exception + */ + public function testRevisionForeignKeys(): void + { + $em = $this->getEntityManager(); + + $user = new UserAudit('phansys'); + + $em->persist($user); + $em->flush(); + + $reader = $this->getAuditManager()->createAuditReader($em); + + $userId = $user->getId(); + static::assertNotNull($userId); + + $revisions = $reader->findRevisions(UserAudit::class, $userId); + + static::assertCount(1, $revisions); + + $revision = $reader->getCurrentRevision(UserAudit::class, $userId); + static::assertSame('1', (string) $revision); + + $revisionsTableName = $this->getAuditManager()->getConfiguration()->getRevisionTableName(); + + $em->getConnection()->delete($revisionsTableName, ['id' => $revision]); + } + + protected function getAuditManager(): AuditManager + { + if (null !== $this->auditManager) { + return $this->auditManager; + } + + $auditConfig = AuditConfiguration::forEntities($this->auditedEntities); + $auditConfig->setGlobalIgnoreColumns(['ignoreme']); + $auditConfig->setDisabledForeignKeys(true); + $auditConfig->setUsernameCallable(static fn (): string => 'beberlei'); + + $this->auditManager = new AuditManager($auditConfig, $this->getClock()); + $this->auditManager->registerEvents($this->getEntityManager()->getEventManager()); + + return $this->auditManager; + } +}