") * @Serializer\Expose() */ public $votes; /** * @ORM\OneToMany(targetEntity="App\Entity\StackOfVotes", mappedBy="poll", orphanRemoval=true, cascade={"persist", "remove"}) * @Serializer\Expose() */ public $stacksOfVotes; /** * @ORM\OneToMany(targetEntity="App\Entity\Choice", mappedBy="poll", orphanRemoval=true, cascade={"persist", "remove"}) * @Serializer\Expose() * @Serializer\Type("ArrayCollection") */ public $choices; /** * @ORM\OneToMany(targetEntity="App\Entity\Comment", mappedBy="poll", orphanRemoval=true, cascade={"persist", "remove"}) * @Serializer\Expose() * @Serializer\Type("ArrayCollection") */ public $comments; /** * number of days from now for default expiracy date * @var int * @Serializer\Expose() */ public $defaultExpiracyDaysFromNow = 60; /** * vote restricted by a password in md5 format * @ORM\Column(type="string", length=255, nullable=true) */ private $password; /** * used to allow administration * @ORM\Column(type="string", length=255) * @Serializer\Type("string") */ private $adminKey; private $maxChoicesLimit = 25; public function __construct() { $this->initiate(); $this->setCreatedAt(new DateTime()); $this->setAdminKey($this->generateRandomKey()); $this->votes = new ArrayCollection(); $this->stacksOfVotes = new ArrayCollection(); $this->choices = new ArrayCollection(); $this->comments = new ArrayCollection(); } private function initiate() { $this->votes = new ArrayCollection(); $this->stacksOfVotes = new ArrayCollection(); $this->choices = new ArrayCollection(); $this->comments = new ArrayCollection(); $this->setAdminKey($this->generateRandomKey()); $this->setCreatedAt(new DateTime()); $this->setExpiracyDate($this->addDaysToDate( new DateTime(), $this->defaultExpiracyDaysFromNow )); $this->setAllowedAnswers(['yes', 'maybe', 'no']); } public function setCreatedAt(DateTimeInterface $createdAt): self { $this->createdAt = $createdAt; return $this; } public function displayForAdmin() { $content = $this->display(); $content['owner'] = $this->getOwner()->displayForAdmin(); $content['admin_key'] = $this->getAdminKey(); $content['password_hash'] = $this->getPassword(); $content['id'] = $this->getId(); ksort($content); return $content; } // counts each number of answer for this choice public function display(): array { $computedAnswers = $this->computeAnswers(); $displayedStackOfVotes = []; foreach ($this->getStacksOfVotes() as $stack) { $displayedStackOfVotes[] = $stack->display(); } $displayedChoices = []; foreach ($this->getChoices() as $choice) { $displayedChoices[] = $choice->display($this->getKind()); } $displayedComments = []; foreach ($this->getComments() as $comment) { $displayedComments[] = $comment->display(); } $resp = [ 'allowed_answers' => $this->getAllowedAnswers(), 'choices' => $computedAnswers['answers'], 'choices_grouped' => $computedAnswers['grouped_dates'], 'choices_max' => $this->getChoicesMax(), 'comments' => $displayedComments, 'created_at' => $this->getCreatedAt()->format('c'), 'custom_url' => $this->getCustomUrl(), 'description' => $this->getDescription(), 'expiracy_date' => $this->getExpiracyDate()->format('c'), 'hide_results' => $this->getHideResults(), 'kind' => $this->getKind(), 'max_score' => $computedAnswers['max_score'], 'modification_policy' => $this->getModificationPolicy(), 'owner' => ['pseudo' => $this->getOwner()->getPseudo(),], 'password_protected' => $this->getPassword() ? 'yes' : 'no', 'show_results_even_if_password' => $this->getShowResultEvenIfPasswords(), 'stacks' => $displayedStackOfVotes, 'title' => $this->getTitle(), 'votes_allowed' => $this->getVotesAllowed(), 'votes_max' => $this->getVotesMax(), ]; ksort($resp); return $resp; } public function computeAnswers() { $computedArray = []; $grouped_dates = []; $maxScore = 0; if ($this->getKind() == 'date') { foreach ($this->getChoices() as $choice) { $boom = explode(' >>> ', $choice->getName()); // handle sub time slices if (count($boom) == 2) { if (!isset($grouped_dates[$boom[0]])) { $grouped_dates[$boom[0]] = [ "date_string" => $boom[0], "choices" => [], ]; } $grouped_dates[$boom[0]]["choices"][] = [ "choice_id" => $choice->getId(), "name" => $boom[1], ]; } elseif (count($boom) == 1) { $name = $choice->getName(); $grouped_dates[$name] = [ "date_string" => $name, "choices" => [], ]; $grouped_dates[$name]["choices"][] = [ "choice_id" => $choice->getId(), "name" => $name, ]; } } } elseif ($this->getKind() == 'text') { foreach ($this->getChoices() as $choice) { $name = $choice->getName(); if (!isset($grouped_dates[$name])) { $grouped_dates[$name] = [ "date_string" => $name, "choices" => [], ]; } $grouped_dates[$name]["choices"][] = [ "choice_id" => $choice->getId(), "name" => $name, ]; } } $scoreInfos = [ 'id' => null, 'name' => null, 'score' => 0, 'created_at' => null, 'yes' => [ 'count' => 0, 'people' => [], ], 'maybe' => [ 'count' => 0, 'people' => [], ], 'no' => [ 'count' => 0, 'people' => [], ], ]; // first, prefill all choices foreach ($this->getChoices() as $choice) { $computedArray[$choice->getId()] = array_merge($scoreInfos, $choice->display($this->getKind())); } // then, compute stack of votes scores on each choice foreach ($this->getStacksOfVotes() as $stack_of_vote) { foreach ($stack_of_vote->getVotes() as $vote) { $answer = $vote->getValue(); $choice_id = $vote->getChoice()->getId(); $choice_url = $vote->getChoice()->getUrl(); if (!isset($computedArray[$choice_id])) { $computedArray[$choice_id] = [ 'id' => $choice_id, 'url' => $choice_url, 'name' => $vote->getChoice()->getName(), 'score' => 0, 'yes' => [ 'count' => 0, 'people' => [], ], 'maybe' => [ 'count' => 0, 'people' => [], ], 'no' => [ 'count' => 0, 'people' => [], ], ]; } $computedArray[$choice_id][$answer]['count']++; $computedArray[$choice_id][$answer]['people'][] = $stack_of_vote->getOwner()->getPseudo(); if ($answer == 'yes') { $computedArray[$choice_id]['score'] += 1; } elseif ($answer == 'maybe') { $computedArray[$choice_id]['score'] += 0.5; } // compare with max value if ($computedArray[$choice_id]['score'] > $maxScore) { $maxScore = $computedArray[$choice_id]['score']; } } } $answersWithStats = []; foreach ($computedArray as $choice_stat) { $answersWithStats[] = $choice_stat; } $groupsOfDates = []; foreach ($grouped_dates as $group) { $ii = 0; foreach ($group["choices"] as $slice) { $slice['score'] = $computedArray[$slice['choice_id']]['score']; $slice['yes'] = $computedArray[$slice['choice_id']]['yes']; $slice['maybe'] = $computedArray[$slice['choice_id']]['maybe']; $slice['no'] = $computedArray[$slice['choice_id']]['no']; $slice['id'] = $slice['choice_id']; $group["choices"][$ii] = $slice; $ii++; } $groupsOfDates[] = $group; } return [ 'answers' => $answersWithStats, 'grouped_dates' => $groupsOfDates, 'max_score' => $maxScore, ]; } /** * @return Collection|Choice[] */ public function getChoices(): Collection { return $this->choices; } public function getKind(): ?string { return $this->kind; } public function setKind(string $kind): self { $this->kind = $kind; return $this; } public function getStacksOfVotes() { return $this->stacksOfVotes; } public function setStacksOfVotes(?StackOfVotes $stacksOfVotes): self { $this->stacksOfVotes = $stacksOfVotes; return $this; } /** * @return Collection|Comment[] */ public function getComments(): Collection { return $this->comments; } public function getTitle(): ?string { return $this->title; } public function setTitle(string $title): self { $this->title = $title; return $this; } public function getDescription(): ?string { return $this->description; } public function setDescription(string $description): self { $this->description = $description; return $this; } public function getCreatedAt(): ?DateTimeInterface { return $this->createdAt; } public function getExpiracyDate(): ?DateTimeInterface { return $this->expiracyDate; } public function setExpiracyDate(DateTimeInterface $expiracyDate): self { $this->expiracyDate = $expiracyDate; return $this; } public function getVotesMax() { return $this->votesMax; } public function setVotesMax($votesMax): self { $this->votesMax = $votesMax; return $this; } public function getChoicesMax() { return $this->choicesMax; } public function setChoicesMax($choicesMax): self { $this->choicesMax = $choicesMax; return $this; } public function getAllowedAnswers(): ?array { return $this->allowedAnswers; } public function setAllowedAnswers(array $allowedAnswers): self { if (!count($allowedAnswers)) { $this->allowedAnswers = ['yes']; } else { $this->allowedAnswers = $allowedAnswers; } return $this; } public function getVotesAllowed(): ?bool { return $this->votesAllowed; } public function setVotesAllowed(?bool $votesAllowed): self { $this->votesAllowed = $votesAllowed; return $this; } public function getModificationPolicy(): ?string { return $this->modificationPolicy; } public function setModificationPolicy(string $modificationPolicy): self { $this->modificationPolicy = $modificationPolicy; return $this; } public function getHideResults(): ?bool { return $this->hideResults; } public function setHideResults(bool $hideResults): self { $this->hideResults = $hideResults; return $this; } public function getShowResultEvenIfPasswords(): ?bool { return $this->showResultEvenIfPasswords; } public function setShowResultEvenIfPasswords(bool $showResultEvenIfPasswords): self { $this->showResultEvenIfPasswords = $showResultEvenIfPasswords; return $this; } public function getOwner(): ?Owner { return $this->owner; } public function setOwner(?Owner $owner): self { $this->owner = $owner; return $this; } public function getPassword(): ?string { return $this->password; } public function setPassword(string $password): self { $this->password = md5($password); return $this; } public function getAdminKey(): ?string { return $this->adminKey; } public function setAdminKey(string $adminKey): self { $this->adminKey = $adminKey; return $this; } public function getId(): ?int { return $this->id; } public function findChoiceById(int $id) { $choices = $this->getChoices(); $counter = 0; // there must be something cleaner than this in Doctrine ArrayCollection foreach ($choices as $choice) { $counter++; if ($counter > $this->maxChoicesLimit) { throw new ErrorException("max number of choices reached for this poll"); } if ($choice && $choice->getId() == $id) { return $choice; } } return null; } /** * @return Collection|Vote[] */ public function getVotes(): Collection { return $this->votes; } public function getCustomUrl(): ?string { return $this->customUrl; } public function setCustomUrl(string $customUrl): self { $this->customUrl = $customUrl; return $this; } public function getMailOnComment(): ?bool { return $this->mailOnComment; } public function setMailOnComment(bool $mailOnComment): self { $this->mailOnComment = $mailOnComment; return $this; } public function getMailOnVote(): ?bool { return $this->mailOnVote; } public function setMailOnVote(bool $mailOnVote): self { $this->mailOnVote = $mailOnVote; return $this; } public function addComment(Comment $comment): self { if (!$this->comments->contains($comment)) { $this->comments[] = $comment; $comment->setPoll($this); } return $this; } public function removeComment(Comment $comment): self { if ($this->comments->contains($comment)) { $this->comments->removeElement($comment); // set the owning side to null (unless already changed) if ($comment->getPoll() === $this) { $comment->setPoll(null); } } return $this; } public function addStackOfVote(StackOfVotes $stackOfVote): self { if (!$this->stacksOfVotes->contains($stackOfVote)) { $this->stacksOfVotes[] = $stackOfVote; $stackOfVote->setPoll($this); } return $this; } public function removeStackOfVote(StackOfVotes $stackOfVote): self { if ($this->stacksOfVotes->contains($stackOfVote)) { $this->stacksOfVotes->removeElement($stackOfVote); // set the owning side to null (unless already changed) if ($stackOfVote->getPoll() === $this) { $stackOfVote->setPoll(null); } } return $this; } public function addVote(Vote $vote): self { if (!$this->votes->contains($vote)) { $this->votes[] = $vote; $vote->setPoll($this); } return $this; } public function removeVote(Vote $vote): self { if ($this->votes->contains($vote)) { $this->votes->removeElement($vote); // set the owning side to null (unless already changed) if ($vote->getPoll() === $this) { $vote->setPoll(null); } } return $this; } public function addTextChoiceArray(array $choiceTextArray): self { foreach ($choiceTextArray as $text) { $newChoice = new Choice(); $newChoice->setName($text); $this->addChoice($newChoice); } return $this; } public function addChoice(Choice $choice): self { if (!is_null($this->choices)) { if (!$this->choices->contains($choice)) { $this->choices[] = $choice; $choice->setPoll($this); } } else { $this->choices[] = $choice; $choice->setPoll($this); } return $this; } public function addStacksOfVote(StackOfVotes $stacksOfVote): self { if (!$this->stacksOfVotes->contains($stacksOfVote)) { $this->stacksOfVotes[] = $stacksOfVote; $stacksOfVote->setPoll($this); } return $this; } public function removeStacksOfVote(StackOfVotes $stacksOfVote): self { if ($this->stacksOfVotes->contains($stacksOfVote)) { $this->stacksOfVotes->removeElement($stacksOfVote); // set the owning side to null (unless already changed) if ($stacksOfVote->getPoll() === $this) { $stacksOfVote->setPoll(null); } } return $this; } public function removeChoice(Choice $choice): self { if ($this->choices->contains($choice)) { $this->choices->removeElement($choice); // set the owning side to null (unless already changed) if ($choice->getPoll() === $this) { $choice->setPoll(null); } } return $this; } public function getCommentsAllowed(): ?bool { return $this->commentsAllowed; } public function setCommentsAllowed(?bool $commentsAllowed): self { $this->commentsAllowed = $commentsAllowed; return $this; } public function getIsZeroKnowledge(): ?bool { return $this->isZeroKnowledge; } public function setIsZeroKnowledge(?bool $isZeroKnowledge): self { $this->isZeroKnowledge = $isZeroKnowledge; return $this; } }