Skip to content

Commit 96a37a0

Browse files
committed
Implemented proof of concept to convert between database types
1 parent 2157916 commit 96a37a0

File tree

3 files changed

+166
-50
lines changed

3 files changed

+166
-50
lines changed

config/packages/doctrine.yaml

Lines changed: 43 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,56 @@
11
doctrine:
22
dbal:
3-
url: '%env(resolve:DATABASE_URL)%'
4-
5-
# Required for DAMA doctrine test bundle
6-
use_savepoints: true
7-
8-
# IMPORTANT: You MUST configure your server version,
9-
# either here or in the DATABASE_URL env var (see .env file)
10-
3+
# 1. GLOBAL SETTINGS (Apply to all connections)
114
types:
12-
# UTC datetimes
13-
datetime:
14-
class: App\Doctrine\Types\UTCDateTimeType
15-
date:
16-
class: App\Doctrine\Types\UTCDateTimeType
5+
datetime: App\Doctrine\Types\UTCDateTimeType
6+
date: App\Doctrine\Types\UTCDateTimeType
7+
datetime_immutable: App\Doctrine\Types\UTCDateTimeImmutableType
8+
date_immutable: App\Doctrine\Types\UTCDateTimeImmutableType
9+
big_decimal: App\Doctrine\Types\BigDecimalType
10+
tinyint: App\Doctrine\Types\TinyIntType
1711

18-
datetime_immutable:
19-
class: App\Doctrine\Types\UTCDateTimeImmutableType
20-
date_immutable:
21-
class: App\Doctrine\Types\UTCDateTimeImmutableType
12+
connections:
13+
default:
14+
use_savepoints: true
15+
schema_filter: ~^(?!internal)~
16+
url: '%env(resolve:DATABASE_URL)%'
2217

23-
big_decimal:
24-
class: App\Doctrine\Types\BigDecimalType
25-
tinyint:
26-
class: App\Doctrine\Types\TinyIntType
27-
28-
schema_filter: ~^(?!internal)~
29-
# Only enable this when needed
30-
profiling_collect_backtrace: false
18+
migration_source:
19+
use_savepoints: true
20+
schema_filter: ~^(?!internal)~
21+
url: '%env(resolve:DB_MIGRATION_SOURCE)%'
3122

3223
orm:
3324
auto_generate_proxy_classes: true
3425
enable_lazy_ghost_objects: true
35-
report_fields_where_declared: true
36-
validate_xml_mapping: true
37-
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
38-
identity_generation_preferences:
39-
Doctrine\DBAL\Platforms\PostgreSQLPlatform: identity
40-
auto_mapping: true
41-
controller_resolver:
42-
auto_mapping: true
43-
mappings:
44-
App:
45-
type: attribute
46-
is_bundle: false
47-
dir: '%kernel.project_dir%/src/Entity'
48-
prefix: 'App\Entity'
49-
alias: App
5026

51-
dql:
52-
string_functions:
53-
regexp: App\Doctrine\Functions\Regexp
54-
field: DoctrineExtensions\Query\Mysql\Field
55-
field2: App\Doctrine\Functions\Field2
56-
natsort: App\Doctrine\Functions\Natsort
57-
array_position: App\Doctrine\Functions\ArrayPosition
58-
ilike: App\Doctrine\Functions\ILike
27+
entity_managers:
28+
default: &common_orm_settings
29+
report_fields_where_declared: true
30+
validate_xml_mapping: true
31+
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
32+
identity_generation_preferences:
33+
Doctrine\DBAL\Platforms\PostgreSQLPlatform: identity
34+
mappings:
35+
App:
36+
type: attribute
37+
is_bundle: false
38+
dir: '%kernel.project_dir%/src/Entity'
39+
prefix: 'App\Entity'
40+
alias: App
41+
dql:
42+
string_functions:
43+
regexp: App\Doctrine\Functions\Regexp
44+
field: DoctrineExtensions\Query\Mysql\Field
45+
field2: App\Doctrine\Functions\Field2
46+
natsort: App\Doctrine\Functions\Natsort
47+
array_position: App\Doctrine\Functions\ArrayPosition
48+
ilike: App\Doctrine\Functions\ILike
49+
connection: default
50+
51+
migration_source:
52+
<<: *common_orm_settings
53+
connection: migration_source
5954

6055
when@test:
6156
doctrine:
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
/*
3+
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
4+
*
5+
* Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
6+
*
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU Affero General Public License as published
9+
* by the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Affero General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Affero General Public License
18+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
declare(strict_types=1);
22+
23+
24+
namespace App\Command\Migrations;
25+
26+
use App\DataTables\Helpers\ColumnSortHelper;
27+
use App\Entity\Parts\Manufacturer;
28+
use App\Services\ImportExportSystem\PartKeeprImporter\PKImportHelper;
29+
use Doctrine\ORM\EntityManagerInterface;
30+
31+
use Doctrine\ORM\Id\AssignedGenerator;
32+
use Doctrine\ORM\Mapping\ClassMetadata;
33+
use Doctrine\Persistence\ManagerRegistry;
34+
use Doctrine\Persistence\ObjectManager;
35+
use Symfony\Component\Console\Attribute\AsCommand;
36+
use Symfony\Component\Console\Command\Command;
37+
use Symfony\Component\Console\Input\InputInterface;
38+
use Symfony\Component\Console\Output\OutputInterface;
39+
40+
#[AsCommand('partdb:migrate-db', 'Migrate the database to a different platform')]
41+
class DBMigrationCommand extends Command
42+
{
43+
private readonly EntityManagerInterface $sourceEM;
44+
private readonly EntityManagerInterface $targetEM;
45+
46+
public function __construct(private readonly ManagerRegistry $managerRegistry,
47+
private readonly PKImportHelper $importHelper,
48+
)
49+
{
50+
$this->sourceEM = $this->managerRegistry->getManager('migration_source');
51+
$this->targetEM = $this->managerRegistry->getManager('default');
52+
53+
parent::__construct();
54+
}
55+
56+
public function execute(InputInterface $input, OutputInterface $output): int
57+
{
58+
// Example migration logic (to be replaced with actual migration code)
59+
$output->writeln('Starting database migration...');
60+
61+
//Disable all event listeners on target EM to avoid unwanted side effects
62+
$eventManager = $this->targetEM->getEventManager();
63+
foreach ($eventManager->getAllListeners() as $event => $listeners) {
64+
foreach ($listeners as $listener) {
65+
$eventManager->removeEventListener($event, $listener);
66+
}
67+
}
68+
69+
$output->writeln('Clear target database...');
70+
$this->importHelper->purgeDatabaseForImport($this->targetEM, ['internal', 'migration_versions']);
71+
72+
$metadata = $this->targetEM->getMetadataFactory()->getAllMetadata();
73+
74+
//First we modify each entity metadata to have an persist cascade on all relations
75+
foreach ($metadata as $metadatum) {
76+
$entityClass = $metadatum->getName();
77+
$output->writeln('Modifying cascade and ID settings for entity: ' . $entityClass);
78+
79+
foreach ($metadatum->getAssociationNames() as $fieldName) {
80+
$mapping = $metadatum->getAssociationMapping($fieldName);
81+
$mapping->cascade = array_unique(array_merge($mapping->cascade, ['persist']));
82+
83+
$metadatum->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);
84+
$metadatum->setIdGenerator(new AssignedGenerator());
85+
}
86+
}
87+
88+
89+
//Afterwards we migrate all entities
90+
foreach ($metadata as $metadatum) {
91+
//skip all superclasses
92+
if ($metadatum->isMappedSuperclass) {
93+
continue;
94+
}
95+
96+
$entityClass = $metadatum->getName();
97+
98+
$output->writeln('Migrating entity: ' . $entityClass);
99+
100+
$repo = $this->sourceEM->getRepository($entityClass);
101+
$items = $repo->findAll();
102+
foreach ($items as $item) {
103+
$this->targetEM->persist($item);
104+
}
105+
$this->targetEM->flush();
106+
}
107+
108+
//Migrate all manufacturers from source to target
109+
/*$manufacturerRepo = $this->sourceEM->getRepository(Manufacturer::class);
110+
$manufacturers = $manufacturerRepo->findAll();
111+
foreach ($manufacturers as $manufacturer) {
112+
$this->targetEM->persist($manufacturer);
113+
}
114+
$this->targetEM->flush();
115+
*/
116+
117+
$output->writeln('Database migration completed successfully.');
118+
119+
return Command::SUCCESS;
120+
}
121+
}

src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelper.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ public function __construct(protected EntityManagerInterface $em)
3939
* Existing users and groups are not purged.
4040
* This is needed to avoid ID collisions.
4141
*/
42-
public function purgeDatabaseForImport(): void
42+
public function purgeDatabaseForImport(?EntityManagerInterface $entityManager = null, array $excluded_tables = ['users', 'groups', 'u2f_keys', 'internal', 'migration_versions']): void
4343
{
4444
//We use the ResetAutoIncrementORMPurger to reset the auto increment values of the tables. Also it normalizes table names before checking for exclusion.
45-
$purger = new ResetAutoIncrementORMPurger($this->em, ['users', 'groups', 'u2f_keys', 'internal', 'migration_versions']);
45+
$purger = new ResetAutoIncrementORMPurger($entityManager ?? $this->em, $excluded_tables);
4646
$purger->purge();
4747
}
4848

0 commit comments

Comments
 (0)