|  |  | @ -4,6 +4,9 @@ namespace App\Models; | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | use App\Models\Concerns\Reportable; |  |  |  | use App\Models\Concerns\Reportable; | 
			
		
	
		
		
			
				
					
					|  |  |  | use App\Tables\Changeset; |  |  |  | use App\Tables\Changeset; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | use App\Tables\Column; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | use Illuminate\Database\Eloquent\Builder; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | use Illuminate\Support\Facades\DB; | 
			
		
	
		
		
			
				
					
					|  |  |  | use MightyPork\Exceptions\NotApplicableException; |  |  |  | use MightyPork\Exceptions\NotApplicableException; | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | /** |  |  |  | /** | 
			
		
	
	
		
		
			
				
					|  |  | @ -16,7 +19,7 @@ use MightyPork\Exceptions\NotApplicableException; | 
			
		
	
		
		
			
				
					
					|  |  |  |  * @property int $revision_id |  |  |  |  * @property int $revision_id | 
			
		
	
		
		
			
				
					
					|  |  |  |  * @property int $author_id |  |  |  |  * @property int $author_id | 
			
		
	
		
		
			
				
					
					|  |  |  |  * @property string $note |  |  |  |  * @property string $note | 
			
		
	
		
		
			
				
					
					|  |  |  |  * @property Proposal $changes - JSONB |  |  |  |  * @property Changeset $changeset - JSONB | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  * @property-read User $author |  |  |  |  * @property-read User $author | 
			
		
	
		
		
			
				
					
					|  |  |  |  * @property-read Table $table |  |  |  |  * @property-read Table $table | 
			
		
	
		
		
			
				
					
					|  |  |  |  * @property-read Revision $revision |  |  |  |  * @property-read Revision $revision | 
			
		
	
	
		
		
			
				
					|  |  | @ -46,17 +49,30 @@ class Proposal extends BaseModel | 
			
		
	
		
		
			
				
					
					|  |  |  |         return $this->belongsTo(Table::class); |  |  |  |         return $this->belongsTo(Table::class); | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     public function getChangesAttribute($value) |  |  |  |     public function scopeUnmerged(Builder $query, Table $table) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     { |  |  |  |     { | 
			
		
	
		
		
			
				
					
					|  |  |  |         $changeset = Changeset::fromObject(fromJSON($value)); |  |  |  |         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->revision = $this->revision; | 
			
		
	
		
		
			
				
					
					|  |  |  |         $changeset->table = $this->table; |  |  |  |         $changeset->table = $this->getAttribute('table'); | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         $changeset->note = $this->note; |  |  |  |         $changeset->note = $this->note; | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         return $changeset; |  |  |  |         return $changeset; | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     public function setChangesAttribute($value) |  |  |  |     public function setChangesetAttribute($value) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     { |  |  |  |     { | 
			
		
	
		
		
			
				
					
					|  |  |  |         if ($value instanceof Changeset) { |  |  |  |         if ($value instanceof Changeset) { | 
			
		
	
		
		
			
				
					
					|  |  |  |             $this->attributes['changes'] = toJSON($value->toObject()); |  |  |  |             $this->attributes['changes'] = toJSON($value->toObject()); | 
			
		
	
	
		
		
			
				
					|  |  | @ -98,10 +114,96 @@ class Proposal extends BaseModel | 
			
		
	
		
		
			
				
					
					|  |  |  |             // relations |  |  |  |             // relations | 
			
		
	
		
		
			
				
					
					|  |  |  |             'table_id' => $changeset->table->getKey(), |  |  |  |             'table_id' => $changeset->table->getKey(), | 
			
		
	
		
		
			
				
					
					|  |  |  |             'revision_id' => $changeset->revision->getKey(), |  |  |  |             'revision_id' => $changeset->revision->getKey(), | 
			
		
	
		
		
			
				
					
					|  |  |  |             'author_id' => \Auth::user()->getKey(), |  |  |  |             'author_id' => \user()->getKey(), | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             // the proposal info |  |  |  |             // the proposal info | 
			
		
	
		
		
			
				
					
					|  |  |  |             'note' => $changeset->note, |  |  |  |             'note' => $changeset->note, | 
			
		
	
		
		
			
				
					
					|  |  |  |             'changes' => $changeset->toObject(), |  |  |  |             '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_map(function(Column $c) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     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()]); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         }); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
	
		
		
			
				
					|  |  | 
 |