Commit a7776b0c authored by Maik Messerschmidt's avatar Maik Messerschmidt
Browse files

Added infrastructure for data codecs.

parent a1cceec3
<?php
namespace App\Logic\Codec;
/**
* Helper trait (read: type class) for decoders / encoders with table-like format.
*/
trait Table {
/**
* Converts an array of n+1 rows to an array of n associative arrays.
*/
static function rowsToObjects(array $rows) {
$collected = collect($rows);
$header = $collected->shift();
// Create associative arrays from the header keys.
return $collected
->map(function($entry) use ($header) {
// Fill / remove $entry with values to match the headers length.
$entry = array_pad($entry, count($header), null);
$entry = array_slice($entry, 0, count($header));
return array_combine($header, $entry);
})->toArray();
}
/**
* Converts an array of n associative arrays to an array of n+1 rows.
*
* Note: We use _associative arrays_ not php objects,
* even if the name is objectsToRows().
* I found this name much easier to understand than assocsToRows()
* or anything alike.
*/
static function objectsToRows(array $objects) {
$header = collect($objects)
->map('array_keys')
->flatten()
->unique()
->toArray();
$rows = collect($objects)->map(function($entry) use ($header) {
$ordered = [];
foreach ($header as $key) {
array_push($ordered, $entry[$key]);
}
return $ordered;
});
$rows->prepend($header);
return $rows->toArray();
}
}
<?php
namespace App\Logic\Decoder;
use App\Logic\Decoder\Table;
use League\Csv\Reader;
/**
* Provides a non-streamable interface for converting string from and to CSV.
* Useful for importing inputs and exporting results with small payload.
*
* Note: Will likely lead to memory problems for big datasets.
*/
class CSV {
use Table;
const name = "csv";
const description = "CSV Table";
/**
* Decodes a CSV string to an array of rows.
*/
static function toRows(string $datasets) : array {
return collect(
Reader::createFromString($datasets)->getRecords())->toArray();
}
}
<?php
namespace App\Logic\Decoder;
use App\Logic\Decoder\CSV;
class Decoders {
const classes = [
// JSONObject::class,
// JSONObjects::class,
// JSONRows::class,
CSV::class
];
/**
* Returns names and descriptions of all decoders as collection.
*/
static function all() {
$names = collect(self::classes)
->map(function($cls) { return $cls::name; });
$descriptions = collect(self::classes)
->map(function($cls) { return $cls::description; });
return $names->combine($descriptions);
}
static function get(string $name) {
foreach (self::classes as $cls) {
if ($cls::name === $name)
return $cls;
}
throw new Exception("Unknown decoder '{$name}'.");
}
}
<?php
namespace App\Logic\Decoder;
use App\Logic\Codec\Table as TableCodec;
/**
* Helper trait (read: type class) for decoders of table-like format.
*
* minimal class implementation:
* - one of toRows() or toObjects
*/
trait Table {
use TableCodec;
static function toObjects(string $datasets) {
return self::rowsToObjects(self::toRows($datasets));
}
static function toRows(string $datasets) {
return self::objectsToRows(self::toObjects($datasets));
}
}
<?php
namespace App\Logic\Encoder;
use App\Logic\Encoder\Table;
use League\Csv\Writer;
/**
* Provides a non-streamable interface for converting datasets to CSV.
* Useful for importing inputs and exporting results with small payload.
*
* Note: Will likely lead to memory problems for big datasets.
*/
class CSV {
use Table;
const name = "csv";
const description = "CSV Table";
/**
* Encodes an array of rows to a CSV string.
*/
static function fromRows(array $datasets) : string {
$writer = Writer::createFromString('');
$writer->insertAll($datasets);
return $writer->getContent();
}
}
<?php
namespace App\Logic\Encoder;
// use App\Logic\Encoder\JSONObject;
// use App\Logic\Encoder\JSONObjects;
// use App\Logic\Encoder\JSONRows;
use App\Logic\Encoder\CSV;
class Encoders {
const classes = [
JSONObject::class,
JSONObjects::class,
// JSONRows::class,
CSV::class
];
/**
* Returns names and descriptions of all encoders as collection.
*/
static function all() {
$names = collect(self::classes)
->map(function($cls) { return $cls::name; });
$descriptions = collect(self::classes)
->map(function($cls) { return $cls::description; });
return $names->combine($descriptions);
}
static function get(string $name) {
foreach (self::classes as $cls) {
if ($cls::name === $name)
return $cls;
}
throw new Exception("Unknown encoder '{$name}'.");
}
}
<?php
namespace App\Logic\Encoder;
use App\Logic\Encoder\Table;
/**
* Provides a non-streamable interface for encoding datasets to a single json object.
* Useful for importing inputs and exporting results with small payload.
*
* Note: Will likely lead to memory problems for big datasets.
*/
class JSONObject {
use Table;
const name = "json_object";
const description = "Single JSON Object";
static function fromObjects(array $datasets) : string {
return json_encode($datasets[0], JSON_FORCE_OBJECT);
}
}
<?php
namespace App\Logic\Encoder;
use App\Logic\Encoder\Table;
/**
* Provides a non-streamable interface for encoding datasets to an array of json objects.
* Useful for importing inputs and exporting results with small payload.
*
* Note: Will likely lead to memory problems for big datasets.
*/
class JSONObjects {
use Table;
const name = "json_objects";
const description = "Array of JSON Objects";
static function fromObjects(array $datasets) : string {
$entries = collect($datasets)
->map(function($dataset) {
return json_encode($dataset, JSON_FORCE_OBJECT);
});
return "[\n " . $entries->join(",\n ") . "\n]";
}
}
<?php
namespace App\Logic\Encoder;
use App\Logic\Codec\Table as TableCodec;
/**
* Helper trait (read: type class) for encoders for table-like structures.
*
* minimal class implementation:
* - one of fromRows() or fromObjects().
*/
trait Table {
use TableCodec;
static function fromRows(array $rows) {
return self::fromObjects(self::rowsToObjects($rows));
}
static function fromObjects(array $objects) {
return self::fromRows(self::objectsToRows($objects));
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment