rudimentary export options
This commit is contained in:
@@ -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,119 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace App\Tables;
|
||||
|
||||
|
||||
use JsonSerializable;
|
||||
use MightyPork\Exceptions\NotApplicableException;
|
||||
use MightyPork\Utils\Utils;
|
||||
|
||||
/**
|
||||
* Helper class representing one column in a data table.
|
||||
*
|
||||
* @property-read string $name
|
||||
* @property-read string $title
|
||||
* @property-read string $type
|
||||
*/
|
||||
class Column implements JsonSerializable
|
||||
{
|
||||
const colTypes = [
|
||||
'int', 'bool', 'float', 'string'
|
||||
];
|
||||
|
||||
private $name;
|
||||
private $title;
|
||||
private $type;
|
||||
|
||||
public static function columnsFromJson($columns)
|
||||
{
|
||||
if (is_string($columns)) {
|
||||
$columns = json_decode($columns);
|
||||
}
|
||||
|
||||
return array_map(function ($x) {
|
||||
return new Column($x);
|
||||
}, $columns);
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
if (property_exists($this, $name)) {
|
||||
return $this->$name;
|
||||
}
|
||||
|
||||
throw new NotApplicableException("No such column property");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from object or array
|
||||
*
|
||||
* @param $obj
|
||||
*/
|
||||
public function __construct($obj)
|
||||
{
|
||||
$b = new \objBag($obj);
|
||||
$this->name = $b->name;
|
||||
$this->title = $b->title;
|
||||
$this->type = $b->type;
|
||||
|
||||
if (!in_array($this->type, self::colTypes)) {
|
||||
throw new NotApplicableException("\"$this->type\" is not a valid column type.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array with keys {name, title, type}
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return [
|
||||
'name' => $this->name,
|
||||
'title' => $this->title,
|
||||
'type' => $this->type,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a value to the target type, validating it in the process
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return bool|float|int|string
|
||||
*/
|
||||
public function cast($value)
|
||||
{
|
||||
switch ($this->type) {
|
||||
case 'int':
|
||||
if (is_int($value)) return $value;
|
||||
if (is_float($value)) return round($value);
|
||||
if (is_numeric($value)) return intval($value);
|
||||
throw new NotApplicableException("Could not convert value \"$value\" to int!");
|
||||
|
||||
case 'float':
|
||||
if (is_int($value) || is_float($value)) return (float)$value;
|
||||
if (is_numeric($value)) return floatval($value);
|
||||
throw new NotApplicableException("Could not convert value \"$value\" to float!");
|
||||
|
||||
case 'bool':
|
||||
return Utils::parseBool($value);
|
||||
|
||||
case 'string':
|
||||
return "$value";
|
||||
|
||||
default:
|
||||
throw new \LogicException("Illegal column type: \"$this->type\"");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify data which should be serialized to JSON
|
||||
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||
* @return mixed data which can be serialized by <b>json_encode</b>,
|
||||
* which is a value of any type other than a resource.
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user