project_id === $project->id, 404); abort_unless($request->user()->can('confirmCurrentRevision', $project), 403); $data = $request->validate([ 'lock_version' => ['required', 'integer', 'min:0'], ]); try { DB::transaction(function () use ($request, $project, $revision, $data): void { $locked = Project::query()->whereKey($project->id)->lockForUpdate()->firstOrFail(); if ((int) $locked->lock_version !== (int) $data['lock_version']) { throw new OptimisticLockConflictException('The project changed since you loaded it.'); } $lockedRevision = ProjectRevision::query()->whereKey($revision->id)->firstOrFail(); if ($lockedRevision->project_id !== $locked->id) { abort(404); } if (! $lockedRevision->isApproved()) { throw ValidationException::withMessages(['revision_id' => 'Only an approved revision can be confirmed as current.']); } if ($lockedRevision->files()->count() === 0) { throw ValidationException::withMessages(['revision_id' => 'An approved revision must have at least one file before it can be confirmed as current.']); } $previousId = $locked->current_revision_id; $locked->update([ 'current_revision_id' => $lockedRevision->id, 'lock_version' => $locked->lock_version + 1, ]); ProjectEvent::record($locked->id, 'current_revision_confirmed', $request->user()->id, ProjectRevision::class, $lockedRevision->id, [ 'previous_current_revision_id' => $previousId, 'new_current_revision_id' => $lockedRevision->id, ]); }); } catch (OptimisticLockConflictException $exception) { return back()->withErrors(['lock_version' => $exception->getMessage()])->setStatusCode(409); } return redirect()->route('projects.show', $project)->with('status', 'Current revision confirmed.'); } public function destroy(Request $request, Project $project): RedirectResponse { abort_unless($request->user()->can('confirmCurrentRevision', $project), 403); $data = $request->validate([ 'lock_version' => ['required', 'integer', 'min:0'], ]); try { DB::transaction(function () use ($request, $project, $data): void { $locked = Project::query()->whereKey($project->id)->lockForUpdate()->firstOrFail(); if ((int) $locked->lock_version !== (int) $data['lock_version']) { throw new OptimisticLockConflictException('The project changed since you loaded it.'); } $previousId = $locked->current_revision_id; $locked->update(['current_revision_id' => null, 'lock_version' => $locked->lock_version + 1]); ProjectEvent::record($locked->id, 'current_revision_cleared', $request->user()->id, metadata: [ 'previous_current_revision_id' => $previousId, 'new_current_revision_id' => null, ]); }); } catch (OptimisticLockConflictException $exception) { return back()->withErrors(['lock_version' => $exception->getMessage()])->setStatusCode(409); } return redirect()->route('projects.show', $project)->with('status', 'Current revision cleared.'); } }