addresses = new ArrayCollection(); $this->members = new ArrayCollection(); $this->commentMembers = new CommentEmbeddable(); $this->compositions = new ArrayCollection(); } public function addAddress(Address $address): self { if (!$this->addresses->contains($address)) { $this->addresses[] = $address; $this->makeAddressConsistent(); } return $this; } public function addComposition(HouseholdComposition $composition): self { if (!$this->compositions->contains($composition)) { $composition->setHousehold($this); $this->compositions[] = $composition; } $this->householdCompositionConsistency(); return $this; } public function addMember(HouseholdMember $member): self { if (!$this->members->contains($member)) { $this->members[] = $member; $member->setHousehold($this); } return $this; } /** * By default, the addresses are ordered by date, descending (the most * recent first). * * @Assert\Callback(methods={"validate"}) * * @return \Chill\MainBundle\Entity\Address[] */ public function getAddresses() { return $this->addresses; } public function getAddressesOrdered(): array { $addresses = $this->getAddresses()->toArray(); usort($addresses, static function (Address $a, Address $b) { $validFromA = $a->getValidFrom()->format('Y-m-d'); $validFromB = $b->getValidFrom()->format('Y-m-d'); if ($a === $b) { if (null === $a->getId()) { return 1; } if (null === $b->getId()) { return -1; } return $a->getId() <=> $b->getId(); } return $validFromA <=> $validFromB; }); return $addresses; } public function getCommentMembers(): CommentEmbeddable { return $this->commentMembers; } /** * @return ArrayCollection|Collection|HouseholdComposition[] */ public function getCompositions(): Collection { return $this->compositions; } /** * @Serializer\Groups({"read", "docgen:read"}) * @Serializer\SerializedName("current_address") */ public function getCurrentAddress(?DateTime $at = null): ?Address { $at = null === $at ? new DateTime('today') : $at; $addrs = $this->getAddresses()->filter(static function (Address $a) use ($at) { return $a->getValidFrom() <= $at && ( null === $a->getValidTo() || $a->getValidTo() > $at ); }); if ($addrs->count() > 0) { return $addrs->first(); } return null; } public function getCurrentComposition(?DateTimeImmutable $at = null): ?HouseholdComposition { $at ??= new DateTimeImmutable('today'); $criteria = new Criteria(); $expr = Criteria::expr(); $criteria->where( $expr->andX( $expr->orX( $expr->isNull('endDate'), $expr->gt('endDate', $at) ), $expr->lte('startDate', $at) ) ); $compositions = $this->compositions->matching($criteria); if ($compositions->count() > 0) { return $compositions->first(); } return null; } /** * @Serializer\Groups({"docgen:read"}) */ public function getCurrentMembers(?DateTimeImmutable $now = null): Collection { return $this->getMembers()->matching($this->buildCriteriaCurrentMembers($now)); } public function getCurrentMembersByPosition(Position $position, ?DateTimeInterface $now = null) { $criteria = new Criteria(); $expr = Criteria::expr(); $criteria->where($expr->eq('position', $position)); return $this->getCurrentMembers($now)->matching($criteria); } /** * get current members ids. * * Used in serialization * * @Serializer\Groups({"read"}) * @Serializer\SerializedName("current_members_id") */ public function getCurrentMembersIds(?DateTimeImmutable $now = null): Collection { return $this->getCurrentMembers($now)->map( static fn (HouseholdMember $m) => $m->getId() ); } /** * @return HouseholdMember[] */ public function getCurrentMembersOrdered(?DateTimeImmutable $now = null): Collection { $members = $this->getCurrentMembers($now); $members->getIterator() ->uasort( static function (HouseholdMember $a, HouseholdMember $b) { if ($a->getPosition() === null) { if ($b->getPosition() === null) { return 0; } return -1; } if ($b->getPosition() === null) { return 1; } if ($a->getPosition()->getOrdering() < $b->getPosition()->getOrdering()) { return -1; } if ($a->getPosition()->getOrdering() > $b->getPosition()->getOrdering()) { return 1; } if ($a->isHolder() && !$b->isHolder()) { return 1; } if (!$a->isHolder() && $b->isHolder()) { return -1; } return 0; } ); return $members; } public function getCurrentMembersWithoutPosition(?DateTimeInterface $now = null) { $criteria = new Criteria(); $expr = Criteria::expr(); $criteria->where($expr->isNull('position')); return $this->getCurrentMembers($now)->matching($criteria); } /** * Get the persons currently associated to the household. * * Return a list of Person, instead of a list of HouseholdMembers * * @return Person[] */ public function getCurrentPersons(?DateTimeImmutable $now = null): Collection { return $this->getCurrentMembers($now) ->map(static function (HouseholdMember $m) { return $m->getPerson(); }); } public function getId(): ?int { return $this->id; } /** * @return Collection|HouseholdMember[] */ public function getMembers(): Collection { return $this->members; } public function getMembersDuringMembership(HouseholdMember $membership) { return $this->getMembersOnRange( $membership->getStartDate(), $membership->getEndDate() )->filter( static function (HouseholdMember $m) use ($membership) { return $m->getPerson() !== $membership->getPerson(); } ); } public function getMembersHolder(): Collection { $criteria = new Criteria(); $expr = Criteria::expr(); $criteria->where( $expr->eq('holder', true) ); return $this->getMembers()->matching($criteria); } public function getMembersOnRange(DateTimeImmutable $from, ?DateTimeImmutable $to): Collection { return $this->getMembers()->filter(static function (HouseholdMember $m) use ($from, $to) { if (null === $m->getEndDate() && null !== $to) { return $m->getStartDate() <= $to; } if (null === $to) { return $m->getStartDate() >= $from || null === $m->getEndDate(); } if (null !== $m->getEndDate() && $m->getEndDate() < $from) { return false; } if ($m->getStartDate() <= $to) { return true; } return false; }); } public function getNonCurrentMembers(?DateTimeImmutable $now = null): Collection { $criteria = new Criteria(); $expr = Criteria::expr(); $date = null === $now ? (new DateTimeImmutable('today')) : $now; $criteria ->where( $expr->gt('startDate', $date) ) ->orWhere( $expr->andX( $expr->lte('endDate', $date), $expr->neq('endDate', null) ) ); return $this->getMembers()->matching($criteria); } public function getNonCurrentMembersByPosition(Position $position, ?DateTimeInterface $now = null) { $criteria = new Criteria(); $expr = Criteria::expr(); $criteria->where($expr->eq('position', $position)); return $this->getNonCurrentMembers($now)->matching($criteria); } public function getNonCurrentMembersWithoutPosition(?DateTimeInterface $now = null) { $criteria = new Criteria(); $expr = Criteria::expr(); $criteria->where($expr->isNull('position')); return $this->getNonCurrentMembers($now)->matching($criteria); } public function getPreviousAddressOf(Address $address): ?Address { $iterator = new ArrayIterator($this->getAddressesOrdered()); $iterator->rewind(); while ($iterator->valid()) { $current = $iterator->current(); $iterator->next(); if ($iterator->valid()) { if ($iterator->current() === $address) { return $current; } } } return null; } public function getWaitingForBirth(): bool { return $this->waitingForBirth; } public function getWaitingForBirthDate(): ?DateTimeImmutable { return $this->waitingForBirthDate; } /** * @internal */ public function householdCompositionConsistency(): void { $compositionOrdered = $this->compositions->toArray(); usort( $compositionOrdered, static function (HouseholdComposition $a, HouseholdComposition $b) { return $a->getStartDate() <=> $b->getStartDate(); } ); $iterator = new ArrayIterator($compositionOrdered); $iterator->rewind(); /** @var ?HouseholdComposition $previous */ $previous = null; do { /** @var ?HouseholdComposition $current */ $current = $iterator->current(); if (null !== $previous) { if (null === $previous->getEndDate() || $previous->getEndDate() > $current->getStartDate()) { $previous->setEndDate($current->getStartDate()); } } $previous = $current; $iterator->next(); } while ($iterator->valid()); } public function makeAddressConsistent(): void { $iterator = new ArrayIterator($this->getAddressesOrdered()); $iterator->rewind(); while ($iterator->valid()) { $current = $iterator->current(); $iterator->next(); if ($iterator->valid()) { $current->setValidTo($iterator->current()->getValidFrom()); } } } public function removeAddress(Address $address) { $this->addresses->removeElement($address); } public function removeComposition(HouseholdComposition $composition): self { if ($this->compositions->removeElement($composition)) { $composition->setHousehold(null); } return $this; } public function removeMember(HouseholdMember $member): self { if ($this->members->removeElement($member)) { // set the owning side to null (unless already changed) if ($member->getHousehold() === $this) { $member->setHousehold(null); } } return $this; } public function setCommentMembers(CommentEmbeddable $commentMembers): self { $this->commentMembers = $commentMembers; return $this; } /** * Force an address starting at the current day * on the Household. * * This will force the startDate's address on today. * * Used on household creation. * * @Serializer\Groups({"create"}) */ public function setForceAddress(Address $address) { $address->setValidFrom(new DateTime('today')); $this->addAddress($address); } public function setWaitingForBirth(bool $waitingForBirth): self { $this->waitingForBirth = $waitingForBirth; return $this; } public function setWaitingForBirthDate(?DateTimeImmutable $waitingForBirthDate): self { $this->waitingForBirthDate = $waitingForBirthDate; return $this; } public function validate(ExecutionContextInterface $context, $payload) { $addresses = $this->getAddresses(); $cond = true; for ($i = 0; count($addresses) - 1 > $i; ++$i) { if ($addresses[$i]->getValidFrom() !== $addresses[$i + 1]->getValidTo()) { $cond = false; $context->buildViolation('The address are not sequentials. The validFrom date of one address should be equal to the validTo date of the previous address.') ->atPath('addresses') ->addViolation(); } } } private function buildCriteriaCurrentMembers(?DateTimeImmutable $now = null): Criteria { $criteria = new Criteria(); $expr = Criteria::expr(); $date = null === $now ? (new DateTimeImmutable('today')) : $now; $criteria ->where($expr->orX( $expr->isNull('startDate'), $expr->lte('startDate', $date) )) ->andWhere($expr->orX( $expr->isNull('endDate'), $expr->gt('endDate', $date) )); return $criteria; } }