Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[4.x] Allow custom form exporters to be defined #8837

Merged
28 changes: 11 additions & 17 deletions config/forms.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,27 +48,21 @@

/*
|--------------------------------------------------------------------------
| CSV Export Delimiter
| Exporters
|--------------------------------------------------------------------------
|
| Statamic will use this character as delimiter for csv exports.
| Here you may define all the available form submission exporters.
| You may customize the options within each exporter's array.
|
*/

'csv_delimiter' => ',',

/*
|--------------------------------------------------------------------------
| CSV Export Headings
|--------------------------------------------------------------------------
|
| The values to be used in the csv export header rows.
| Can be the field handle or the field display text.
|
| Supported values: "handle", "display"
|
*/

'csv_headers' => 'handle',
'exporters' => [
'csv' => [
'class' => Statamic\Forms\Exporters\CsvExporter::class,
],
'json' => [
'class' => Statamic\Forms\Exporters\JsonExporter::class,
],
ryanmitchell marked this conversation as resolved.
Show resolved Hide resolved
],

];
10 changes: 8 additions & 2 deletions resources/views/forms/show.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,17 @@
@endcan
</dropdown-list>

@if ($exporters = $form->exporters())
<dropdown-list>
<button class="btn" slot="trigger">{{ __('Export Submissions') }}</button>
<dropdown-item :text="__('Export as CSV')" redirect="{{ cp_route('forms.export', ['type' => 'csv', 'form' => $form->handle()]) }}?download=true"></dropdown-item>
<dropdown-item :text="__('Export as JSON')" redirect="{{ cp_route('forms.export', ['type' => 'json', 'form' => $form->handle()]) }}?download=true"></dropdown-item>
@foreach ($exporters as $exporter)
<dropdown-item
text="{{ $exporter->title() }}"
redirect="{{ $exporter->downloadUrl() }}"
></dropdown-item>
@endforeach
</dropdown-list>
@endif
</div>
</header>

Expand Down
28 changes: 0 additions & 28 deletions src/Contracts/Forms/Exporter.php

This file was deleted.

38 changes: 0 additions & 38 deletions src/Forms/Exporters/AbstractExporter.php

This file was deleted.

43 changes: 15 additions & 28 deletions src/Forms/Exporters/CsvExporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,40 @@

use League\Csv\Writer;
use SplTempFileObject;
use Statamic\Support\Arr;

class CsvExporter extends AbstractExporter
class CsvExporter extends Exporter
{
/**
* @var Writer
*/
private $writer;

/**
* Create a new CsvExporter.
*/
public function __construct()
private Writer $writer;
protected static string $title = 'CSV';

public function export(): string
{
$this->writer = Writer::createFromFileObject(new SplTempFileObject);
$this->writer->setDelimiter(config('statamic.forms.csv_delimiter', ','));
}
$this->writer->setDelimiter(Arr::get($this->config, 'delimiter', config('statamic.forms.csv_delimiter', ',')));

/**
* Perform the export.
*
* @return string
*/
public function export()
{
$this->insertHeaders();

$this->insertData();

return (string) $this->writer;
}

/**
* Insert the headers into the CSV.
*/
private function insertHeaders()
{
$key = config('statamic.forms.csv_headers', 'handle');
$key = Arr::get($this->config, 'headers', config('statamic.forms.csv_headers', 'handle'));

$headers = $this->form()->fields()
$headers = $this->form->fields()
->map(fn ($field) => $key === 'display' ? $field->display() : $field->handle())
->push($key === 'display' ? __('Date') : 'date')
->values()->all();

$this->writer->insertOne($headers);
}

/**
* Insert the submission data into the CSV.
*/
private function insertData()
{
$data = $this->form()->submissions()->map(function ($submission) {
$data = $this->form->submissions()->map(function ($submission) {
$submission = $submission->toArray();

$submission['date'] = (string) $submission['date'];
Expand All @@ -69,4 +51,9 @@ private function insertData()

$this->writer->insertAll($data);
}

public function extension(): string
{
return 'csv';
}
}
84 changes: 84 additions & 0 deletions src/Forms/Exporters/Exporter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

namespace Statamic\Forms\Exporters;

use Illuminate\Http\Response;
use Statamic\Contracts\Forms\Form;
use Statamic\Facades\File;
use Symfony\Component\HttpFoundation\BinaryFileResponse;

abstract class Exporter
{
protected static string $title;
protected array $config;
protected string $handle;
protected Form $form;

abstract public function export(): string;

public function setHandle(string $handle)
{
$this->handle = $handle;

return $this;
}

public function setConfig(array $config)
{
$this->config = $config;

return $this;
}

public function setForm(Form $form)
{
$this->form = $form;

return $this;
}

public function contentType(): string
{
return 'text/plain';
}

public function extension(): string
{
return 'txt';
}

public function title(): string
{
return __($this->config['title'] ?? static::$title);
}

public function allowedOnForm(Form $form)
{
return ! isset($this->config['forms']) || in_array($form->handle(), $this->config['forms']);
}

public function downloadUrl()
{
return cp_route('forms.export', [
'type' => $this->handle,
'form' => $this->form->handle(),
'download' => true,
]);
}

public function response(): Response
{
return response($this->export())->header('Content-Type', $this->contentType());
}

public function download(): BinaryFileResponse
{
$content = $this->export();

$path = storage_path('statamic/tmp/forms/'.$this->form->handle().'-'.time().'.'.$this->extension());

File::put($path, $content);

return response()->download($path)->deleteFileAfterSend();
}
}
25 changes: 25 additions & 0 deletions src/Forms/Exporters/ExporterRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Statamic\Forms\Exporters;

use Exception;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Support\Collection;
use Statamic\Support\Arr;

class ExporterRepository
{
public function all(): Collection
{
return collect(config('statamic.forms.exporters', []))
->map(function ($config, $handle) {
try {
$exporter = app($class = Arr::pull($config, 'class'));
} catch (BindingResolutionException $e) {
throw new Exception("Class [$class] does not exist, defined in exporter [$handle].");
}

return $exporter->setHandle($handle)->setConfig($config);
});
}
}
25 changes: 11 additions & 14 deletions src/Forms/Exporters/JsonExporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,24 @@

namespace Statamic\Forms\Exporters;

class JsonExporter extends AbstractExporter
class JsonExporter extends Exporter
{
/**
* Perform the export.
*
* @return string
*/
public function export()
protected static string $title = 'JSON';

public function export(): string
{
$submissions = $this->form()->submissions()->toArray();
$submissions = $this->form->submissions()->toArray();

return json_encode($submissions);
}

/**
* Get the content type.
*
* @return string
*/
public function contentType()
public function contentType(): string
{
return 'application/json';
}

public function extension(): string
{
return 'json';
}
}
14 changes: 14 additions & 0 deletions src/Forms/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Statamic\Facades\Form as FormFacade;
use Statamic\Facades\YAML;
use Statamic\Forms\Exceptions\BlueprintUndefinedException;
use Statamic\Forms\Exporters\Exporter;
use Statamic\Statamic;
use Statamic\Support\Arr;
use Statamic\Support\Traits\FluentlyGetsAndSets;
Expand Down Expand Up @@ -389,4 +390,17 @@ public function actionUrl()
{
return route('statamic.forms.submit', $this->handle());
}

public function exporters()
{
return FormFacade::exporters()
->all()
->filter->allowedOnForm($this)
->each->setForm($this);
}

public function exporter(string $handle): ?Exporter
{
return $this->exporters()->get($handle);
}
}
Loading