Skip to content

Commit f620013

Browse files
committed
Updating exercise file link interactions with other entities and updating exercise file replacements (to correctly preserve links).
1 parent 8364d97 commit f620013

File tree

5 files changed

+38
-18
lines changed

5 files changed

+38
-18
lines changed

app/V1Module/presenters/ExerciseFilesPresenter.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ public function actionUploadExerciseFiles(string $id)
126126

127127
$files = $this->uploadedFiles->findAllById($this->getRequest()->getPost("files"));
128128
$currentFiles = [];
129+
$filesToRemove = [];
129130
$totalFileSize = 0;
130131

131132
/** @var ExerciseFile $file */
@@ -145,7 +146,7 @@ public function actionUploadExerciseFiles(string $id)
145146
if (array_key_exists($file->getName(), $currentFiles)) {
146147
/** @var ExerciseFile $currentFile */
147148
$currentFile = $currentFiles[$file->getName()];
148-
$exercise->getExerciseFiles()->removeElement($currentFile);
149+
$filesToRemove[$file->getName()] = $currentFile;
149150
$totalFileSize -= $currentFile->getFileSize();
150151
} else {
151152
$totalFileCount += 1;
@@ -174,6 +175,20 @@ public function actionUploadExerciseFiles(string $id)
174175
foreach ($files as $file) {
175176
$hash = $this->fileStorage->storeUploadedExerciseFile($file);
176177
$exerciseFile = ExerciseFile::fromUploadedFileAndExercise($file, $exercise, $hash);
178+
if (array_key_exists($file->getName(), $filesToRemove)) {
179+
/** @var ExerciseFile $currentFile */
180+
$fileToRemove = $filesToRemove[$file->getName()];
181+
182+
// move links from old file to the new one
183+
$links = $this->fileLinks->findBy(["exerciseFile" => $fileToRemove, "exercise" => $exercise]);
184+
foreach ($links as $link) {
185+
$link->setExerciseFile($exerciseFile);
186+
$this->fileLinks->persist($link, false);
187+
}
188+
189+
$exercise->getExerciseFiles()->removeElement($fileToRemove);
190+
}
191+
177192
$this->uploadedFiles->persist($exerciseFile, false);
178193
$this->uploadedFiles->remove($file, false);
179194
}

app/model/entity/ExerciseFile.php

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,6 @@ function (Assignment $assignment) {
6363
*/
6464
protected $pipelines;
6565

66-
/**
67-
* @ORM\OneToMany(targetEntity="ExerciseFileLink", mappedBy="exerciseFile")
68-
* @var Collection<ExerciseFileLink>
69-
*/
70-
protected $links;
71-
72-
7366
/**
7467
* ExerciseFile constructor.
7568
* @param string $name
@@ -95,7 +88,6 @@ public function __construct(
9588
$this->exercises = new ArrayCollection();
9689
$this->assignments = new ArrayCollection();
9790
$this->pipelines = new ArrayCollection();
98-
$this->links = new ArrayCollection();
9991

10092
if ($exercise) {
10193
$this->exercises->add($exercise);

app/model/entity/ExerciseFileLink.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88

99
/**
1010
* Additional identification for files that are accessible to users via stable URLs.
11-
* The entity always points to a single ExerciseFile, but also follows the CoW principle used for all
12-
* exercise-assignment records. I.e., when Assignment is created from Exercise, the links are copied
13-
* as well, but they still point to the same ExerciseFile entities.
11+
* The entity always points to a single ExerciseFile, but also follows the principles used for all
12+
* exercise-assignment records. However, links are small, so they are copied eagerly (not by CoW).
13+
* I.e., when Assignment is created from Exercise, the links are copied (immediately) as well.
14+
* The link of an exercise may be updated, but the link of an assignment is immutable.
1415
* @ORM\Entity
1516
* @ORM\Table(uniqueConstraints={@ORM\UniqueConstraint(columns={"key", "exercise_id"})})
1617
*/
@@ -45,19 +46,21 @@ class ExerciseFileLink implements JsonSerializable
4546
*/
4647
protected $requiredRole;
4748

48-
4949
/**
50-
* @ORM\ManyToOne(targetEntity="ExerciseFile", inversedBy="links", cascade={"persist", "remove"})
50+
* @ORM\ManyToOne(targetEntity="ExerciseFile")
51+
* @ORM\JoinColumn(onDelete="CASCADE")
5152
*/
5253
protected $exerciseFile;
5354

5455
/**
5556
* @ORM\ManyToOne(targetEntity="Exercise", inversedBy="fileLinks")
57+
* @ORM\JoinColumn(onDelete="CASCADE")
5658
*/
5759
protected $exercise;
5860

5961
/**
6062
* @ORM\ManyToOne(targetEntity="Assignment", inversedBy="fileLinks")
63+
* @ORM\JoinColumn(onDelete="CASCADE")
6164
*/
6265
protected $assignment;
6366

@@ -177,6 +180,16 @@ public function getExerciseFile(): ExerciseFile
177180
return $this->exerciseFile;
178181
}
179182

183+
/**
184+
* This is a rare case where we allow changing the linked ExerciseFile.
185+
* It is used only when the immutable ExerciseFile is being replaced.
186+
* Changing the link to point to a different ExerciseFile should not be allowed otherwise.
187+
*/
188+
public function setExerciseFile(ExerciseFile $exerciseFile): void
189+
{
190+
$this->exerciseFile = $exerciseFile;
191+
}
192+
180193
public function getExercise(): ?Exercise
181194
{
182195
return $this->exercise;

app/model/entity/base/ExerciseData.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ function (AttachmentFile $file) {
365365
}
366366

367367
/**
368-
* @ORM\OneToMany(targetEntity="ExerciseFileLink", mappedBy="exerciseFile", cascade={"persist", "remove"})
368+
* @ORM\OneToMany(targetEntity="ExerciseFileLink", mappedBy="exerciseFile", cascade={"persist"})
369369
* @var Collection<ExerciseFileLink>
370370
*/
371371
protected $fileLinks;

migrations/Version20251114174621.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ public function up(Schema $schema): void
2121
{
2222
// this up() migration is auto-generated, please modify it to your needs
2323
$this->addSql('CREATE TABLE exercise_file_link (id CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', exercise_file_id CHAR(36) DEFAULT NULL COMMENT \'(DC2Type:uuid)\', exercise_id CHAR(36) DEFAULT NULL COMMENT \'(DC2Type:uuid)\', assignment_id CHAR(36) DEFAULT NULL COMMENT \'(DC2Type:uuid)\', `key` VARCHAR(16) NOT NULL, save_name VARCHAR(255) DEFAULT NULL, required_role VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL, INDEX IDX_1187F77549DE8E29 (exercise_file_id), INDEX IDX_1187F775E934951A (exercise_id), INDEX IDX_1187F775D19302F8 (assignment_id), UNIQUE INDEX UNIQ_1187F7758A90ABA9E934951A (`key`, exercise_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
24-
$this->addSql('ALTER TABLE exercise_file_link ADD CONSTRAINT FK_1187F77549DE8E29 FOREIGN KEY (exercise_file_id) REFERENCES `uploaded_file` (id)');
25-
$this->addSql('ALTER TABLE exercise_file_link ADD CONSTRAINT FK_1187F775E934951A FOREIGN KEY (exercise_id) REFERENCES exercise (id)');
26-
$this->addSql('ALTER TABLE exercise_file_link ADD CONSTRAINT FK_1187F775D19302F8 FOREIGN KEY (assignment_id) REFERENCES assignment (id)');
24+
$this->addSql('ALTER TABLE exercise_file_link ADD CONSTRAINT FK_1187F775D19302F8 FOREIGN KEY (assignment_id) REFERENCES assignment (id) ON DELETE CASCADE');
25+
$this->addSql('ALTER TABLE exercise_file_link ADD CONSTRAINT FK_1187F775E934951A FOREIGN KEY (exercise_id) REFERENCES exercise (id) ON DELETE CASCADE');
26+
$this->addSql('ALTER TABLE exercise_file_link ADD CONSTRAINT FK_1187F77549DE8E29 FOREIGN KEY (exercise_file_id) REFERENCES `uploaded_file` (id) ON DELETE CASCADE');
2727
$this->addSql('ALTER TABLE uploaded_file DROP is_public');
2828
}
2929

0 commit comments

Comments
 (0)