false])] public bool $reusable = false; /** @var Collection */ #[Map(if: false)] #[ORM\ManyToMany(targetEntity: QuestionLabel::class, inversedBy: 'bankQuestions')] public private(set) Collection $labels; /** @var Collection */ #[Map(if: false)] #[ORM\OneToMany(targetEntity: BankAnswer::class, mappedBy: 'bankQuestion', cascade: ['persist'], orphanRemoval: true)] #[ORM\OrderBy(['ordering' => 'ASC'])] public private(set) Collection $answers; /** @var Collection */ #[Map(if: false)] #[ORM\OneToMany(targetEntity: BankQuestionUsage::class, mappedBy: 'bankQuestion', cascade: ['persist'], orphanRemoval: true)] public private(set) Collection $usages; public function __construct() { $this->labels = new ArrayCollection(); $this->answers = new ArrayCollection(); $this->usages = new ArrayCollection(); } public function addAnswer(BankAnswer $answer): static { if (!$this->answers->contains($answer)) { $this->answers->add($answer); $answer->bankQuestion = $this; } return $this; } public function removeAnswer(BankAnswer $answer): static { $this->answers->removeElement($answer); return $this; } public function addLabel(QuestionLabel $label): static { if (!$this->labels->contains($label)) { $this->labels->add($label); } return $this; } public function removeLabel(QuestionLabel $label): static { $this->labels->removeElement($label); return $this; } public function addUsage(BankQuestionUsage $usage): static { if (!$this->usages->contains($usage)) { $this->usages->add($usage); } return $this; } public bool $isUsed { get => !$this->usages->isEmpty(); } public bool $canBeAssigned { get => $this->reusable || !$this->isUsed; } /** True when the question is fully complete and can be assigned to a quiz. */ public bool $isCompleteForQuiz { get => $this->answers->count() >= 2 && 1 === $this->answers->filter(static fn (BankAnswer $answer): bool => $answer->isRightAnswer)->count(); } public function isUsedInQuiz(Quiz $quiz): bool { return $this->usages->exists(static fn (int $key, BankQuestionUsage $usage): bool => $usage->quiz === $quiz); } #[Assert\Callback] public function validateAnswers(ExecutionContextInterface $context): void { if ($this->answers->isEmpty()) { return; } $this->answers->filter(static fn (BankAnswer $answer): bool => $answer->isRightAnswer)->count(); if ($this->answers->count() < 2) { $context->buildViolation('A question needs at least two answers') ->atPath('answers') ->addViolation(); } } public function __toString(): string { return $this->question; } }