accompanyingPeriodParticipations = new ArrayCollection(); $this->spokenLanguages = new ArrayCollection(); $this->addresses = new ArrayCollection(); $this->altNames = new ArrayCollection(); $this->otherPhoneNumbers = new ArrayCollection(); $this->householdParticipations = new ArrayCollection(); $this->householdAddresses = new ArrayCollection(); $this->genderComment = new CommentEmbeddable(); $this->maritalStatusComment = new CommentEmbeddable(); $this->periodLocatedOn = new ArrayCollection(); $this->accompanyingPeriodRequested = new ArrayCollection(); } /** * This private function scan accompanyingPeriodParticipations Collection, * searching for a given AccompanyingPeriod */ private function participationsContainAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod): ?AccompanyingPeriodParticipation { foreach ($this->accompanyingPeriodParticipations as $participation) { /** @var AccompanyingPeriodParticipation $participation */ if ($accompanyingPeriod === $participation->getAccompanyingPeriod()) { return $participation; }} return null; } /** * This public function is the same but return only true or false */ public function containsAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod): bool { return ($this->participationsContainAccompanyingPeriod($accompanyingPeriod)) ? false : true; } /** * Add AccompanyingPeriodParticipation * * @uses AccompanyingPeriod::addPerson */ public function addAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod): self { $participation = new AccompanyingPeriodParticipation($accompanyingPeriod, $this); $this->accompanyingPeriodParticipations->add($participation); return $this; } /** * Remove AccompanyingPeriod */ public function removeAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod) : void { $participation = $this->participationsContainAccompanyingPeriod($accompanyingPeriod); if (! null === $participation) { $participation->setEndDate(\DateTimeImmutable::class); $this->accompanyingPeriodParticipations->removeElement($participation); } } /** * set the Person file as open at the given date. * * For updating a opening's date, you should update AccompanyingPeriod instance * directly. * * For closing a file, @see this::close * * To check if the Person and its accompanying period is consistent, use validation. */ public function open(AccompanyingPeriod $accompanyingPeriod) : void { $this->proxyAccompanyingPeriodOpenState = true; $this->addAccompanyingPeriod($accompanyingPeriod); } /** * Set the Person file as closed at the given date. * * For update a closing date, you should update AccompanyingPeriod instance * directly. * * To check if the Person and its accompanying period are consistent, use validation. * * @throws \Exception if two lines of the accompanying period are open. */ public function close(AccompanyingPeriod $accompanyingPeriod = null) : void { $this->proxyAccompanyingPeriodOpenState = false; } /** * Return the opened accompanying period. */ public function getOpenedAccompanyingPeriod() : ?AccompanyingPeriod { if ($this->isOpen() === false) { return null; } foreach ($this->accompanyingPeriodParticipations as $participation) { /** @var AccompanyingPeriodParticipation $participation */ if ($participation->getAccompanyingPeriod()->isOpen()) { return $participation->getAccompanyingPeriod(); } } return null; } /** * Returns the opened accompanying period. * * @deprecated since 1.1 use `getOpenedAccompanyingPeriod instead */ public function getCurrentAccompanyingPeriod() : ?AccompanyingPeriod { return $this->getOpenedAccompanyingPeriod(); } /** * Get AccompanyingPeriods array */ public function getAccompanyingPeriods(): array { $accompanyingPeriods = []; foreach ($this->accompanyingPeriodParticipations as $participation) { /** @var AccompanyingPeriodParticipation $participation */ $accompanyingPeriods[] = $participation->getAccompanyingPeriod(); } return $accompanyingPeriods; } /** * Get current accompanyingPeriods array */ public function getCurrentAccompanyingPeriods(): array { $currentAccompanyingPeriods = []; $currentDate = new DateTime(); foreach ($this->accompanyingPeriodParticipations as $participation) { $endDate = $participation->getEndDate(); if ($endDate === null || $endDate > $currentDate){ $currentAccompanyingPeriods[] = $participation->getAccompanyingPeriod(); } } return $currentAccompanyingPeriods; } /** * Get AccompanyingPeriodParticipations Collection * * @return AccompanyingPeriodParticipation[]|Collection */ public function getAccompanyingPeriodParticipations(): Collection { return $this->accompanyingPeriodParticipations; } /** * Return a collection of participation, where the participation * is still opened, not a draft, and the period is still opened * * @return AccompanyingPeriodParticipation[]|Collection */ public function getOpenedParticipations(): Collection { // create a criteria for filtering easily $criteria = Criteria::create(); $criteria ->andWhere(Criteria::expr()->eq('endDate', NULL)) ->orWhere(Criteria::expr()->gt('endDate', new \DateTime('now'))) ; return $this->getAccompanyingPeriodParticipations() ->matching($criteria) ->filter(function (AccompanyingPeriodParticipation $app) { $period = $app->getAccompanyingPeriod(); return ( NULL === $period->getClosingDate() || new \DateTime('now') < $period->getClosingDate() ) && AccompanyingPeriod::STEP_DRAFT !== $period->getStep(); }); } /** * Get the accompanying periods of a give person with the chronological order. */ public function getAccompanyingPeriodsOrdered(): array { $periods = $this->getAccompanyingPeriods(); //order by date : usort($periods, function($a, $b) { $dateA = $a->getOpeningDate(); $dateB = $b->getOpeningDate(); if ($dateA == $dateB) { $dateEA = $a->getClosingDate(); $dateEB = $b->getClosingDate(); if ($dateEA == $dateEB) { return 0; } if ($dateEA < $dateEB) { return -1; } else { return +1; } } if ($dateA < $dateB) { return -1 ; } else { return 1; } }); return $periods; } /** * Check if the person is opened */ public function isOpen() : bool { foreach ($this->getAccompanyingPeriods() as $period) { if ($period->isOpen()) { return true; } } return false; } public function getId(): ?int { return $this->id; } /** * Set firstName * * @param string $firstName * @return Person */ public function setFirstName($firstName) { $this->firstName = $firstName; return $this; } /** * Get firstName * * @return string */ public function getFirstName() { return $this->firstName; } /** * Set lastName * * @param string $lastName * @return Person */ public function setLastName($lastName) { $this->lastName = $lastName; return $this; } /** * Get lastName * * @return string */ public function getLastName() { return $this->lastName; } /** * @return Collection */ public function getAltNames(): Collection { return $this->altNames; } /** * @param Collection $altNames * @return $this */ public function setAltNames(Collection $altNames) { $this->altNames = $altNames; return $this; } /** * @param PersonAltName $altName * @return $this */ public function addAltName(PersonAltName $altName) { if (FALSE === $this->altNames->contains($altName)) { $this->altNames->add($altName); $altName->setPerson($this); } return $this; } /** * @param PersonAltName $altName * @return $this */ public function removeAltName(PersonAltName $altName) { if ($this->altNames->contains($altName)) { $altName->setPerson(null); $this->altNames->removeElement($altName); } return $this; } /** * Set birthdate * * @param \DateTime $birthdate * @return Person */ public function setBirthdate($birthdate) { $this->birthdate = $birthdate; return $this; } /** * Get birthdate * * @return \DateTime */ public function getBirthdate() { return $this->birthdate; } /** * Return the age of a person, calculated at the date 'now'. * * If the person has a deathdate, calculate the age at the deathdate. * * @param string $at A valid string to create a DateTime. */ public function getAge(string $at = 'now'): ?int { if ($this->birthdate instanceof \DateTimeInterface) { if ($this->deathdate instanceof \DateTimeInterface) { return (int) date_diff($this->birthdate, $this->deathdate)->format('%y'); } return (int) date_diff($this->birthdate, date_create($at))->format('%y'); } return null; } /** * Set placeOfBirth * * @param string $placeOfBirth * @return Person */ public function setPlaceOfBirth($placeOfBirth) { if ($placeOfBirth === null) { $placeOfBirth = ''; } $this->placeOfBirth = $placeOfBirth; return $this; } /** * Get placeOfBirth * * @return string */ public function getPlaceOfBirth() { return $this->placeOfBirth; } /** * Set gender * * @param string $gender * @return Person */ public function setGender($gender) { $this->gender = $gender; return $this; } /** * Get gender * * @return string */ public function getGender() { return $this->gender; } /** * return gender as a Numeric form. * This is used for translations * @return int * @deprecated Keep for legacy. Used in Chill 1.5 for feminize before icu translations */ public function getGenderNumeric() { switch ($this->getGender()) { case self::FEMALE_GENDER: return 1; case self::MALE_GENDER: return 0; case self::BOTH_GENDER: return 2; default: return -1; } } /** * Set memo * * @param string $memo * @return Person */ public function setMemo($memo) { if ($memo === null) { $memo = ''; } if ($this->memo !== $memo) { $this->memo = $memo; } return $this; } /** * Get memo * * @return string */ public function getMemo() { return $this->memo; } /** * Set maritalStatus * * @param MaritalStatus $maritalStatus * @return Person */ public function setMaritalStatus(MaritalStatus $maritalStatus = null) { $this->maritalStatus = $maritalStatus; return $this; } /** * Get maritalStatus * * @return MaritalStatus */ public function getMaritalStatus() { return $this->maritalStatus; } /** * Set civility * * @param Civility $civility * @return Person */ public function setCivility(Civility $civility = null) { $this->civility = $civility; return $this; } /** * Get civility * * @return Civility */ public function getCivility() { return $this->civility; } /** * Set contactInfo * * @param string $contactInfo * @return Person */ public function setcontactInfo($contactInfo) { if ($contactInfo === null) { $contactInfo = ''; } $this->contactInfo = $contactInfo; return $this; } /** * Get contactInfo * * @return string */ public function getcontactInfo() { return $this->contactInfo; } /** * Set email * * @param string $email * @return Person */ public function setEmail($email) { if ($email === null) { $email = ''; } $this->email = $email; return $this; } /** * Get email * * @return string */ public function getEmail() { return $this->email; } /** * Set countryOfBirth * * @param Chill\MainBundle\Entity\Country $countryOfBirth * @return Person */ public function setCountryOfBirth(Country $countryOfBirth = null) { $this->countryOfBirth = $countryOfBirth; return $this; } /** * Get countryOfBirth * * @return Chill\MainBundle\Entity\Country */ public function getCountryOfBirth() { return $this->countryOfBirth; } /** * Set nationality * * @param Chill\MainBundle\Entity\Country $nationality * @return Person */ public function setNationality(Country $nationality = null) { $this->nationality = $nationality; return $this; } /** * Get nationality * * @return Country */ public function getNationality(): ?Country { return $this->nationality; } /** * @return string */ public function getLabel() { return $this->getFirstName()." ".$this->getLastName(); } /** * Get center * * @return Center */ public function getCenter() { return $this->center; } /** * Set the center * * @param Center $center * @return \Chill\PersonBundle\Entity\Person */ public function setCenter(Center $center) { $this->center = $center; return $this; } /** * Set cFData * * @param array $cFData * * @return Report */ public function setCFData($cFData) { $this->cFData = $cFData; return $this; } /** * Get cFData * * @return array */ public function getCFData() { if ($this->cFData === null) { $this->cFData = []; } return $this->cFData; } /** * Set phonenumber * * @param string $phonenumber * @return Person */ public function setPhonenumber(?string $phonenumber = '') { $this->phonenumber = (string) $phonenumber; return $this; } /** * Get phonenumber * * @return string */ public function getPhonenumber(): string { return $this->phonenumber; } /** * Set mobilenumber * * @param string $mobilenumber * @return Person */ public function setMobilenumber(?string $mobilenumber = '') { $this->mobilenumber = (string) $mobilenumber; return $this; } /** * Get mobilenumber * * @return string */ public function getMobilenumber(): string { return $this->mobilenumber; } /** * @return Collection */ public function getOtherPhoneNumbers(): Collection { return $this->otherPhoneNumbers; } /** * @param Collection $otherPhoneNumbers * @return $this */ public function setOtherPhoneNumbers(Collection $otherPhoneNumbers) { $this->otherPhoneNumbers = $otherPhoneNumbers; return $this; } /** * @param PersonPhone $otherPhoneNumber * @return $this */ public function addOtherPhoneNumber(PersonPhone $otherPhoneNumber) { if (false === $this->otherPhoneNumbers->contains($otherPhoneNumber)) { $otherPhoneNumber->setPerson($this); $this->otherPhoneNumbers->add($otherPhoneNumber); } return $this; } /** * @param PersonPhone $otherPhoneNumber * @return $this */ public function removeOtherPhoneNumber(PersonPhone $otherPhoneNumber) { if ($this->otherPhoneNumbers->contains($otherPhoneNumber)) { $this->otherPhoneNumbers->removeElement($otherPhoneNumber); } return $this; } /** * @return string */ public function __toString() { return $this->getLabel(); } /** * Set spokenLanguages * * @param type $spokenLanguages * @return Person */ public function setSpokenLanguages($spokenLanguages) { $this->spokenLanguages = $spokenLanguages; return $this; } /** * Get spokenLanguages * * @return ArrayCollection */ public function getSpokenLanguages() { return $this->spokenLanguages; } /** * @param Address $address * @return $this */ public function addAddress(Address $address) { $this->addresses[] = $address; return $this; } /** * @param Address $address */ public function removeAddress(Address $address) { $this->addresses->removeElement($address); } /** * By default, the addresses are ordered by date, descending (the most * recent first) */ public function getAddresses(): Collection { return $this->addresses; } /** * @deprecated Use `getCurrentPersonAddress` instead * @param DateTime|null $from * @return false|mixed|null * @throws \Exception */ public function getLastAddress(DateTime $from = null) { return $this->getCurrentPersonAddress(); } /** * get the address associated with the person at the given date * * If the `$at` parameter is now, use the method `getCurrentPersonAddress`, which is optimized * on database side. * * @param DateTime|null $at * @return Address|null * @throws \Exception */ public function getAddressAt(?\DateTime $at = null): ?Address { $at ??= new DateTime('now'); /** @var ArrayIterator $addressesIterator */ $addressesIterator = $this->getAddresses() ->filter(static fn (Address $address): bool => $address->getValidFrom() <= $at) ->getIterator(); $addressesIterator->uasort( static fn (Address $left, Address $right): int => $right->getValidFrom() <=> $left->getValidFrom() ); return [] === ($addresses = iterator_to_array($addressesIterator)) ? null : current($addresses); } /** * Get the current person address * * @return Address|null */ public function getCurrentPersonAddress(): ?Address { if (null === $this->currentPersonAddress) { return null; } return $this->currentPersonAddress->getAddress(); } /** * Validation callback that checks if the accompanying periods are valid * * This method add violation errors. * * @Assert\Callback( * groups={"accompanying_period_consistent"} * ) */ public function isAccompanyingPeriodValid(ExecutionContextInterface $context) { $r = $this->checkAccompanyingPeriodsAreNotCollapsing(); if ($r !== true) { if ($r['result'] === self::ERROR_PERIODS_ARE_COLLAPSING) { $context->buildViolation('Two accompanying periods have days in commun') ->atPath('accompanyingPeriods') ->addViolation(); } if ($r['result'] === self::ERROR_ADDIND_PERIOD_AFTER_AN_OPEN_PERIOD) { $context->buildViolation('A period is opened and a period is added after it') ->atPath('accompanyingPeriods') ->addViolation(); } } } /** * Return true if the person has two addresses with the * same validFrom date (in format 'Y-m-d') */ public function hasTwoAdressWithSameValidFromDate() { $validYMDDates = array(); foreach ($this->addresses as $ad) { $validDate = $ad->getValidFrom()->format('Y-m-d'); if (in_array($validDate, $validYMDDates)) { return true; } $validYMDDates[] = $validDate; } return false; } /** * Validation callback that checks if the addresses are valid (do not have * two addresses with the same validFrom date) * * This method add violation errors. * * @Assert\Callback( * groups={"addresses_consistent"} * ) */ public function isAddressesValid(ExecutionContextInterface $context) { if ($this->hasTwoAdressWithSameValidFromDate()) { $context ->buildViolation('Two addresses has the same validFrom date') ->atPath('addresses') ->addViolation() ; } } const ERROR_PERIODS_ARE_COLLAPSING = 1; // when two different periods // have days in commun const ERROR_ADDIND_PERIOD_AFTER_AN_OPEN_PERIOD = 2; // where there exist // a period opened and another one after it /** * Function used for validation that check if the accompanying periods of * the person are not collapsing (i.e. have not shared days) or having * a period after an open period. * * @return true | array True if the accompanying periods are not collapsing, * an array with data for displaying the error */ public function checkAccompanyingPeriodsAreNotCollapsing() { $periods = $this->getAccompanyingPeriodsOrdered(); $periodsNbr = sizeof($periods); $i = 0; while($i < $periodsNbr - 1) { $periodI = $periods[$i]; $periodAfterI = $periods[$i + 1]; if($periodI->isOpen()) { return array( 'result' => self::ERROR_ADDIND_PERIOD_AFTER_AN_OPEN_PERIOD, 'dateOpening' => $periodAfterI->getOpeningDate(), 'dateClosing' => $periodAfterI->getClosingDate(), 'date' => $periodI->getOpeningDate() ); } elseif ($periodI->getClosingDate() >= $periodAfterI->getOpeningDate()) { return array( 'result' => self::ERROR_PERIODS_ARE_COLLAPSING, 'dateOpening' => $periodI->getOpeningDate(), 'dateClosing' => $periodI->getClosingDate(), 'date' => $periodAfterI->getOpeningDate() ); } $i++; } return true; } public function getFullnameCanonical() : string { return $this->fullnameCanonical; } public function setFullnameCanonical($fullnameCanonical) : Person { $this->fullnameCanonical = $fullnameCanonical; return $this; } public function addHouseholdParticipation(HouseholdMember $member): self { $this->householdParticipations[] = $member; return $this; } public function getHouseholdParticipations(): Collection { return $this->householdParticipations; } /** * Get participation where the person does share the household. * * Order by startDate, desc */ public function getHouseholdParticipationsShareHousehold(): Collection { $criteria = new Criteria(); $expr = Criteria::expr(); $criteria ->where( $expr->eq('shareHousehold', true) ) ->orderBy(['startDate' => Criteria::DESC]) ; return $this->getHouseholdParticipations() ->matching($criteria) ; } /** * Get participation where the person does not share the household. * * Order by startDate, desc */ public function getHouseholdParticipationsNotShareHousehold(): Collection { $criteria = new Criteria(); $expr = Criteria::expr(); $criteria ->where( $expr->eq('shareHousehold', false) ) ->orderBy(['startDate' => Criteria::DESC]) ; return $this->getHouseholdParticipations() ->matching($criteria) ; } public function getCurrentHousehold(?\DateTimeImmutable $at = null): ?Household { $participation = $this->getCurrentHouseholdParticipationShareHousehold($at); return $participation instanceof HouseholdMember ? $participation->getHousehold() : null; } public function getCurrentHouseholdParticipationShareHousehold(?\DateTimeImmutable $at = null): ?HouseholdMember { $criteria = new Criteria(); $expr = Criteria::expr(); $date = NULL === $at ? new \DateTimeImmutable('today') : $at; $datef = $date->format('Y-m-d'); if ( NULL !== ($this->currentHouseholdParticipationAt[$datef] ?? NULL)) { return $this->currentHouseholdParticipationAt[$datef]; } $criteria ->where( $expr->andX( $expr->lte('startDate', $date), $expr->orX( $expr->isNull('endDate'), $expr->gt('endDate', $date) ), $expr->eq('shareHousehold', true) ) ); $participations = $this->getHouseholdParticipations() ->matching($criteria) ; return $participations->count() > 0 ? $this->currentHouseholdParticipationAt[$datef] = $participations->first() : null; } public function isSharingHousehold(?\DateTimeImmutable $at = null): bool { return NULL !== $this->getCurrentHousehold($at); } public function getHouseholdAddresses(): Collection { return $this->householdAddresses; } public function getCurrentHouseholdAddress(?\DateTimeImmutable $at = null): ?Address { if ( NULL === $at || $at->format('Ymd') === (new \DateTime('today'))->format('Ymd') ) { return $this->currentPersonAddress instanceof PersonCurrentAddress ? $this->currentPersonAddress->getAddress() : NULL; } // if not now, compute the date from history $criteria = new Criteria(); $expr = Criteria::expr(); $criteria->where( $expr->lte('validFrom', $at) ) ->andWhere( $expr->orX( $expr->isNull('validTo'), $expr->gte('validTo', $at) ) ); $addrs = $this->getHouseholdAddresses() ->matching($criteria) ; if ($addrs->count() > 0) { return $addrs->first()->getAddress(); } else { return null; } } public function hasCurrentHouseholdAddress(?\DateTimeImmutable $at = null): bool { return null !== $this->getCurrentHouseholdAddress($at); } public function getGenderComment(): CommentEmbeddable { return $this->genderComment; } public function setGenderComment(CommentEmbeddable $genderComment): self { $this->genderComment = $genderComment; return $this; } public function getMaritalStatusComment(): CommentEmbeddable { return $this->maritalStatusComment; } public function setMaritalStatusComment(CommentEmbeddable $maritalStatusComment): self { $this->maritalStatusComment = $maritalStatusComment; return $this; } public function getDeathdate(): ?\DateTimeInterface { return $this->deathdate; } public function setDeathdate(?\DateTimeInterface $deathdate): self { $this->deathdate = $deathdate; return $this; } public function getMaritalStatusDate(): ?\DateTimeInterface { return $this->maritalStatusDate; } public function setMaritalStatusDate(?\DateTimeInterface $maritalStatusDate): self { $this->maritalStatusDate = $maritalStatusDate; return $this; } public function getAcceptSMS(): ?bool { return $this->acceptSMS; } public function setAcceptSMS(bool $acceptSMS): self { $this->acceptSMS = $acceptSMS; return $this; } public function getAcceptEmail(): ?bool { return $this->acceptEmail; } public function setAcceptEmail(bool $acceptEmail): self { $this->acceptEmail = $acceptEmail; return $this; } public function getNumberOfChildren(): ?int { return $this->numberOfChildren; } public function setNumberOfChildren(int $numberOfChildren): self { $this->numberOfChildren = $numberOfChildren; return $this; } /** * @return AccompanyingPeriod[]|Collection */ public function getAccompanyingPeriodRequested(): Collection { return $this->accompanyingPeriodRequested; } /** * Return a list of all accompanying period where the person is involved: * * * as requestor; * * as participant, only for opened participation; * * @param bool $asParticipantOpen add participation which are still opened * @param bool $asRequestor add accompanying period where the person is requestor * @return AccompanyingPeriod[]|Collection */ public function getAccompanyingPeriodInvolved( bool $asParticipantOpen = true, bool $asRequestor = true ): Collection { $result = new ArrayCollection(); if ($asParticipantOpen) { foreach ($this->getOpenedParticipations() ->map(fn (AccompanyingPeriodParticipation $app) => $app->getAccompanyingPeriod()) as $period ) { if (!$result->contains($period)) { $result->add($period); } } } if ($asRequestor) { foreach ($this->accompanyingPeriodRequested as $period) { if (!$result->contains($period)) { $result->add($period); } } } return $result; } /** * Handy method to get the AccompanyingPeriodParticipation * matching a given AccompanyingPeriod. * * Used in template, to find the participation when iterating on a list * of period. * * @param \Chill\PersonBundle\Entity\AccompanyingPeriod $period * @return AccompanyingPeriodParticipation */ public function findParticipationForPeriod(AccompanyingPeriod $period): ?AccompanyingPeriodParticipation { $closeCandidates = []; foreach ($this->getAccompanyingPeriodParticipations() as $participation) { if ($participation->getAccompanyingPeriod() === $period) { if ($participation->isOpen()) { return $participation; } $closeCandidates[] = $participation; } } if (0 < count($closeCandidates)) { return $closeCandidates[0]; } return null; } public function getCreatedBy(): ?User { return $this->createdBy; } public function setCreatedBy(User $createdBy): self { $this->createdBy = $createdBy; return $this; } public function getCreatedAt(): ?DateTimeInterface { return $this->createdAt; } public function setCreatedAt(\DateTimeInterface $datetime): self { $this->createdAt = $datetime; return $this; } public function getUpdatedBy(): ?User { return $this->updatedBy; } public function getUpdatedAt(): ?DateTimeInterface { return $this->updatedAt; } public function setUpdatedBy(User $user): self { $this->updatedBy = $user; return $this; } public function setUpdatedAt(\DateTimeInterface $datetime): self { $this->updatedAt = $datetime; return $this; } }