datatable.directory codebase
				https://datatable.directory/
			
			
		
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							210 lines
						
					
					
						
							7.1 KiB
						
					
					
				
			
		
		
	
	
							210 lines
						
					
					
						
							7.1 KiB
						
					
					
				| <?php
 | |
| 
 | |
| namespace App\Models;
 | |
| 
 | |
| use App\Models\Concerns\Reportable;
 | |
| use App\Tables\Changeset;
 | |
| use App\Tables\Column;
 | |
| use Illuminate\Database\Eloquent\Builder;
 | |
| use Illuminate\Support\Facades\DB;
 | |
| use MightyPork\Exceptions\NotApplicableException;
 | |
| 
 | |
| /**
 | |
|  * Change proposal
 | |
|  *
 | |
|  * @property int $id
 | |
|  * @property \Carbon\Carbon $created_at
 | |
|  * @property \Carbon\Carbon $updated_at
 | |
|  * @property int $table_id
 | |
|  * @property int $revision_id
 | |
|  * @property int $author_id
 | |
|  * @property string $note
 | |
|  * @property Changeset $changeset - JSONB
 | |
|  * @property-read User $author
 | |
|  * @property-read Table $table
 | |
|  * @property-read Revision $revision
 | |
|  */
 | |
| class Proposal extends BaseModel
 | |
| {
 | |
|     use Reportable;
 | |
|     protected $guarded = [];
 | |
| 
 | |
|     protected $touches = ['author', 'table'];
 | |
| 
 | |
|     /** Authoring user */
 | |
|     public function author()
 | |
|     {
 | |
|         return $this->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_values(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()]);
 | |
|         });
 | |
|     }
 | |
| }
 | |
| 
 |