Skip to content

Commit ab346e8

Browse files
Merge pull request #7337 from christianbeeznest/rna-23159-2
Internal: Fix fast attendance migration dry-run and path format - refs BT#23159
2 parents 8ba864a + eccd949 commit ab346e8

1 file changed

Lines changed: 48 additions & 7 deletions

File tree

src/CoreBundle/Command/MigrateAttendancesFastCommand.php

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,31 @@ protected function configure(): void
5353
InputOption::VALUE_NONE,
5454
'Alias of --drop-c-item-property (drops legacy table c_item_property after successful migration, only if no pending attendances remain).'
5555
);
56+
57+
$this->addOption(
58+
'dry-run',
59+
null,
60+
InputOption::VALUE_NONE,
61+
'Preview mode: prints the computed resource_node.path and rolls back the transaction (no data persisted).'
62+
);
5663
}
5764

5865
protected function execute(InputInterface $input, OutputInterface $output): int
5966
{
6067
$io = new SymfonyStyle($input, $output);
6168
$io->title('Fast attendances migration');
6269

70+
$dryRun = (bool) $input->getOption('dry-run');
71+
6372
// Accept both flags as the same action (alias)
6473
$dropItemProperty = (bool) $input->getOption('drop-c-item-property')
6574
|| (bool) $input->getOption('drop-c-item-properties');
6675

76+
if ($dryRun && $dropItemProperty) {
77+
$io->note('Dry-run enabled: ignoring --drop-c-item-property (no schema changes will be applied).');
78+
$dropItemProperty = false;
79+
}
80+
6781
$fallbackAdminId = $this->getFallbackAdminId();
6882
$uuidIsBinary = $this->detectUuidIsBinary();
6983

@@ -95,6 +109,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
95109
return Command::SUCCESS;
96110
}
97111

112+
if ($dryRun) {
113+
$io->warning('Dry-run enabled: all changes will be rolled back. Only paths will be printed.');
114+
}
115+
98116
$processedCourses = 0;
99117
$processedAttendances = 0;
100118

@@ -142,7 +160,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
142160
continue;
143161
}
144162

145-
$io->section("Course {$courseId}: migrating ".\count($attendanceRows).' attendances');
163+
$io->section("Course {$courseId}: processing ".\count($attendanceRows).' attendances');
146164

147165
$displayOrder = 0;
148166
$this->connection->beginTransaction();
@@ -218,8 +236,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int
218236
'user_id' => $toUserId,
219237
]);
220238

221-
// resource_node.path should be aligned with the course tree: <coursePath>-<nodeId>/
222-
$newPath = $coursePath.'-'.$resourceNodeId.'/';
239+
// resource_node.path should follow the same structure as the standard migration:
240+
// <parentPath>/<title>-<attendanceIid>-<nodeId>/
241+
$segmentTitle = trim(str_replace(['/', '\\'], '-', $attendanceTitle));
242+
$segmentTitle = preg_replace('/\s+/', ' ', $segmentTitle) ?: $segmentTitle;
243+
244+
$newPath = $coursePath.'/'.$segmentTitle.'-'.$attendanceId.'-'.$resourceNodeId.'/';
245+
246+
if ($dryRun) {
247+
$io->writeln(sprintf(' - Attendance %d -> path: %s', $attendanceId, $newPath));
248+
}
249+
223250
$this->connection->update('resource_node', ['path' => $newPath], ['id' => $resourceNodeId]);
224251

225252
// Mark attendance as migrated by storing the new resource_node_id.
@@ -229,15 +256,31 @@ protected function execute(InputInterface $input, OutputInterface $output): int
229256
$processedAttendances++;
230257
}
231258

232-
$this->connection->commit();
259+
if ($dryRun) {
260+
$this->connection->rollBack();
261+
$io->note("Dry-run: rolled back changes for course {$courseId}.");
262+
} else {
263+
$this->connection->commit();
264+
}
265+
233266
$processedCourses++;
234267
} catch (\Throwable $e) {
235-
$this->connection->rollBack();
268+
try {
269+
$this->connection->rollBack();
270+
} catch (\Throwable) {
271+
// Ignore rollback failures.
272+
}
273+
236274
$io->error("Course {$courseId}: transaction failed - ".$e->getMessage());
237275
return Command::FAILURE;
238276
}
239277
}
240278

279+
if ($dryRun) {
280+
$io->success("Dry-run finished. Courses processed: {$processedCourses}. Attendances simulated: {$processedAttendances}.");
281+
return Command::SUCCESS;
282+
}
283+
241284
$io->success("Done. Courses processed: {$processedCourses}. Attendances migrated: {$processedAttendances}.");
242285

243286
if ($dropItemProperty) {
@@ -353,14 +396,12 @@ private function maybeDropItemProperty(SymfonyStyle $io): void
353396
$io->section('Dropping legacy table "c_item_property"...');
354397

355398
try {
356-
// DBAL generates the proper DROP TABLE for the current platform.
357399
$sm = $this->connection->createSchemaManager();
358400
$sm->dropTable('c_item_property');
359401

360402
$io->success('Legacy table "c_item_property" dropped.');
361403
} catch (DbalException $e) {
362404
$io->error('Failed to drop legacy table "c_item_property": '.$e->getMessage());
363-
// Optional: throw $e; // if you want to fail hard
364405
}
365406
}
366407

0 commit comments

Comments
 (0)