belongsTo(User::class, 'author_id'); } /** Target revision */ public function revision() { return $this->belongsTo(Revision::class); } /** Target table (that this was submitted to) */ public function table() { return $this->belongsTo(Table::class); } public function scopeUnmerged(Builder $query, Table $table) { return $query->whereRaw(' "id" NOT IN (SELECT "proposal_id" FROM "revisions" LEFT JOIN "table_revision_pivot" ON "revisions"."id" = "table_revision_pivot"."revision_id" WHERE "table_revision_pivot"."table_id" = ?)', [ $table->getKey() ]); } public function getChangesetAttribute() { $changeset = Changeset::fromObject(fromJSON($this->attributes['changes'], true)); $changeset->revision = $this->revision; $changeset->table = $this->getAttribute('table'); $changeset->note = $this->note; return $changeset; } public function setChangesetAttribute($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 (empty($changeset->note)) { 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'); } // Assign unique row IDs to new rows (they use temporary negative IDs in a draft) $changeset->renumberRows(); return new Proposal([ // relations 'table_id' => $changeset->table->getKey(), 'revision_id' => $changeset->revision->getKey(), 'author_id' => \user()->getKey(), // the proposal info 'note' => $changeset->note, 'changeset' => $changeset, // this is without a note ]); } /** * Accept the proposed changes: create a new revision based on the parent revision * and changes described in this Proposal, and link it to the table. */ public function createRevision() { DB::transaction(function () { $changeset = $this->changeset; $columns = $changeset->fetchAndTransformColumns(); $newRevision = new Revision([ 'ancestor_id' => $changeset->revision->getKey(), 'proposal_id' => $this->getKey(), 'note' => $changeset->note, 'row_count' => 0, // Will be set later when we are sure about the row count 'columns' => array_filter(array_map(function(Column $c) { if ($c->toRemove) return null; return $c->toArray(false); }, $columns)), ]); $newRevision->save(); // this gives it an ID, needed to associate rows // --- Copy over rows that are left unchanged --- // ...this directly works with the pivot $removedRowIds = (array)($changeset->removedRows ?? []); $changedRowIds = array_keys((array)($changeset->rowUpdates ?? [])); $excludedGRIDs = array_merge($removedRowIds, $changedRowIds); $excluded_ids = []; if ($excludedGRIDs) { $questionmarks = str_repeat('?,', count($excludedGRIDs) - 1) . '?'; $excluded_ids = $changeset->revision->rows()->whereRaw("data->'_id' IN ($questionmarks)", $excludedGRIDs) ->get(['id'])->pluck('id')->toArray(); } $query = ' INSERT INTO revision_row_pivot SELECT ? as revision_id, row_id FROM revision_row_pivot WHERE revision_id = ?'; $subs = [ $newRevision->getKey(), $changeset->revision->getKey() ]; if ($excluded_ids) { $questionmarks = str_repeat('?,', count($excluded_ids) - 1) . '?'; $query .= ' AND row_id NOT IN (' . $questionmarks . ')'; $subs = array_merge($subs, $excluded_ids); } DB::statement($query, $subs); // --- Insert modified rows --- if ($changeset->rowUpdates) { $ids = array_keys($changeset->rowUpdates); $questionmarks = str_repeat('?,', count($ids) - 1) . '?'; $toChange = $changeset->revision->rows()->whereRaw("data->'_id' IN ($questionmarks)", $ids)->get(); $updateData = []; foreach ($toChange as $row) { $updateData[] = new Row([ 'data' => $changeset->transformRow($row->data, false), ]); } $newRevision->rows()->saveMany($updateData); } // --- Insert new rows --- if ($changeset->newRows) { $newRowData = []; foreach ($changeset->newRows as $newRow) { $newRowData[] = new Row(['data' => $newRow]); } $newRevision->rows()->saveMany($newRowData); } $newRevision->update(['row_count' => $newRevision->rows()->count()]); // --- Attach this revision to the table --- $changeset->table->revisions()->save($newRevision); $changeset->table->update(['revision_id' => $newRevision->getKey()]); }); } }