parent
3bf9b68503
commit
1037f438ce
@ -0,0 +1,125 @@ |
||||
<?php |
||||
|
||||
|
||||
namespace App\Tables; |
||||
|
||||
|
||||
use App\Models\Table; |
||||
|
||||
/** |
||||
* Table exporter base class |
||||
*/ |
||||
abstract class BaseExporter |
||||
{ |
||||
/** |
||||
* @var Table the table to export |
||||
*/ |
||||
protected $table; |
||||
|
||||
/** |
||||
* @var Column[] - parsed columns for the table |
||||
*/ |
||||
protected $columns; |
||||
|
||||
/** |
||||
* @var bool - true if user wants download; can e.g. change mime type to text/plain if not |
||||
*/ |
||||
protected $wantDownload; |
||||
|
||||
/** |
||||
* TODO export options from enabled columns, sorts, etc |
||||
* |
||||
* @param Table $table |
||||
*/ |
||||
public function __construct(Table $table) |
||||
{ |
||||
$this->table = $table; |
||||
$this->columns = Column::columnsFromJson($table->revision->columns); |
||||
} |
||||
|
||||
/** |
||||
* @return string - mime type for the downloaded file |
||||
*/ |
||||
protected abstract function getMimeType(); |
||||
|
||||
/** |
||||
* @return string - file extension for the downloaded file |
||||
*/ |
||||
protected abstract function getFileExtension(); |
||||
|
||||
/** |
||||
* @return string - file name without extension, by default the table name |
||||
*/ |
||||
protected function getFileBasename() |
||||
{ |
||||
return $this->table->name; |
||||
} |
||||
|
||||
/** |
||||
* Generate a response that appears as a file download to the browser |
||||
* |
||||
* @param bool $download - force download, otherwise the browser may show it in the window |
||||
*/ |
||||
public function exportToBrowser($download = true) |
||||
{ |
||||
$this->wantDownload = $download; |
||||
|
||||
$mimeType = $this->getMimeType(); |
||||
$filename = $this->getFileBasename() . '.' . $this->getFileExtension(); |
||||
|
||||
ob_end_clean(); |
||||
|
||||
// Redirect output to a client’s web browser |
||||
header("Content-Type: $mimeType; charset=utf-8"); |
||||
|
||||
if ($download) { |
||||
header("Content-Disposition: attachment;filename=\"$filename\""); |
||||
} |
||||
|
||||
// Cache headers |
||||
header('Cache-Control: max-age=0'); |
||||
// IE9 |
||||
header('Cache-Control: max-age=1'); |
||||
// Other IE headers |
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past |
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified |
||||
header('Cache-Control: cache, must-revalidate'); // HTTP/1.1 |
||||
header('Pragma: no-cache'); // HTTP/1.0 |
||||
|
||||
$this->writeDocument(); |
||||
exit; |
||||
} |
||||
|
||||
/** |
||||
* Generator that produces PHP arrays for all rows, fetched from the DB in given chunks. |
||||
* |
||||
* @param int $chunkSize - size of one chunk |
||||
* @return \Generator|array[] |
||||
*/ |
||||
protected function iterateRows($chunkSize = 1000) |
||||
{ |
||||
$revision = $this->table->revision; |
||||
|
||||
$count = $revision->rows()->count(); |
||||
|
||||
$start = 0; |
||||
while ($start < $count) { |
||||
$rows = $revision->rows()->offset($start)->limit($chunkSize)->get(); |
||||
|
||||
foreach ($rows as $row) { |
||||
$data = json_decode($row->data); |
||||
|
||||
// column renaming, value formatting... |
||||
|
||||
yield $data; |
||||
} |
||||
|
||||
$start += $chunkSize; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Write the document to stdout ('php://output') |
||||
*/ |
||||
protected abstract function writeDocument(); |
||||
} |
@ -0,0 +1,118 @@ |
||||
<?php |
||||
|
||||
|
||||
namespace App\Tables; |
||||
|
||||
|
||||
class CStructArrayExporter extends BaseExporter |
||||
{ |
||||
/** |
||||
* @return string - mime type for the downloaded file |
||||
*/ |
||||
protected function getMimeType() |
||||
{ |
||||
return $this->wantDownload ? 'text/x-csrc' : 'text/plain'; |
||||
} |
||||
|
||||
/** |
||||
* @return string - file extension for the downloaded file |
||||
*/ |
||||
protected function getFileExtension() |
||||
{ |
||||
return 'c'; |
||||
} |
||||
|
||||
private function cify($name) |
||||
{ |
||||
$name = preg_replace('/[^a-z0-9_]/i', '_', $name); |
||||
if (ctype_digit($name[0])) { |
||||
$name = '_' . $name; |
||||
} |
||||
return $name; |
||||
} |
||||
|
||||
/** |
||||
* Write the document to stdout ('php://output') |
||||
*/ |
||||
protected function writeDocument() |
||||
{ |
||||
$fields = array_map(function (Column $c) { |
||||
$type = $c->type; |
||||
if ($type == 'string') { |
||||
$type = 'const char *'; |
||||
} |
||||
|
||||
$name = $this->cify($c->name); |
||||
|
||||
return (object)['name' => $c->name, 'type' => $c->type, 'ctype' => $type, 'cname' => $name]; |
||||
}, $this->columns); |
||||
|
||||
$ctablename = $this->cify($this->table->name); |
||||
|
||||
// preamble |
||||
echo "#include <stdbool.h>\n\n"; |
||||
|
||||
echo "struct " . $ctablename . " {\n"; |
||||
foreach ($fields as $field) { |
||||
echo " " . $field->ctype . " " . $field->cname . ";\n"; |
||||
} |
||||
echo "};\n\n"; |
||||
|
||||
// Now the table itself |
||||
echo "const struct ".$ctablename.' tbl_'.$ctablename."[] = {\n"; |
||||
|
||||
$first = true; |
||||
foreach ($this->iterateRows() as $row) { |
||||
if ($first) { |
||||
$first = false; |
||||
} else { |
||||
echo ",\n"; |
||||
} |
||||
|
||||
echo " {"; |
||||
|
||||
$firstf = true; |
||||
foreach ($fields as $field) { |
||||
if ($firstf) { |
||||
$firstf = false; |
||||
} else { |
||||
echo ", "; |
||||
} |
||||
|
||||
$val = 0; |
||||
switch ($field->type) { |
||||
case 'string': |
||||
$val = ""; |
||||
break; |
||||
|
||||
case 'bool': |
||||
$val = false; |
||||
break; |
||||
} |
||||
|
||||
if (isset($row->{$field->name})) { |
||||
$val = $row->{$field->name}; |
||||
} |
||||
|
||||
// export to C format |
||||
switch ($field->type) { |
||||
case 'string': |
||||
echo '"' . addcslashes($val, "\0..\37!@\@\177..\377") . '"'; |
||||
break; |
||||
|
||||
case 'bool': |
||||
echo (int)$val; |
||||
break; |
||||
|
||||
case 'int': |
||||
case 'float': |
||||
echo $val; |
||||
break; |
||||
} |
||||
} |
||||
echo "}"; |
||||
} |
||||
|
||||
echo "\n};\n"; |
||||
} |
||||
} |
@ -0,0 +1,52 @@ |
||||
<?php |
||||
|
||||
|
||||
namespace App\Tables; |
||||
|
||||
|
||||
class CsvExporter extends BaseExporter |
||||
{ |
||||
/** |
||||
* @return string - mime type for the downloaded file |
||||
*/ |
||||
protected function getMimeType() |
||||
{ |
||||
return $this->wantDownload ? 'text/csv' : 'text/plain'; |
||||
} |
||||
|
||||
/** |
||||
* @return string - file extension for the downloaded file |
||||
*/ |
||||
protected function getFileExtension() |
||||
{ |
||||
return 'csv'; |
||||
} |
||||
|
||||
/** |
||||
* Write the document to stdout ('php://output') |
||||
*/ |
||||
protected function writeDocument() |
||||
{ |
||||
$handle = fopen('php://output', 'w'); |
||||
|
||||
$columnNames = array_map(function (Column $c) { |
||||
return $c->title; |
||||
}, $this->columns); |
||||
|
||||
fputcsv($handle, $columnNames); |
||||
|
||||
foreach ($this->iterateRows() as $row) { |
||||
$items = []; |
||||
foreach ($this->columns as $column) { |
||||
if (isset($row->{$column->name})) { |
||||
$items[] = $row->{$column->name}; |
||||
} else { |
||||
$items[] = null; |
||||
} |
||||
} |
||||
fputcsv($handle, $items); |
||||
} |
||||
|
||||
fclose($handle); |
||||
} |
||||
} |
@ -0,0 +1,44 @@ |
||||
<?php |
||||
|
||||
|
||||
namespace App\Tables; |
||||
|
||||
|
||||
class JsonExporter extends BaseExporter |
||||
{ |
||||
/** |
||||
* @return string - mime type for the downloaded file |
||||
*/ |
||||
protected function getMimeType() |
||||
{ |
||||
return 'application/json'; |
||||
} |
||||
|
||||
/** |
||||
* @return string - file extension for the downloaded file |
||||
*/ |
||||
protected function getFileExtension() |
||||
{ |
||||
return 'json'; |
||||
} |
||||
|
||||
/** |
||||
* Write the document to stdout ('php://output') |
||||
*/ |
||||
protected function writeDocument() |
||||
{ |
||||
echo "[\n"; |
||||
|
||||
$first = true; |
||||
foreach ($this->iterateRows() as $row) { |
||||
if ($first) { |
||||
$first = false; |
||||
} else { |
||||
echo ",\n"; |
||||
} |
||||
echo json_encode($row, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES); |
||||
} |
||||
|
||||
echo "\n]\n"; |
||||
} |
||||
} |
Binary file not shown.
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 19 KiB |
Binary file not shown.
Binary file not shown.
@ -0,0 +1,22 @@ |
||||
<b>Export presets:</b> |
||||
|
||||
<ul class="mt-2"> |
||||
@foreach([ |
||||
'json' => 'JSON array of objects', |
||||
'csv' => 'CSV table', |
||||
'c' => 'C structs array', |
||||
] as $type => $title) |
||||
|
||||
<li><a href="{{ route('table.export', [ |
||||
'user' => $table->owner->name, |
||||
'table' => $table->name, |
||||
'format' => $type |
||||
]) }}">{{ $title }}</a> – <a href="{{ route('table.export', [ |
||||
'user' => $table->owner->name, |
||||
'table' => $table->name, |
||||
'format' => $type, |
||||
'dl' => 1 |
||||
]) }}">@icon(fa-download px-1, Download)</a> |
||||
|
||||
@endforeach |
||||
</ul> |
Loading…
Reference in new issue