preparing for proposal composition
This commit is contained in:
@@ -5,6 +5,7 @@ namespace App\Http\Controllers;
|
||||
use App\Models\Revision;
|
||||
use App\Models\Table;
|
||||
use App\Models\User;
|
||||
use App\Tables\Changeset;
|
||||
use App\Tables\Column;
|
||||
use App\Tables\ColumnNumerator;
|
||||
use App\Tables\CStructArrayExporter;
|
||||
@@ -57,22 +58,29 @@ class TableController extends Controller
|
||||
public function draftChange(Request $request, User $user, string $table)
|
||||
{
|
||||
/** @var Table $tableModel */
|
||||
$tableModel = $user->tables()->with('revision')->where('name', $table)->first();
|
||||
$tableModel = $user->tables()->where('name', $table)->first();
|
||||
if ($tableModel === null) abort(404, "No such table.");
|
||||
|
||||
$revision = $tableModel->revision;
|
||||
$session_key = "proposal_{$tableModel->id}";
|
||||
|
||||
/** @var Changeset $changeset */
|
||||
$changeset = $request->session()->remember($session_key, function () use ($tableModel) {
|
||||
$changeset = new Changeset();
|
||||
$changeset->table = $tableModel;
|
||||
$changeset->revision = $tableModel->revision;
|
||||
return $changeset;
|
||||
});
|
||||
|
||||
$revision = $changeset->revision;
|
||||
$columns = Column::columnsFromJson($revision->columns);
|
||||
|
||||
$rows = $revision->rowsData($columns)->paginate(25, []);
|
||||
|
||||
// TODO instantiate changeset and store it in session
|
||||
|
||||
return view('table.view', [
|
||||
return view('table.propose', [
|
||||
'table' => $tableModel,
|
||||
'revision' => $revision,
|
||||
'columns' => $columns,
|
||||
'rows' => $rows,
|
||||
'changeset' => $changeset,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
+59
-1
@@ -3,6 +3,8 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Concerns\Reportable;
|
||||
use App\Tables\Changeset;
|
||||
use MightyPork\Exceptions\NotApplicableException;
|
||||
|
||||
/**
|
||||
* Change proposal
|
||||
@@ -14,7 +16,7 @@ use App\Models\Concerns\Reportable;
|
||||
* @property int $revision_id
|
||||
* @property int $author_id
|
||||
* @property string $note
|
||||
* @property object $changes - JSONB
|
||||
* @property Proposal $changes - JSONB
|
||||
* @property-read User $author
|
||||
* @property-read Table $table
|
||||
* @property-read Revision $revision
|
||||
@@ -43,4 +45,60 @@ class Proposal extends BaseModel
|
||||
{
|
||||
return $this->belongsTo(Table::class);
|
||||
}
|
||||
|
||||
public function getChangesAttribute($value)
|
||||
{
|
||||
$changeset = Changeset::fromObject(fromJSON($value));
|
||||
$changeset->revision = $this->revision;
|
||||
$changeset->table = $this->table;
|
||||
$changeset->note = $this->note;
|
||||
|
||||
return $changeset;
|
||||
}
|
||||
|
||||
public function setChangesAttribute($value)
|
||||
{
|
||||
if ($value instanceof Changeset) {
|
||||
$this->attributes['changes'] = toJSON($value->toObject());
|
||||
} else {
|
||||
throw new NotApplicableException("Only a Changeset may be set to Proposal->changes");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Proposal instance wrapping this changeset,
|
||||
* owned by the currently logged in User. The created instance
|
||||
* is NOT saved yet.
|
||||
*
|
||||
* @param Changeset $changeset - changeset to hydrate the proposal with
|
||||
* @return Proposal
|
||||
*/
|
||||
public static function fromChangeset(Changeset $changeset)
|
||||
{
|
||||
if (!$changeset->hasAnyChanges()) {
|
||||
throw new NotApplicableException('No changes to propose.');
|
||||
}
|
||||
|
||||
if ($changeset->note == null) {
|
||||
throw new NotApplicableException('Proposal note must be filled.');
|
||||
}
|
||||
|
||||
if ($changeset->table == null || !$changeset->table instanceof Table) {
|
||||
throw new NotApplicableException('Table not assigned to Changeset');
|
||||
}
|
||||
|
||||
if ($changeset->revision == null || !$changeset->revision instanceof Revision) {
|
||||
throw new NotApplicableException('Revision not assigned to Changeset');
|
||||
}
|
||||
|
||||
return new Proposal([
|
||||
// relations
|
||||
'table_id' => $changeset->table->getKey(),
|
||||
'revision_id' => $changeset->revision->getKey(),
|
||||
'author_id' => \Auth::user()->getKey(),
|
||||
// the proposal info
|
||||
'note' => $changeset->note,
|
||||
'changes' => $changeset->toObject(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,44 @@
|
||||
|
||||
namespace App\Tables;
|
||||
|
||||
use App\Models\Revision;
|
||||
use App\Models\Table;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* Object representing a set of table modifications
|
||||
*/
|
||||
class Changeset
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
const object_excluded_properties = [
|
||||
'revision',
|
||||
'table',
|
||||
'note',
|
||||
];
|
||||
|
||||
/**
|
||||
* Rows whose content changed
|
||||
* @var Revision - base revision this changeset belongs to
|
||||
*/
|
||||
public $revision;
|
||||
|
||||
/**
|
||||
* @var Table - table this changeset belongs to
|
||||
*/
|
||||
public $table;
|
||||
|
||||
/**
|
||||
* @var string - user's note attached to this changeset (future proposal)
|
||||
*/
|
||||
public $note;
|
||||
|
||||
/**
|
||||
* Rows whose content changed, identified by _id.
|
||||
* Only changed values are to be filled. Columns are identified by GCIDs
|
||||
*
|
||||
* @var array|null - [GRID -> values, ...]
|
||||
* @var array|null - [[_id:…, …], …]
|
||||
*/
|
||||
public $rowUpdates;
|
||||
|
||||
@@ -19,7 +48,7 @@ class Changeset
|
||||
* New rows in the full Row::data format, including GRIDs.
|
||||
* Values are identified by GCIDs from previously defined, or new columns.
|
||||
*
|
||||
* @var array|null - [[_id:..., ...], [..., ...], ...]
|
||||
* @var array|null - [[_id:…, …], …]
|
||||
*/
|
||||
public $newRows;
|
||||
|
||||
@@ -41,7 +70,7 @@ class Changeset
|
||||
/**
|
||||
* New columns in the full format, including GCIDs
|
||||
*
|
||||
* @var array|null - [[id:..., ...], [..., ...], ...]
|
||||
* @var array|null - [[id:…, …], …]
|
||||
*/
|
||||
public $newColumns;
|
||||
|
||||
@@ -66,4 +95,65 @@ class Changeset
|
||||
* @var int[]|null - GCIDs
|
||||
*/
|
||||
public $removedColumns;
|
||||
|
||||
private function walkProps()
|
||||
{
|
||||
$properties = (new ReflectionClass($this))->getProperties();
|
||||
|
||||
foreach ($properties as $property) {
|
||||
if (in_array($property->name, self::object_excluded_properties)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
yield $property->name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconstruct from a object, such as one found in Proposal.
|
||||
* Note that the fromProposal() method should be used when the
|
||||
* proposal is available, as it populates additional fields.
|
||||
*
|
||||
* @param \stdClass $changes
|
||||
* @return Changeset
|
||||
*/
|
||||
public static function fromObject($changes)
|
||||
{
|
||||
$changeset = new Changeset();
|
||||
|
||||
foreach ($changeset->walkProps() as $prop) {
|
||||
if (isset($changes->$prop)) {
|
||||
$changeset->$prop = $changes->$prop;
|
||||
}
|
||||
}
|
||||
|
||||
return $changeset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize to an object format that may be stored in a Proposal.
|
||||
*
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function toObject()
|
||||
{
|
||||
$object = new \stdClass();
|
||||
|
||||
foreach ($this->walkProps() as $prop) {
|
||||
$object->$prop = array_values($this->$prop);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function hasAnyChanges()
|
||||
{
|
||||
foreach ($this->walkProps() as $prop) {
|
||||
if (!empty($this->$prop)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user