creating tables

This commit is contained in:
2018-07-21 23:07:41 +02:00
parent 75afc67f31
commit 26488e5883
15 changed files with 215 additions and 48 deletions
+78 -13
View File
@@ -2,8 +2,13 @@
namespace App\Http\Controllers;
use App\Models\Revision;
use App\Models\Row;
use App\Models\Table;
use App\Models\User;
use App\Utils\Column;
use Illuminate\Http\Request;
use MightyPork\Exceptions\NotApplicableException;
class TableController extends Controller
{
@@ -15,14 +20,14 @@ class TableController extends Controller
public function create()
{
$exampleColumns =
"latin, string, Latin Name\n".
"common, string, Common Name\n".
"lifespan, int, Lifespan (years)";
"latin,string,Latin Name\n".
"common,string,Common Name\n".
"lifespan,int,Lifespan (years)";
$exampleData =
"Mercenaria mercenaria, hard clam, 40\n" .
"Magallana gigas, pacific oyster, 30\n" .
"Patella vulgata, common limpet, 20";
"Mercenaria mercenaria,hard clam,40\n" .
"Magallana gigas,pacific oyster,30\n" .
"Patella vulgata,common limpet,20";
return view('table.create',
compact('exampleColumns', 'exampleData')
@@ -31,32 +36,92 @@ class TableController extends Controller
public function storeNew(Request $request)
{
/** @var User $u */
$u = \Auth::user();
$this->validate($request, [
'name' => 'required',
'title' => 'string|nullable',
'title' => 'string',
'description' => 'string|nullable',
'license' => 'string|nullable',
'upstream' => 'string|nullable',
'origin' => 'string|nullable',
'columns' => 'required',
'data' => 'string|nullable',
]);
// Check if table name is unique for user
$name = $request->get('name');
if ($u->tables()->where('name', $name)->exists()) {
$tabName = $request->get('name');
if ($u->tables()->where('name', $tabName)->exists()) {
return $this->backWithErrors([
'name' => "A table called \"$name\" already exists in your account.",
'name' => "A table called \"$tabName\" already exists in your account.",
]);
}
// Parse and validate the columns specification
/** @var Column[] $columns */
$columns = [];
$colTable = array_map('str_getcsv', explode("\n", $request->get('columns')));
foreach ($colTable as $col) {
$col = array_map('trim', $col);
if (count($col) < 2) {
return $this->backWithErrors([
'columns' => "All columns must have at least name and type.",
]);
}
try {
$columns[] = new Column([
'name' => $col[0],
'type' => $col[1],
'title' => count($col) >= 3 ? $col[2] : $col[0], // title falls back to =name if not specified,
]);
} catch (\Exception $e) {
return $this->backWithErrors(['columns' => $e->getMessage()]);
}
}
$rowTable = array_map('str_getcsv', explode("\n", $request->get('data')));
$rowsData = null;
try {
$rowsData = array_map(function ($row) use ($columns) {
if (count($row) != count($columns)) {
throw new NotApplicableException("All rows must have ".count($columns)." fields.");
}
$parsed = [];
foreach ($row as $i => $val) {
$key = $columns[$i]->name;
$parsed[$key] = $columns[$i]->cast($val);
}
return [
'data' => json_encode($parsed),
'refs' => 1,
];
}, $rowTable);
}catch (\Exception $e) {
return $this->backWithErrors(['columns' => $e->getMessage()]);
}
$revision = Revision::create([
'refs' => 1, // from the new table
'note' => "Initial revision of table $u->name/$tabName",
'columns' => json_encode($columns),
]);
$table = Table::create([
'owner_id' => $u->id,
'revision_id' => $revision->id,
'name' => $tabName,
'title' => $request->get('title'),
'description' => $request->get('description'),
'license' => $request->get('license'),
'origin' => $request->get('origin'),
]);
$revision->rows()->createMany($rowsData);
// Now we create rows, a revision pointing to them, and the table using it.
return "Ok.";
}
}
+2
View File
@@ -19,6 +19,8 @@ use Illuminate\Database\Eloquent\Model;
*/
class ContentReport extends Model
{
protected $guarded = [];
/** Authoring user */
public function author()
{
+1
View File
@@ -23,6 +23,7 @@ use Illuminate\Database\Eloquent\Model;
class Proposal extends Model
{
use Reportable;
protected $guarded = [];
protected static function boot()
{
+2 -1
View File
@@ -15,7 +15,7 @@ use Riesjart\Relaquent\Model\Concerns\HasRelaquentRelationships;
* @property int $refs
* @property int $ancestor_id
* @property string $note
* @property string $index_column
* @property object $columns
* @property Revision|null $parentRevision
* @property Row[]|Collection $rows
* @property Proposal|null $sourceProposal - proposal that was used to create this revision
@@ -24,6 +24,7 @@ use Riesjart\Relaquent\Model\Concerns\HasRelaquentRelationships;
class Revision extends Model
{
use HasRelaquentRelationships;
protected $guarded = [];
protected static function boot()
{
+1
View File
@@ -13,5 +13,6 @@ use Illuminate\Database\Eloquent\Model;
*/
class Row extends Model
{
protected $guarded = [];
public $timestamps = false;
}
+3 -1
View File
@@ -15,10 +15,11 @@ use Illuminate\Database\Eloquent\Model;
* @property int $owner_id
* @property int $ancestor_id
* @property int $revision_id
* @property string $name
* @property string $title
* @property string $description
* @property string $license
* @property string $source_link
* @property string $origin
* @property User $owner
* @property Table $parentTable
* @property Table[]|Collection $forks
@@ -32,6 +33,7 @@ use Illuminate\Database\Eloquent\Model;
class Table extends Model
{
use Reportable;
protected $guarded = [];
protected static function boot()
{
+1
View File
@@ -24,6 +24,7 @@ use Illuminate\Database\Eloquent\Model;
class TableComment extends Model
{
use Reportable;
protected $guarded = [];
protected static function boot()
{
+108
View File
@@ -0,0 +1,108 @@
<?php
namespace App\Utils;
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 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();
}
}