tables()->where('name', $table)->first(); $revision = $tableModel->revision; return view('table.view', [ 'table' => $tableModel, 'revision' => $revision, 'columns' => Column::columnsFromJson(json_decode($revision->columns)), 'rows' => $revision->rows, ]); } /** * SHow a form for creating a new table * * @return \Illuminate\Http\Response */ public function create() { $exampleColumns = "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"; return view('table.create', compact('exampleColumns', 'exampleData') ); } public function storeNew(Request $request) { /** @var User $u */ $u = \Auth::user(); $input = $this->validate($request, [ 'name' => [ 'required', VALI_NAME, Rule::unique('tables'), ], 'title' => ['required', VALI_LINE], 'description' => ['nullable', VALI_TEXT], 'license' => ['nullable', VALI_TEXT], 'origin' => ['nullable', VALI_TEXT], 'columns' => 'required|string', 'data' => 'string|nullable', ]); // Check if table name is unique for user if ($u->tables()->where('name', $input->name)->exists()) { return $this->backWithErrors([ 'name' => "A table called \"$input->name\" already exists in your account.", ]); } // Parse and validate the columns specification /** @var Column[] $columns */ $columns = []; $column_keys = []; // for checking duplicates $colTable = array_map('str_getcsv', explode("\n", $input->columns)); // prevent griefing via long list of columns if (count($colTable) > 100) return $this->backWithErrors(['columns' => "Too many columns"]); foreach ($colTable as $col) { $col = array_map('trim', $col); if (count($col) < 2 || strlen($col[0])==0) { return $this->backWithErrors(['columns' => "All columns must have at least name and type."]); } try { if (in_array($col[0], $column_keys)) { return $this->backWithErrors(['columns' => "Duplicate column: $col[0]"]); } $column_keys[] = $col[0]; $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()]); } } if (count($columns) == 0) return $this->backWithErrors(['columns' => "Define at least one column"]); $rowTable = array_map('str_getcsv', explode("\n", $input->data)); // Preparing data to insert into the Rows table $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; if (strlen($val) > 255) { // try to stop people inserting unstructured crap / malformed CSV throw new NotApplicableException("Value for column $key too long."); } $parsed[$key] = $columns[$i]->cast($val); } return [ 'data' => json_encode($parsed), ]; }, $rowTable); } catch (\Exception $e) { return $this->backWithErrors(['columns' => $e->getMessage()]); } $revisionFields = [ 'note' => "Initial revision of table $u->name/$input->name", 'columns' => json_encode($columns), 'row_count' => count($rowsData), ]; $tableFields = [ 'owner_id' => $u->id, 'revision_id' => 0, 'name' => $input->name, 'title' => $input->title, 'description' => $input->description, 'license' => $input->license, 'origin' => $input->origin, ]; \DB::transaction(function () use ($revisionFields, $tableFields, $rowsData) { $revision = Revision::create($revisionFields); $tableFields['revision_id'] = $revision->id; // to meet the not-null constraint $table = Table::create($tableFields); // Attach the revision to the table, set as current $table->revisions()->attach($revision); $table->revision()->associate($revision); // Spawn rows, linked to the revision $revision->rows()->createMany($rowsData); }); return redirect(route('table.view', ['user' => $u, 'table' => $input->name])); } }