Skip to content

Commit 246a7bc

Browse files
committed
Command to migrate attachments from one disk to another
1 parent d3f4aa0 commit 246a7bc

File tree

5 files changed

+183
-2
lines changed

5 files changed

+183
-2
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,14 @@ A command is provided to cleanup the attachments not bound to a model
322322
The `-s` (or `--since=[timeInMinutes]`) option can be set to specify
323323
another time limit in minutes : only unbound files older than the
324324
specified age will be deleted. This value is set to **1440** by default.
325+
326+
## Migrate command
327+
328+
To migrate **all** attachments from one source disk to another :
329+
330+
php artisan attachments:migrate public s3
331+
332+
Files are removed from source disk if successfully saved on the target disk.
325333

326334
## Customization
327335

src/AttachmentsServiceProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Bnb\Laravel\Attachments;
44

55
use Bnb\Laravel\Attachments\Console\Commands\CleanupAttachments;
6+
use Bnb\Laravel\Attachments\Console\Commands\MigrateAttachments;
67
use Illuminate\Support\ServiceProvider;
78

89
class AttachmentsServiceProvider extends ServiceProvider
@@ -31,6 +32,7 @@ public function boot()
3132
if ($this->app->runningInConsole()) {
3233
$this->commands([
3334
CleanupAttachments::class,
35+
MigrateAttachments::class,
3436
]);
3537
}
3638
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<?php
2+
/**
3+
* laravel
4+
*
5+
* @author Jérémy GAULIN <[email protected]>
6+
* @copyright 2017 - B&B Web Expertise
7+
*/
8+
9+
namespace Bnb\Laravel\Attachments\Console\Commands;
10+
11+
use Bnb\Laravel\Attachments\Attachment;
12+
use Carbon\Carbon;
13+
use Exception;
14+
use Illuminate\Console\Command;
15+
use Illuminate\Support\Collection;
16+
use Illuminate\Support\Facades\Storage;
17+
use Lang;
18+
use Log;
19+
use Symfony\Component\Console\Input\InputArgument;
20+
use Symfony\Component\Console\Input\InputOption;
21+
use Throwable;
22+
23+
class MigrateAttachments extends Command
24+
{
25+
26+
protected $signature = 'attachments:migrate';
27+
28+
29+
public function __construct()
30+
{
31+
parent::__construct();
32+
33+
$this->setDescription(Lang::get('attachments::messages.console.migrate_description'));
34+
35+
$this->getDefinition()->addArgument(new InputArgument('from', InputArgument::REQUIRED,
36+
Lang::get('attachments::messages.console.migrate_option_from')))
37+
;
38+
39+
$this->getDefinition()->addArgument(new InputArgument('to', InputArgument::REQUIRED,
40+
Lang::get('attachments::messages.console.migrate_option_to')))
41+
;
42+
}
43+
44+
45+
public function handle()
46+
{
47+
if ($this->argument('from') === $this->argument('to')) {
48+
$this->error(Lang::get('attachments::messages.console.migrate_error_missing'));
49+
50+
return;
51+
}
52+
53+
if (empty(config(sprintf('filesystems.disks.%s', $this->argument('from'))))) {
54+
$this->error(Lang::get('attachments::messages.console.migrate_error_from'));
55+
56+
return;
57+
}
58+
59+
if (empty(config(sprintf('filesystems.disks.%s', $this->argument('to'))))) {
60+
$this->error(Lang::get('attachments::messages.console.migrate_error_to'));
61+
62+
return;
63+
}
64+
65+
try {
66+
Storage::disk($this->argument('from'))
67+
->has('.')
68+
;
69+
} catch (Exception $e) {
70+
$this->error(Lang::get('attachments::messages.console.migrate_invalid_from'));
71+
}
72+
try {
73+
74+
Storage::disk($this->argument('to'))
75+
->has('.')
76+
;
77+
} catch (Exception $e) {
78+
$this->error(Lang::get('attachments::messages.console.migrate_invalid_to'));
79+
}
80+
81+
$query = Attachment::query()
82+
->where('disk', '=', $this->argument('from'));
83+
84+
$this
85+
->getOutput()
86+
->progressStart($query->count())
87+
;
88+
89+
do {
90+
$deferred = [];
91+
$continue = true;
92+
93+
try {
94+
$items = $query
95+
->take(10)
96+
->get()
97+
->each(function (Attachment $attachment) use (&$deferred) {
98+
if ($this->move($attachment, $deferred)) {
99+
$attachment->disk = $this->argument('to');
100+
101+
$attachment->save();
102+
}
103+
104+
$this
105+
->getOutput()
106+
->progressAdvance()
107+
;
108+
});
109+
} catch (Exception $e) {
110+
$continue = false;
111+
112+
$this->error($e->getMessage());
113+
Log::error($e);
114+
}
115+
116+
foreach ($deferred as $callable) {
117+
try {
118+
$callable();
119+
} catch (Exception | Throwable $e) {
120+
$this->warn(sprintf('Failed to clean source file : %s', $e->getMessage()));
121+
Log::error($e);
122+
}
123+
}
124+
} while ($continue && $items->isNotEmpty());
125+
126+
$this
127+
->getOutput()
128+
->progressFinish()
129+
;
130+
}
131+
132+
133+
private function move(Attachment $attachment, &$deferred)
134+
{
135+
$from = $attachment->disk;
136+
$to = $this->argument('to');
137+
$filepath = $attachment->filepath;
138+
139+
if ( ! Storage::disk($from)->exists($filepath)) {
140+
return true;
141+
}
142+
143+
Storage::disk($to)
144+
->put($filepath, Storage::disk($from)->get($filepath))
145+
;
146+
147+
$deferred[] = function () use ($from, $filepath) {
148+
Storage::disk($from)
149+
->delete($filepath)
150+
;
151+
};
152+
153+
return true;
154+
}
155+
}

translations/en/messages.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,13 @@
1616
'cleanup_confirm' => 'Do you confirm the deletion of unbound attachments ?',
1717
'cleanup_option_since' => 'Minimum age (in minutes) of the attachment to delete (based on modification date).',
1818
'cleanup_no_data' => 'No attachment to handle.',
19+
'migrate_description' => 'Migrate attachments from given disk to another keeping path.',
20+
'migrate_option_from' => 'Source disk.',
21+
'migrate_option_to' => 'Destination disk.',
22+
'migrate_error_missing' => 'Cannot not migrate to the same disk.',
23+
'migrate_error_from' => 'Unknown source disk.',
24+
'migrate_error_to' => 'Unknown target disk.',
25+
'migrate_invalid_from' => 'Unreadable source disk.',
26+
'migrate_invalid_to' => 'Unreadable destination disk.',
1927
],
20-
];
28+
];

translations/fr/messages.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,13 @@
1616
'cleanup_confirm' => 'Confirmez-vous vouloir supprimer les pièces jointes non liées à un modèle ?',
1717
'cleanup_option_since' => 'Âge minimum (en minutes) des données à supprimer (se base sur la date de modification).',
1818
'cleanup_no_data' => 'Aucune pièce jointe à traiter.',
19+
'migrate_description' => 'Migre les pièces jointes d’un disque donné vers un autre en conservant le chemin.',
20+
'migrate_option_from' => 'Disque d’origine.',
21+
'migrate_option_to' => 'Disque de destination.',
22+
'migrate_error_missing' => 'Impossible de migrer vers le même disque.',
23+
'migrate_error_from' => 'Disque d’origine inconnu.',
24+
'migrate_error_to' => 'Disque de destination inconnu.',
25+
'migrate_invalid_from' => 'Disque d’origine illisible.',
26+
'migrate_invalid_to' => 'Disque de destination illisible.',
1927
],
20-
];
28+
];

0 commit comments

Comments
 (0)