Skip to content

Commit f70e26c

Browse files
authored
feat: Create script to migrate old url storage to new s3 (#414)
1 parent 1711b6a commit f70e26c

File tree

3 files changed

+179
-1
lines changed

3 files changed

+179
-1
lines changed
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Laravelcm\DatabaseMigration\Commands;
6+
7+
use Illuminate\Console\Command;
8+
use Illuminate\Support\Collection;
9+
use Illuminate\Support\Facades\DB;
10+
use Illuminate\Support\Facades\Storage;
11+
12+
final class UpdateStorageUrlsCommand extends Command
13+
{
14+
protected $signature = 'db:update-storage-urls
15+
{--dry-run : Show what would be updated without executing}
16+
{--old-domain=https://laravel.cm/storage : Old storage domain to replace}
17+
{--new-disk=media : New disk to use for URLs}';
18+
19+
protected $description = 'Update old storage URLs to new S3 URLs in database content';
20+
21+
private array $affectedTables = [
22+
'articles' => ['body'],
23+
'threads' => ['body'],
24+
'replies' => ['body'],
25+
];
26+
27+
public function handle(): int
28+
{
29+
$isDryRun = $this->option('dry-run');
30+
/** @var string $oldDomain */
31+
$oldDomain = $this->option('old-domain');
32+
/** @var string $newDisk */
33+
$newDisk = $this->option('new-disk');
34+
35+
if ($isDryRun) {
36+
$this->warn('🔍 DRY RUN MODE - No URLs will be actually updated');
37+
}
38+
39+
$this->info('🔄 Updating storage URLs in database content...');
40+
$this->newLine();
41+
42+
/** @var string $newBaseUrl */
43+
$newBaseUrl = $this->getNewBaseUrl($newDisk);
44+
45+
if (! $newBaseUrl) {
46+
$this->error("❌ Could not determine base URL for disk: {$newDisk}");
47+
48+
return Command::FAILURE;
49+
}
50+
51+
$this->info("Old domain: {$oldDomain}");
52+
$this->info("New base URL: {$newBaseUrl}");
53+
$this->newLine();
54+
55+
$totalUpdated = 0;
56+
$totalRecords = 0;
57+
58+
foreach ($this->affectedTables as $table => $columns) {
59+
foreach ($columns as $column) {
60+
$records = $this->getRecordsWithOldUrls($table, $column, $oldDomain);
61+
$recordCount = $records->count();
62+
$totalRecords += $recordCount;
63+
64+
if ($recordCount === 0) {
65+
continue;
66+
}
67+
68+
$this->info("📋 Processing {$table}.{$column} - {$recordCount} records found");
69+
70+
$progressBar = $this->output->createProgressBar($recordCount);
71+
$progressBar->start();
72+
73+
$updated = 0;
74+
foreach ($records as $record) {
75+
$newContent = $this->updateUrlsInContent($record->$column, $oldDomain, $newBaseUrl);
76+
77+
if ($newContent !== $record->$column) {
78+
if (! $isDryRun) {
79+
DB::table($table)
80+
->where('id', $record->id)
81+
->update([$column => $newContent]);
82+
}
83+
$updated++;
84+
}
85+
86+
$progressBar->advance();
87+
}
88+
89+
$progressBar->finish();
90+
$this->newLine();
91+
92+
if ($updated > 0) {
93+
if ($isDryRun) {
94+
$this->line(" Would update {$updated} records in {$table}.{$column}");
95+
} else {
96+
$this->line(" ✅ Updated {$updated} records in {$table}.{$column}");
97+
}
98+
}
99+
100+
$totalUpdated += $updated;
101+
}
102+
}
103+
104+
$this->newLine();
105+
if ($isDryRun) {
106+
$this->info("✅ Dry run completed - {$totalUpdated} records would be updated out of {$totalRecords} total records with old URLs");
107+
} else {
108+
$this->info("✅ URL migration completed - {$totalUpdated} records updated out of {$totalRecords} total records with old URLs");
109+
}
110+
111+
return Command::SUCCESS;
112+
}
113+
114+
private function getNewBaseUrl(string $disk): ?string
115+
{
116+
try {
117+
$diskConfig = config("filesystems.disks.{$disk}");
118+
119+
if (! $diskConfig) {
120+
return null;
121+
}
122+
123+
if ($diskConfig['driver'] === 's3') {
124+
$baseUrl = '';
125+
126+
if (isset($diskConfig['url']) && $diskConfig['url']) {
127+
$baseUrl = rtrim($diskConfig['url'], '/');
128+
} else {
129+
$bucket = $diskConfig['bucket'] ?? '';
130+
$region = $diskConfig['region'] ?? '';
131+
if ($bucket && $region) {
132+
$baseUrl = "https://{$bucket}.s3.{$region}.amazonaws.com";
133+
}
134+
}
135+
136+
if ($baseUrl && isset($diskConfig['root']) && $diskConfig['root']) {
137+
$root = trim($diskConfig['root'], '/');
138+
139+
if (filled($root)) {
140+
$baseUrl .= '/'.$root;
141+
}
142+
}
143+
144+
return $baseUrl;
145+
}
146+
147+
return Storage::disk($disk)->url('');
148+
} catch (\Exception $e) {
149+
$this->error("Error getting base URL for disk {$disk}: ".$e->getMessage());
150+
151+
return null;
152+
}
153+
}
154+
155+
private function getRecordsWithOldUrls(string $table, string $column, string $oldDomain): Collection
156+
{
157+
return collect(
158+
DB::table($table)
159+
->select('id', $column)
160+
->where($column, 'LIKE', "%{$oldDomain}%")
161+
->get()
162+
);
163+
}
164+
165+
private function updateUrlsInContent(string $content, string $oldDomain, string $newBaseUrl): string
166+
{
167+
// Pattern to match the old storage URLs
168+
// Example: https://laravel.cm/storage/ODvtYqlGsnpk9gn4hUNfBZfCw25CdlIrFL4GpooD.png
169+
$pattern = '#'.preg_quote($oldDomain, '#').'/([a-zA-Z0-9._-]+\.[a-zA-Z]{2,4})#';
170+
171+
return (string) preg_replace_callback($pattern, function (array $matches) use ($newBaseUrl): string {
172+
$filename = $matches[1];
173+
174+
return $newBaseUrl.'/'.$filename;
175+
}, $content);
176+
}
177+
}

app-modules/database-migration/src/Providers/DatabaseMigrationServiceProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Laravelcm\DatabaseMigration\Commands\MigrateFilesToS3Command;
1010
use Laravelcm\DatabaseMigration\Commands\ResetPostgresSequencesCommand;
1111
use Laravelcm\DatabaseMigration\Commands\SshTunnelCommand;
12+
use Laravelcm\DatabaseMigration\Commands\UpdateStorageUrlsCommand;
1213
use Laravelcm\DatabaseMigration\Services\DatabaseMigrationService;
1314
use Laravelcm\DatabaseMigration\Services\SshTunnelService;
1415

@@ -33,6 +34,7 @@ public function boot(): void
3334
MigrateDatabaseCommand::class,
3435
MigrateFilesToS3Command::class,
3536
ResetPostgresSequencesCommand::class,
37+
UpdateStorageUrlsCommand::class,
3638
]);
3739

3840
$this->publishes([

config/filesystems.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@
8686

8787
'links' => [
8888
public_path('storage') => storage_path('app/public'),
89-
public_path('media') => storage_path('app/media'),
9089
],
9190

9291
];

0 commit comments

Comments
 (0)