|
|
@ -6,7 +6,6 @@ namespace App\Tables; |
|
|
|
use App\Models\Revision; |
|
|
|
use App\Models\Revision; |
|
|
|
use App\Models\Row; |
|
|
|
use App\Models\Row; |
|
|
|
use App\Models\Table; |
|
|
|
use App\Models\Table; |
|
|
|
use Illuminate\Pagination\Paginator; |
|
|
|
|
|
|
|
use Illuminate\Queue\SerializesModels; |
|
|
|
use Illuminate\Queue\SerializesModels; |
|
|
|
use Illuminate\Support\Collection; |
|
|
|
use Illuminate\Support\Collection; |
|
|
|
use MightyPork\Exceptions\NotApplicableException; |
|
|
|
use MightyPork\Exceptions\NotApplicableException; |
|
|
@ -105,6 +104,12 @@ class Changeset |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public $removedColumns = []; |
|
|
|
public $removedColumns = []; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Generator iterating all properties, used internally for serialization to array |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @return \Generator |
|
|
|
|
|
|
|
* @throws \ReflectionException |
|
|
|
|
|
|
|
*/ |
|
|
|
private function walkProps() |
|
|
|
private function walkProps() |
|
|
|
{ |
|
|
|
{ |
|
|
|
$properties = (new ReflectionClass($this))->getProperties(); |
|
|
|
$properties = (new ReflectionClass($this))->getProperties(); |
|
|
@ -299,6 +304,8 @@ class Changeset |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
|
|
|
|
* Retrieve a column by ID (even new columns) |
|
|
|
|
|
|
|
* |
|
|
|
* @param string $id |
|
|
|
* @param string $id |
|
|
|
* @return Column |
|
|
|
* @return Column |
|
|
|
*/ |
|
|
|
*/ |
|
|
@ -315,7 +322,9 @@ class Changeset |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* @param string $id |
|
|
|
* Retrieve a column and modify it by the Changeset. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param string $id - column ID |
|
|
|
* @return Column |
|
|
|
* @return Column |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public function fetchAndTransformColumn(string $id) |
|
|
|
public function fetchAndTransformColumn(string $id) |
|
|
@ -333,6 +342,11 @@ class Changeset |
|
|
|
return $column; |
|
|
|
return $column; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Mark a row for removal |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param int $id - row ID (_id) |
|
|
|
|
|
|
|
*/ |
|
|
|
public function rowRemove(int $id) |
|
|
|
public function rowRemove(int $id) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if ($this->isNewRow($id)) { |
|
|
|
if ($this->isNewRow($id)) { |
|
|
@ -343,21 +357,44 @@ class Changeset |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Undo row removal |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param int $id - row ID (_id) |
|
|
|
|
|
|
|
*/ |
|
|
|
public function rowRestore(int $id) |
|
|
|
public function rowRestore(int $id) |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->removedRows = array_diff($this->removedRows, [$id]); |
|
|
|
$this->removedRows = array_diff($this->removedRows, [$id]); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Test if a row is new |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param int $id - row ID (_id) |
|
|
|
|
|
|
|
* @return bool - is new |
|
|
|
|
|
|
|
*/ |
|
|
|
public function isNewRow(int $id) |
|
|
|
public function isNewRow(int $id) |
|
|
|
{ |
|
|
|
{ |
|
|
|
return isset($this->newRows[$id]); |
|
|
|
return isset($this->newRows[$id]); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Test if a column is new |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param string $id - column ID |
|
|
|
|
|
|
|
* @return bool - is new |
|
|
|
|
|
|
|
*/ |
|
|
|
public function isNewColumn(string $id) |
|
|
|
public function isNewColumn(string $id) |
|
|
|
{ |
|
|
|
{ |
|
|
|
return isset($this->newColumns[$id]); |
|
|
|
return isset($this->newColumns[$id]); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Retrieve a single row and transform it by the Changeset |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param int $id - row ID (_id) |
|
|
|
|
|
|
|
* @return \DecoratedRow|object |
|
|
|
|
|
|
|
*/ |
|
|
|
public function fetchAndTransformRow(int $id) |
|
|
|
public function fetchAndTransformRow(int $id) |
|
|
|
{ |
|
|
|
{ |
|
|
|
$r = $this->fetchRow($id); |
|
|
|
$r = $this->fetchRow($id); |
|
|
@ -365,7 +402,12 @@ class Changeset |
|
|
|
return $transformed; |
|
|
|
return $transformed; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function fetchColumns() |
|
|
|
/** |
|
|
|
|
|
|
|
* Fetch columns from DB (not including new columns) |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @return Column[] |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function fetchRevisionColumns() |
|
|
|
{ |
|
|
|
{ |
|
|
|
return Column::columnsFromJson($this->revision->columns); |
|
|
|
return Column::columnsFromJson($this->revision->columns); |
|
|
|
} |
|
|
|
} |
|
|
@ -384,7 +426,7 @@ class Changeset |
|
|
|
return $nr; |
|
|
|
return $nr; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$r = $this->revision->rowsData($this->fetchColumns(), true, false) |
|
|
|
$r = $this->revision->rowsData($this->fetchRevisionColumns(), true, false) |
|
|
|
->whereRaw("data->>'_id' = ?", $id)->first(); |
|
|
|
->whereRaw("data->>'_id' = ?", $id)->first(); |
|
|
|
|
|
|
|
|
|
|
|
if (!$r) throw new NotExistException("No such row _id = $id in this revision."); |
|
|
|
if (!$r) throw new NotExistException("No such row _id = $id in this revision."); |
|
|
@ -443,8 +485,10 @@ class Changeset |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* @param $newVals |
|
|
|
* Update a column specification |
|
|
|
* @return Column |
|
|
|
* |
|
|
|
|
|
|
|
* @param object $newVals - new values for the column |
|
|
|
|
|
|
|
* @return Column - the column, modified and decorated with _orig etc |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public function columnUpdate($newVals) |
|
|
|
public function columnUpdate($newVals) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -475,6 +519,11 @@ class Changeset |
|
|
|
return $this->fetchAndTransformColumn($id); |
|
|
|
return $this->fetchAndTransformColumn($id); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Mark a column for removal, or forget it if it's a new column |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param string $id - column ID |
|
|
|
|
|
|
|
*/ |
|
|
|
public function columnRemove(string $id) |
|
|
|
public function columnRemove(string $id) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if ($this->isNewColumn($id)) { |
|
|
|
if ($this->isNewColumn($id)) { |
|
|
@ -489,13 +538,18 @@ class Changeset |
|
|
|
$this->clearColumnOrderIfUnchanged(); |
|
|
|
$this->clearColumnOrderIfUnchanged(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Restore a column previously marked for removal |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param string $id - column ID |
|
|
|
|
|
|
|
*/ |
|
|
|
public function columnRestore(string $id) |
|
|
|
public function columnRestore(string $id) |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->removedColumns = array_diff($this->removedColumns, [$id]); |
|
|
|
$this->removedColumns = array_diff($this->removedColumns, [$id]); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Get a page of added rows for display in the editor |
|
|
|
* Get a page of new rows, for display in the editor |
|
|
|
* |
|
|
|
* |
|
|
|
* @param int $perPage |
|
|
|
* @param int $perPage |
|
|
|
* @return \Illuminate\Pagination\LengthAwarePaginator|Collection|array |
|
|
|
* @return \Illuminate\Pagination\LengthAwarePaginator|Collection|array |
|
|
@ -506,14 +560,18 @@ class Changeset |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* @param Column[] $columns |
|
|
|
* Convert a raw CSV array to a rows array, validating and casting the data. |
|
|
|
* @param array $csvArray |
|
|
|
* All created rows are assigned globally unique IDs |
|
|
|
* @param bool $forTableInsert |
|
|
|
* |
|
|
|
* @return \array[][]|Collection |
|
|
|
* @param Column[] $columns - ordered array of columns to collect from all CSV rows |
|
|
|
|
|
|
|
* @param array $csvArray - CSV parsed to 2D array |
|
|
|
|
|
|
|
* @param bool $forTableInsert - if true, row data will be wrapped in ['data'=>...] |
|
|
|
|
|
|
|
* so it can be inserted directly into DB |
|
|
|
|
|
|
|
* @return Collection |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static function csvToRowsArray($columns, $csvArray, $forTableInsert) |
|
|
|
public static function csvToRowsArray($columns, $csvArray, $forTableInsert) |
|
|
|
{ |
|
|
|
{ |
|
|
|
/** @var Collection|array[][] $rows */ |
|
|
|
/** @var Collection $rows */ |
|
|
|
$rows = collect($csvArray)->map(function ($row) use ($columns, $forTableInsert) { |
|
|
|
$rows = collect($csvArray)->map(function ($row) use ($columns, $forTableInsert) { |
|
|
|
if (count($row) == 0 || count($row) == 1 && $row[0] == '') return null; // discard empty lines |
|
|
|
if (count($row) == 0 || count($row) == 1 && $row[0] == '') return null; // discard empty lines |
|
|
|
if (count($row) != count($columns)) { |
|
|
|
if (count($row) != count($columns)) { |
|
|
@ -539,7 +597,8 @@ class Changeset |
|
|
|
} |
|
|
|
} |
|
|
|
})->filter(); |
|
|
|
})->filter(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO we could use some temporary IDs and replace them with |
|
|
|
|
|
|
|
// TODO proper unique IDs when submitting the proposal |
|
|
|
$rowNumerator = new RowNumerator(count($csvArray)); |
|
|
|
$rowNumerator = new RowNumerator(count($csvArray)); |
|
|
|
|
|
|
|
|
|
|
|
if ($forTableInsert) { |
|
|
|
if ($forTableInsert) { |
|
|
@ -556,16 +615,23 @@ class Changeset |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Append N blank rows |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param int $count |
|
|
|
|
|
|
|
*/ |
|
|
|
public function addBlankRows(int $count) |
|
|
|
public function addBlankRows(int $count) |
|
|
|
{ |
|
|
|
{ |
|
|
|
$numerator = new RowNumerator($count); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$columns = $this->fetchAndTransformColumns(); |
|
|
|
$columns = $this->fetchAndTransformColumns(); |
|
|
|
$template = []; |
|
|
|
$template = []; |
|
|
|
foreach ($columns as $column) { |
|
|
|
foreach ($columns as $column) { |
|
|
|
$template[$column->id] = $column->cast(null); |
|
|
|
$template[$column->id] = $column->cast(null); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO we could use some temporary IDs and replace them with |
|
|
|
|
|
|
|
// TODO proper unique IDs when submitting the proposal |
|
|
|
|
|
|
|
$numerator = new RowNumerator($count); |
|
|
|
|
|
|
|
|
|
|
|
foreach ($numerator->generate() as $_id) { |
|
|
|
foreach ($numerator->generate() as $_id) { |
|
|
|
$row = $template; |
|
|
|
$row = $template; |
|
|
|
$row['_id'] = $_id; |
|
|
|
$row['_id'] = $_id; |
|
|
@ -573,6 +639,11 @@ class Changeset |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Add rows from imported CSV |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param array $csvArray - CSV parsed to 2D array |
|
|
|
|
|
|
|
*/ |
|
|
|
public function addFilledRows($csvArray) |
|
|
|
public function addFilledRows($csvArray) |
|
|
|
{ |
|
|
|
{ |
|
|
|
/** @var Column[] $columns */ |
|
|
|
/** @var Column[] $columns */ |
|
|
@ -585,6 +656,11 @@ class Changeset |
|
|
|
$this->newRows = $this->newRows + $rows->toArray(); |
|
|
|
$this->newRows = $this->newRows + $rows->toArray(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Add a blank column (pre-filled with dummies to satisfy validation) |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @return array - column array |
|
|
|
|
|
|
|
*/ |
|
|
|
public function addBlankCol() |
|
|
|
public function addBlankCol() |
|
|
|
{ |
|
|
|
{ |
|
|
|
$cid = (new ColumnNumerator(1))->next(); |
|
|
|
$cid = (new ColumnNumerator(1))->next(); |
|
|
@ -604,6 +680,11 @@ class Changeset |
|
|
|
return $col; |
|
|
|
return $col; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Set a new column order |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param array $order - array of column IDs |
|
|
|
|
|
|
|
*/ |
|
|
|
public function setColOrder(array $order) |
|
|
|
public function setColOrder(array $order) |
|
|
|
{ |
|
|
|
{ |
|
|
|
$allCols = $this->fetchAndTransformColumns(); |
|
|
|
$allCols = $this->fetchAndTransformColumns(); |
|
|
@ -615,9 +696,12 @@ class Changeset |
|
|
|
$this->clearColumnOrderIfUnchanged(); |
|
|
|
$this->clearColumnOrderIfUnchanged(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Remove added rows that are not filled (user added too many with the Add Rows form) |
|
|
|
|
|
|
|
*/ |
|
|
|
public function removeEmptyNewRows() |
|
|
|
public function removeEmptyNewRows() |
|
|
|
{ |
|
|
|
{ |
|
|
|
$cols = $this->fetchColumns(); |
|
|
|
$cols = $this->fetchRevisionColumns(); |
|
|
|
$emptyTpl = collect($cols)->keyBy('id')->map(function(Column $c) { |
|
|
|
$emptyTpl = collect($cols)->keyBy('id')->map(function(Column $c) { |
|
|
|
return $c->cast(null); |
|
|
|
return $c->cast(null); |
|
|
|
})->all(); |
|
|
|
})->all(); |
|
|
@ -638,6 +722,10 @@ class Changeset |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Discard the column order array if it's identical to the natural / default |
|
|
|
|
|
|
|
* ordering. This simplifies the Changeset and reduces possible conflicts |
|
|
|
|
|
|
|
*/ |
|
|
|
public function clearColumnOrderIfUnchanged() |
|
|
|
public function clearColumnOrderIfUnchanged() |
|
|
|
{ |
|
|
|
{ |
|
|
|
$expected = collect($this->revision->columns) |
|
|
|
$expected = collect($this->revision->columns) |
|
|
@ -653,16 +741,25 @@ class Changeset |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Clear the custom column order |
|
|
|
|
|
|
|
*/ |
|
|
|
public function resetColumnOrder() |
|
|
|
public function resetColumnOrder() |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->columnOrder = []; |
|
|
|
$this->columnOrder = []; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Restore all columns marked for removal |
|
|
|
|
|
|
|
*/ |
|
|
|
public function resetRemovedColumns() |
|
|
|
public function resetRemovedColumns() |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->removedColumns = []; |
|
|
|
$this->removedColumns = []; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Discard all added columns |
|
|
|
|
|
|
|
*/ |
|
|
|
public function resetAddedColumns() |
|
|
|
public function resetAddedColumns() |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->columnOrder = array_values( |
|
|
|
$this->columnOrder = array_values( |
|
|
@ -675,21 +772,33 @@ class Changeset |
|
|
|
$this->clearColumnOrderIfUnchanged(); |
|
|
|
$this->clearColumnOrderIfUnchanged(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Discard all column changes |
|
|
|
|
|
|
|
*/ |
|
|
|
public function resetUpdatedColumns() |
|
|
|
public function resetUpdatedColumns() |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->columnUpdates = []; |
|
|
|
$this->columnUpdates = []; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Restore all rows marked for removal |
|
|
|
|
|
|
|
*/ |
|
|
|
public function resetRemovedRows() |
|
|
|
public function resetRemovedRows() |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->removedRows = []; |
|
|
|
$this->removedRows = []; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Discard all added rows |
|
|
|
|
|
|
|
*/ |
|
|
|
public function resetAddedRows() |
|
|
|
public function resetAddedRows() |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->newRows = []; |
|
|
|
$this->newRows = []; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Discard all row changes |
|
|
|
|
|
|
|
*/ |
|
|
|
public function resetUpdatedRows() |
|
|
|
public function resetUpdatedRows() |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->rowUpdates = []; |
|
|
|
$this->rowUpdates = []; |
|
|
|