From 8bd20c9c7852a668b56eb81854b996fcf8f24dd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 15 Jun 2021 22:52:48 +0200 Subject: [PATCH 1/5] [WIP] add fixtures for household address --- .../DataFixtures/ORM/LoadHousehold.php | 55 +++++++++++++++++++ .../Entity/Household/Household.php | 5 +- .../DataFixtures/ORM/LoadThirdParty.php | 11 +--- 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHousehold.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHousehold.php index 8dfca71f6..029a71cfa 100644 --- a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHousehold.php +++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHousehold.php @@ -9,6 +9,7 @@ use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\ObjectManager; use Doctrine\Common\DataFixtures\DependentFixtureInterface; +use Nelmio\Alice\Loader\NativeLoader; class LoadHousehold extends Fixture implements DependentFixtureInterface { @@ -16,12 +17,15 @@ class LoadHousehold extends Fixture implements DependentFixtureInterface private EntityManagerInterface $em; + private NativeLoader $loader; + private CONST NUMBER_OF_HOUSEHOLD = 10; public function __construct(MembersEditorFactory $editorFactory, EntityManagerInterface $em) { $this->editorFactory = $editorFactory; $this->em = $em; + $this->loader = new NativeLoader(); } public function load(ObjectManager $manager) @@ -53,6 +57,8 @@ class LoadHousehold extends Fixture implements DependentFixtureInterface $household = new Household(); $manager->persist($household); + $this->addAddressToHousehold($household, \DateTimeImmutable::createFrom($startDate), $manager); + $movement = $this->editorFactory->createEditor($household); // load adults @@ -89,6 +95,55 @@ class LoadHousehold extends Fixture implements DependentFixtureInterface } } + private function addAddressToHousehold(Household $household, \DateTimeImmutable $date, ObjectManager $manager) + { + if (\random_int(0, 10) > 8) { + // 20% of household without address + return; + } + + $nb = \random_int(1, 6); + + $i = 0; + while ($i < $nb) { + $address = $this->createAddress(); + $address->validFrom($date); + + if (\random_int(0, 20) < 1) { + $date = $date->add(new \DateInterval('P'.\random_int(8, 52).'W')); + $address->validTo($date); + } + + $household->addAddress($address); + $manager->persist($address); + + $date = $date->add(new \DateInterval('P'.\random_int(8, 52).'W')); + $i++; + } + } + + private function createAddress() + { + $objectSet = $this->loader->loadData([ + Address::class => [ + 'address1' => [ + 'street' => '', + 'streetNumber' => '', + 'postCode' => $this->getPostalCode() + ] + ] + ]); + + return $objectSet->getObjects()[0]; + } + + private function getPostalCode(): PostalCode + { + $ref = LoadPostalCodes::$refs[\array_rand(LoadPostalCodes::$refs)]; + + return $this->getReference($ref); + } + private function preparePersonIds() { $this->personIds = $this->em diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php index 0ef2bbb3c..02ea6e66b 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php +++ b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php @@ -68,13 +68,14 @@ class Household */ public function addAddress(Address $address) { - $this->addresses[] = $address; - foreach ($this->getAddresses() as $a) { if ($a->getValidFrom() < $address->getValidFrom() && $a->getValidTo() === NULL) { $a->setValidTo($address->getValidFrom()); } } + + $this->addresses[] = $address; + return $this; } diff --git a/src/Bundle/ChillThirdPartyBundle/DataFixtures/ORM/LoadThirdParty.php b/src/Bundle/ChillThirdPartyBundle/DataFixtures/ORM/LoadThirdParty.php index 63f6e9c3b..7fc5ea1c8 100644 --- a/src/Bundle/ChillThirdPartyBundle/DataFixtures/ORM/LoadThirdParty.php +++ b/src/Bundle/ChillThirdPartyBundle/DataFixtures/ORM/LoadThirdParty.php @@ -88,17 +88,8 @@ class LoadThirdParty extends Fixture Implements DependentFixtureInterface private function getPostalCode(): PostalCode { $ref = LoadPostalCodes::$refs[\array_rand(LoadPostalCodes::$refs)]; + return $this->getReference($ref); - if (count($this->postalCodesIds) === 0) { - // fill the postal codes - $this->em->createQuery('SELECT p.id FROM '.PostalCode::class) - ->getScalarResult(); - } - - $id = $this->postalCodesIds[\array_rand($this->postalCodesIds)]; - - return $this->em->getRepository(PostalCode::class) - ->find($id); } private function createAddress(): ObjectSet From 38bff2e42faedfb17e4bd68b65d6c2bf70889204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 16 Jun 2021 08:35:47 +0200 Subject: [PATCH 2/5] add address to household in fixtures --- .../DataFixtures/ORM/LoadHousehold.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHousehold.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHousehold.php index 029a71cfa..812f2bb07 100644 --- a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHousehold.php +++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHousehold.php @@ -4,12 +4,15 @@ namespace Chill\PersonBundle\DataFixtures\ORM; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Household\Household; +use Chill\MainBundle\Entity\PostalCode; +use Chill\MainBundle\Entity\Address; use Chill\PersonBundle\Household\MembersEditorFactory; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\ObjectManager; use Doctrine\Common\DataFixtures\DependentFixtureInterface; use Nelmio\Alice\Loader\NativeLoader; +use Chill\MainBundle\DataFixtures\ORM\LoadPostalCodes; class LoadHousehold extends Fixture implements DependentFixtureInterface { @@ -57,7 +60,7 @@ class LoadHousehold extends Fixture implements DependentFixtureInterface $household = new Household(); $manager->persist($household); - $this->addAddressToHousehold($household, \DateTimeImmutable::createFrom($startDate), $manager); + $this->addAddressToHousehold($household, clone $startDate, $manager); $movement = $this->editorFactory->createEditor($household); @@ -107,11 +110,11 @@ class LoadHousehold extends Fixture implements DependentFixtureInterface $i = 0; while ($i < $nb) { $address = $this->createAddress(); - $address->validFrom($date); + $address->setValidFrom(\DateTime::createFromImmutable($date)); if (\random_int(0, 20) < 1) { $date = $date->add(new \DateInterval('P'.\random_int(8, 52).'W')); - $address->validTo($date); + $address->setValidTo(\DateTime::createFromImmutable($date)); } $household->addAddress($address); @@ -122,7 +125,7 @@ class LoadHousehold extends Fixture implements DependentFixtureInterface } } - private function createAddress() + private function createAddress(): Address { $objectSet = $this->loader->loadData([ Address::class => [ @@ -134,7 +137,7 @@ class LoadHousehold extends Fixture implements DependentFixtureInterface ] ]); - return $objectSet->getObjects()[0]; + return $objectSet->getObjects()['address1']; } private function getPostalCode(): PostalCode From ef55d2cf7fbd252370687b92b4a0e9f137cbc233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 17 Jun 2021 13:21:55 +0200 Subject: [PATCH 3/5] create view for current address and apply on Person/Household normalizer --- .../ChillMainExtension.php | 8 ++- src/Bundle/ChillMainBundle/Entity/Address.php | 4 +- .../Entity/Household/Household.php | 25 +++++++ .../Household/PersonHouseholdAddress.php | 72 +++++++++++++++++++ .../ChillPersonBundle/Entity/Person.php | 42 +++++++++++ .../PersonHouseholdAddressRepository.php | 48 +++++++++++++ .../Normalizer/PersonNormalizer.php | 1 + .../migrations/Version20210616102900.php | 43 +++++++++++ 8 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/Entity/Household/PersonHouseholdAddress.php create mode 100644 src/Bundle/ChillPersonBundle/Repository/Household/PersonHouseholdAddressRepository.php create mode 100644 src/Bundle/ChillPersonBundle/migrations/Version20210616102900.php 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"); + } +} From fd7c7388d98d21fe43fe6d86e7ccf0e3f489dc9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 17 Jun 2021 13:31:04 +0200 Subject: [PATCH 4/5] fix prepending configuration of doctrine --- .../DependencyInjection/ChillMainExtension.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index 3a31bc8a4..756e075d9 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -197,11 +197,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, [ 'dbal' => [ // ignore views: - 'connections' => [ - 'default' => [ - 'schema_filter' => '~^(?!view_)~', - ], - ], + 'schema_filter' => '~^(?!view_)~', // This is mandatory since we are using postgis as database. 'mapping_types' => [ 'geometry' => 'string', From 27907e7558ce6829cfa4e7b94b17683e20dafe0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 17 Jun 2021 14:02:29 +0200 Subject: [PATCH 5/5] fix modification of key members in household normalization --- src/Bundle/ChillPersonBundle/Entity/Household/Household.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php index e54733cae..4cc158de9 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php +++ b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php @@ -48,7 +48,7 @@ class Household * targetEntity=HouseholdMember::class, * mappedBy="household" * ) - * @Serializer\Groups({"write"}) + * @Serializer\Groups({"write", "read"}) */ private Collection $members; @@ -129,10 +129,6 @@ class Household return $this->members; } - /** - * @Serializer\Groups({ "read" }) - * @Serializer\SerializedName("current_members") - */ public function getCurrentMembers(?\DateTimeImmutable $now = null): Collection { $criteria = new Criteria();