diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index 23a6e4de9..3a31bc8a4 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -195,7 +195,13 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, ->prependExtensionConfig( 'doctrine', [ - 'dbal' => [ + 'dbal' => [ + // ignore views: + 'connections' => [ + 'default' => [ + 'schema_filter' => '~^(?!view_)~', + ], + ], // This is mandatory since we are using postgis as database. 'mapping_types' => [ 'geometry' => 'string', diff --git a/src/Bundle/ChillMainBundle/Entity/Address.php b/src/Bundle/ChillMainBundle/Entity/Address.php index 3bbbfa368..af3fea746 100644 --- a/src/Bundle/ChillMainBundle/Entity/Address.php +++ b/src/Bundle/ChillMainBundle/Entity/Address.php @@ -116,7 +116,7 @@ class Address * @ORM\Column(type="date") * @groups({"write"}) */ - private $validFrom; + private \DateTime $validFrom; /** * Indicates when the address ends. Used to build an history @@ -127,7 +127,7 @@ class Address * @ORM\Column(type="date", nullable=true) * @groups({"write"}) */ - private $validTo; + private ?\DateTime $validTo = null; /** * True if the address is a "no address", aka homeless person, ... diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php index b802c1c4d..e54733cae 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php +++ b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php @@ -100,6 +100,27 @@ class Household return $this->addresses; } + /** + * @Serializer\Groups({ "read" }) + * @Serializer\SerializedName("current_address") + */ + public function getCurrentAddress(\DateTime $at = null): ?Address + { + $at = $at === null ? new \DateTime('today') : $at; + + $addrs = $this->getAddresses()->filter(function (Address $a) use ($at) { + return $a->getValidFrom() < $at && ( + NULL === $a->getValidTo() || $at < $a->getValidTo() + ); + }); + + if ($addrs->count() > 0) { + return $addrs->first(); + } else { + return null; + } + } + /** * @return Collection|HouseholdMember[] */ @@ -108,6 +129,10 @@ class Household return $this->members; } + /** + * @Serializer\Groups({ "read" }) + * @Serializer\SerializedName("current_members") + */ public function getCurrentMembers(?\DateTimeImmutable $now = null): Collection { $criteria = new Criteria(); diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/PersonHouseholdAddress.php b/src/Bundle/ChillPersonBundle/Entity/Household/PersonHouseholdAddress.php new file mode 100644 index 000000000..511416a0e --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Entity/Household/PersonHouseholdAddress.php @@ -0,0 +1,72 @@ +validFrom; + } + + public function getValidTo(): ?\DateTimeImmutable + { + return $this->validTo; + } + + public function getPerson(): ?Person + { + return $this->person; + } + + public function getHousehold(): ?Household + { + return $this->relation; + } + + public function getAddress(): ?Address + { + return $this->address; + } +} diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index 0ec04bf35..4a777221f 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -37,6 +37,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Criteria; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Serializer\Annotation\DiscriminatorMap; +use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress; /** * Person Class @@ -287,6 +288,14 @@ class Person implements HasCenterInterface */ private array $currentHouseholdAt = []; + /** + * @ORM\OneToMany( + * targetEntity=PersonHouseholdAddress::class, + * mappedBy="person" + * ) + */ + private Collection $householdAddresses; + /** * Person constructor. * @@ -300,6 +309,7 @@ class Person implements HasCenterInterface $this->altNames = new ArrayCollection(); $this->otherPhoneNumbers = new ArrayCollection(); $this->householdParticipations = new ArrayCollection(); + $this->householdAddresses = new ArrayCollection(); if ($opening === null) { $opening = new \DateTime(); @@ -1247,4 +1257,36 @@ class Person implements HasCenterInterface { return NULL !== $this->getCurrentHousehold($at); } + + public function getHouseholdAddresses(): Collection + { + return $this->householdAddresses; + } + + public function getCurrentHouseholdAddress(?\DateTimeImmutable $at = null): ?Address + { + $at = $at === null ? new \DateTimeImmutable('today') : $at; + $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; + } + } } diff --git a/src/Bundle/ChillPersonBundle/Repository/Household/PersonHouseholdAddressRepository.php b/src/Bundle/ChillPersonBundle/Repository/Household/PersonHouseholdAddressRepository.php new file mode 100644 index 000000000..d0676d172 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Repository/Household/PersonHouseholdAddressRepository.php @@ -0,0 +1,48 @@ +repository = $em->getRepository(PersonHouseholdAddress::class); + } + + public function find($id, $lockMode = null, $lockVersion = null): ?PersonHouseholdAddress + { + return $this->repository->find($id, $lockMode, $lockVersion); + } + + public function findOneBy(array $criteria, array $orderBy = null): ?PersonHouseholdAddress + { + return $this->repository->findOneBy($criteria, $orderBy); + } + + /** + * @return PersonHouseholdAddress[] + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + /** + * @return PersonHouseholdAddress[] + */ + public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function getClassName() { + return PersonHouseholdAddress::class; + } +} diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php index 98edcec14..cde495f52 100644 --- a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php @@ -75,6 +75,7 @@ class PersonNormalizer implements 'altNames' => $this->normalizeAltNames($person->getAltNames()), 'gender' => $person->getGender(), 'gender_numeric' => $person->getGenderNumeric(), + 'current_household_address' => $this->normalizer->normalize($person->getCurrentHouseholdAddress()), ]; } diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20210616102900.php b/src/Bundle/ChillPersonBundle/migrations/Version20210616102900.php new file mode 100644 index 000000000..1774cb0b3 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20210616102900.php @@ -0,0 +1,43 @@ +addSql("CREATE VIEW view_chill_person_household_address AS ". + "SELECT ". + "members.person_id AS person_id, ". + "members.household_id AS household_id, ". + "members.id AS member_id, ". + "address.id AS address_id, ". + "CASE WHEN address.validFrom < members.startDate THEN members.startDate ELSE address.validFrom END AS validFrom, ". + "CASE WHEN COALESCE(address.validTo, 'infinity') < COALESCE(members.endDate, 'infinity') THEN address.validTo ELSE members.endDate END AS validTo ". + "FROM chill_person_household_members AS members ". + "JOIN chill_person_household_to_addresses AS household_to_addr ON household_to_addr.household_id = members.household_id ". + "JOIN chill_main_address AS address ON household_to_addr.address_id = address.id ". + "AND daterange(address.validFrom, address.validTo) && daterange(members.startDate, members.endDate) ". + "WHERE members.sharedhousehold IS TRUE " + ); + + } + + public function down(Schema $schema): void + { + $this->addSql("DROP VIEW view_chill_person_household_address"); + } +}