addresses = new ArrayCollection(); $this->members = new ArrayCollection(); $this->commentMembers = new CommentEmbeddable(); $this->compositions = new ArrayCollection(); } /** * @return $this */ public function addAddress(Address $address) { foreach ($this->getAddresses() as $a) { if ($a->getValidFrom() <= $address->getValidFrom() && $a->getValidTo() === null) { $a->setValidTo($address->getValidFrom()); } } $this->addresses[] = $address; 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 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 !== $membership; } ); } 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 { $criteria = new Criteria(); $expr = Criteria::expr(); $criteria->where( $expr->gte('startDate', $from) ); if (null !== $to) { $criteria->andWhere( $expr->orX( $expr->lte('endDate', $to), $expr->eq('endDate', null) ), ); } return $this->getMembers() ->matching($criteria); } 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 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 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; } }