create view for current address and apply on Person/Household normalizer

This commit is contained in:
Julien Fastré 2021-06-17 13:21:55 +02:00
parent 38bff2e42f
commit ef55d2cf7f
8 changed files with 240 additions and 3 deletions

View File

@ -195,7 +195,13 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
->prependExtensionConfig( ->prependExtensionConfig(
'doctrine', 'doctrine',
[ [
'dbal' => [ 'dbal' => [
// ignore views:
'connections' => [
'default' => [
'schema_filter' => '~^(?!view_)~',
],
],
// This is mandatory since we are using postgis as database. // This is mandatory since we are using postgis as database.
'mapping_types' => [ 'mapping_types' => [
'geometry' => 'string', 'geometry' => 'string',

View File

@ -116,7 +116,7 @@ class Address
* @ORM\Column(type="date") * @ORM\Column(type="date")
* @groups({"write"}) * @groups({"write"})
*/ */
private $validFrom; private \DateTime $validFrom;
/** /**
* Indicates when the address ends. Used to build an history * Indicates when the address ends. Used to build an history
@ -127,7 +127,7 @@ class Address
* @ORM\Column(type="date", nullable=true) * @ORM\Column(type="date", nullable=true)
* @groups({"write"}) * @groups({"write"})
*/ */
private $validTo; private ?\DateTime $validTo = null;
/** /**
* True if the address is a "no address", aka homeless person, ... * True if the address is a "no address", aka homeless person, ...

View File

@ -100,6 +100,27 @@ class Household
return $this->addresses; 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[] * @return Collection|HouseholdMember[]
*/ */
@ -108,6 +129,10 @@ class Household
return $this->members; return $this->members;
} }
/**
* @Serializer\Groups({ "read" })
* @Serializer\SerializedName("current_members")
*/
public function getCurrentMembers(?\DateTimeImmutable $now = null): Collection public function getCurrentMembers(?\DateTimeImmutable $now = null): Collection
{ {
$criteria = new Criteria(); $criteria = new Criteria();

View File

@ -0,0 +1,72 @@
<?php
namespace Chill\PersonBundle\Entity\Household;
use Chill\MainBundle\Entity\Address;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(readOnly=true)
* @ORM\Table(name="view_chill_person_household_address")
*/
class PersonHouseholdAddress
{
/**
* @ORM\Column(type="date_immutable")
*/
private $validFrom;
/**
* @ORM\Column(type="date_immutable", nullable=true)
*/
private $validTo;
/**
* @ORM\Id
* @ORM\ManyToOne(targetEntity=Person::class)
* @ORM\JoinColumn(nullable=false)
*/
private $person;
/**
* @ORM\Id
* @ORM\ManyToOne(targetEntity=Household::class)
* @ORM\JoinColumn(nullable=false)
*/
private $household;
/**
* @ORM\Id
* @ORM\ManyToOne(targetEntity=Address::class)
* @ORM\JoinColumn(nullable=false)
*/
private $address;
public function getValidFrom(): ?\DateTimeInterface
{
return $this->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;
}
}

View File

@ -37,6 +37,7 @@ use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria; use Doctrine\Common\Collections\Criteria;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap; use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
/** /**
* Person Class * Person Class
@ -287,6 +288,14 @@ class Person implements HasCenterInterface
*/ */
private array $currentHouseholdAt = []; private array $currentHouseholdAt = [];
/**
* @ORM\OneToMany(
* targetEntity=PersonHouseholdAddress::class,
* mappedBy="person"
* )
*/
private Collection $householdAddresses;
/** /**
* Person constructor. * Person constructor.
* *
@ -300,6 +309,7 @@ class Person implements HasCenterInterface
$this->altNames = new ArrayCollection(); $this->altNames = new ArrayCollection();
$this->otherPhoneNumbers = new ArrayCollection(); $this->otherPhoneNumbers = new ArrayCollection();
$this->householdParticipations = new ArrayCollection(); $this->householdParticipations = new ArrayCollection();
$this->householdAddresses = new ArrayCollection();
if ($opening === null) { if ($opening === null) {
$opening = new \DateTime(); $opening = new \DateTime();
@ -1247,4 +1257,36 @@ class Person implements HasCenterInterface
{ {
return NULL !== $this->getCurrentHousehold($at); 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;
}
}
} }

View File

@ -0,0 +1,48 @@
<?php
namespace Chill\PersonBundle\Repository\Household;
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
final class PersonHouseholdAddressRepository implements ObjectRepository
{
private EntityRepository $repository;
public function __construct(EntityManagerInterface $em)
{
$this->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;
}
}

View File

@ -75,6 +75,7 @@ class PersonNormalizer implements
'altNames' => $this->normalizeAltNames($person->getAltNames()), 'altNames' => $this->normalizeAltNames($person->getAltNames()),
'gender' => $person->getGender(), 'gender' => $person->getGender(),
'gender_numeric' => $person->getGenderNumeric(), 'gender_numeric' => $person->getGenderNumeric(),
'current_household_address' => $this->normalizer->normalize($person->getCurrentHouseholdAddress()),
]; ];
} }

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Person;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Create a view for assembling household, person and addresses
*/
final class Version20210616102900 extends AbstractMigration
{
public function getDescription(): string
{
return 'Create a view for assembling household, person and addresses';
}
public function up(Schema $schema): void
{
$this->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");
}
}