Skip to content

Commit bd0b6c5

Browse files
committed
Add command to execute import/export from command line
1 parent be61180 commit bd0b6c5

File tree

3 files changed

+146
-2
lines changed

3 files changed

+146
-2
lines changed

src/Commands/ExportCommand.php

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<?php
2+
3+
namespace Botble\DataSynchronize\Commands;
4+
5+
use Botble\DataSynchronize\Exporter\Exporter;
6+
use Exception;
7+
use Illuminate\Console\Command;
8+
use Illuminate\Contracts\Console\PromptsForMissingInput;
9+
10+
use function Laravel\Prompts\search;
11+
use function Laravel\Prompts\select;
12+
13+
use function Laravel\Prompts\text;
14+
15+
use SplFileInfo;
16+
use Symfony\Component\Console\Attribute\AsCommand;
17+
18+
use Symfony\Component\Console\Input\InputArgument;
19+
use Symfony\Component\Console\Input\InputOption;
20+
use Symfony\Component\Finder\Finder;
21+
22+
#[AsCommand(name: 'data-synchronize:export', description: 'Export data from database to Excel/Csv file')]
23+
class ExportCommand extends Command implements PromptsForMissingInput
24+
{
25+
public function handle(): void
26+
{
27+
$exporter = $this->argument('exporter') ?: search(
28+
label: 'Which exporter do you want to use?',
29+
options: fn (string $value) => array_filter(
30+
$this->possibleExporters(),
31+
fn (string $exporter) => str_contains(strtolower($exporter), strtolower($value))
32+
),
33+
);
34+
$path = $this->argument('path') ?: text(
35+
'Where do you want to save the file?',
36+
'E.g. storage/app/exports'
37+
);
38+
$format = $this->option('format') ?: select(
39+
label: 'Which format do you want to export?',
40+
options: ['csv' => 'CSV', 'xlsx' => 'XLSX'],
41+
default: 'csv',
42+
);
43+
44+
if (! class_exists($exporter)) {
45+
$this->components->error('Exporter class does not exist');
46+
47+
exit(self::FAILURE);
48+
}
49+
50+
$exporter = new $exporter();
51+
52+
if (! $exporter instanceof Exporter) {
53+
$this->components->error('Exporter class must be an instance of ' . Exporter::class);
54+
55+
exit(self::FAILURE);
56+
}
57+
58+
$exporter->format($format);
59+
60+
$this->components->info("Exporting {$exporter->getLabel()} to <comment>{$path}</comment>");
61+
62+
try {
63+
$exporter->export()
64+
->getFile()
65+
->move($path, $exporter->getExportFileName());
66+
} catch (Exception $e) {
67+
$this->components->error($e->getMessage());
68+
69+
exit(self::FAILURE);
70+
}
71+
72+
$this->components->info(
73+
"{$exporter->getLabel()} has been exported to <comment>{$path}/{$exporter->getExportFileName()}</comment>"
74+
);
75+
76+
exit(self::SUCCESS);
77+
}
78+
79+
protected function getOptions(): array
80+
{
81+
return [
82+
['format', null, InputOption::VALUE_OPTIONAL, 'The format of the file (csv, xls, xlsx)'],
83+
];
84+
}
85+
86+
protected function getArguments(): array
87+
{
88+
return [
89+
['exporter', InputArgument::OPTIONAL, 'The exporter class name'],
90+
['path', InputArgument::OPTIONAL, 'The path to save the file'],
91+
];
92+
}
93+
94+
protected function promptForMissingArgumentsUsing(): array
95+
{
96+
return [
97+
'exporter' => ['What is the exporter class name?', 'E.g. Botble\Blog\Exporters\PostExporter'],
98+
'path' => ['Where do you want to save the file?', 'E.g. storage/app/exports'],
99+
];
100+
}
101+
102+
protected function possibleExporters(): array
103+
{
104+
$exporters = [];
105+
106+
collect(
107+
Finder::create()
108+
->files()
109+
->in(platform_path())
110+
->name('*.php')
111+
->contains(Exporter::class)
112+
)
113+
->map(function (SplFileInfo $file) use (&$exporters) {
114+
$class = $this->resolveExporterNamespace($file->getPathname());
115+
116+
if (
117+
class_exists($class)
118+
&& is_subclass_of($class, Exporter::class)
119+
) {
120+
$exporters[] = $class;
121+
}
122+
});
123+
124+
return $exporters;
125+
}
126+
127+
protected function resolveExporterNamespace(string $path): string
128+
{
129+
$content = file_get_contents($path);
130+
$namespace = str($content)->after('namespace ')->before(';')->trim();
131+
$basename = basename($path, '.php');
132+
133+
return $namespace . '\\' . $basename;
134+
}
135+
}

src/Exporter/Exporter.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
use Botble\Base\Facades\BaseHelper;
77
use Botble\DataSynchronize\Concerns\Exporter\HasEmptyState;
88
use Botble\DataSynchronize\Enums\ExportColumnType;
9+
use Carbon\Carbon;
910
use Illuminate\Contracts\View\View;
11+
use Illuminate\Support\Str;
1012
use Maatwebsite\Excel\Concerns\FromCollection;
1113
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
1214
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
@@ -173,7 +175,12 @@ public function getColumns(): array
173175

174176
public function getExportFileName(): string
175177
{
176-
return str_replace(' ', '-', $this->getLabel());
178+
return sprintf(
179+
'%s-%s.%s',
180+
Str::slug($this->getLabel()),
181+
BaseHelper::formatDateTime(Carbon::now(), 'Y-m-d-H-i-s'),
182+
$this->format
183+
);
177184
}
178185

179186
public function render(): View
@@ -201,7 +208,7 @@ public function export(): BinaryFileResponse
201208
},
202209
];
203210

204-
return ExcelFacade::download($this, "{$this->getExportFileName()}.{$this->format}", $writeType, $headers);
211+
return ExcelFacade::download($this, $this->getExportFileName(), $writeType, $headers);
205212
}
206213

207214
public function acceptedColumns(?array $columns): self

src/Providers/DataSynchronizeServiceProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Botble\Base\Supports\ServiceProvider;
88
use Botble\Base\Traits\LoadAndPublishDataTrait;
99
use Botble\DataSynchronize\Commands\ClearChunksCommand;
10+
use Botble\DataSynchronize\Commands\ExportCommand;
1011
use Botble\DataSynchronize\Commands\ExportControllerMakeCommand;
1112
use Botble\DataSynchronize\Commands\ExporterMakeCommand;
1213
use Botble\DataSynchronize\Commands\ImportControllerMakeCommand;
@@ -38,6 +39,7 @@ public function boot(): void
3839
ImportControllerMakeCommand::class,
3940
ExportControllerMakeCommand::class,
4041
ClearChunksCommand::class,
42+
ExportCommand::class,
4143
]);
4244

4345
$this->app->afterResolving(Schedule::class, function (Schedule $schedule) {

0 commit comments

Comments
 (0)