mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
optimize query for current address + documentation
This commit is contained in:
parent
f63d4fcfba
commit
50b7554aea
@ -8,6 +8,29 @@ use Chill\PersonBundle\Entity\Person;
|
|||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* This class links a person to the history of his addresses, through
|
||||||
|
* household membership.
|
||||||
|
*
|
||||||
|
* It is optimized on DB side, and compute the start date and end date
|
||||||
|
* of each address by the belonging of household.
|
||||||
|
*
|
||||||
|
* **note**: the start date and end date are the date of belonging to the address,
|
||||||
|
* not the belonging of the household.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* * person A is member of household W from 2021-01-01 to 2021-12-01
|
||||||
|
* * person A is member of household V from 2021-12-01, still present after
|
||||||
|
* * household W lives in address Q from 2020-06-01 to 2021-06-01
|
||||||
|
* * household W lives in address R from 2021-06-01 to 2022-06-01
|
||||||
|
* * household V lives in address T from 2021-12-01 to still living there after
|
||||||
|
*
|
||||||
|
* The person A will have those 3 entities:
|
||||||
|
*
|
||||||
|
* 1. 1st entity: from 2021-01-01 to 2021-06-01, household W, address Q;
|
||||||
|
* 2. 2st entity: from 2021-06-01 to 2021-12-01, household W, address R;
|
||||||
|
* 3. 3st entity: from 2021-12-01 to NULL, household V, address T;
|
||||||
|
*
|
||||||
* @ORM\Entity(readOnly=true)
|
* @ORM\Entity(readOnly=true)
|
||||||
* @ORM\Table(name="view_chill_person_household_address")
|
* @ORM\Table(name="view_chill_person_household_address")
|
||||||
*/
|
*/
|
||||||
@ -45,11 +68,23 @@ class PersonHouseholdAddress
|
|||||||
*/
|
*/
|
||||||
private $address;
|
private $address;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The start date of the intersection address/household
|
||||||
|
*
|
||||||
|
* (this is not the startdate of the household, not
|
||||||
|
* the startdate of the address)
|
||||||
|
*/
|
||||||
public function getValidFrom(): ?\DateTimeInterface
|
public function getValidFrom(): ?\DateTimeInterface
|
||||||
{
|
{
|
||||||
return $this->validFrom;
|
return $this->validFrom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The end date of the intersection address/household
|
||||||
|
*
|
||||||
|
* (this is not the enddate of the household, not
|
||||||
|
* the enddate of the address)
|
||||||
|
*/
|
||||||
public function getValidTo(): ?\DateTimeImmutable
|
public function getValidTo(): ?\DateTimeImmutable
|
||||||
{
|
{
|
||||||
return $this->validTo;
|
return $this->validTo;
|
||||||
|
@ -35,6 +35,7 @@ use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
|||||||
use Chill\MainBundle\Entity\HasCenterInterface;
|
use Chill\MainBundle\Entity\HasCenterInterface;
|
||||||
use Chill\MainBundle\Entity\Address;
|
use Chill\MainBundle\Entity\Address;
|
||||||
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
|
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
|
||||||
|
use Chill\PersonBundle\Entity\Person\PersonCurrentAddress;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
@ -412,6 +413,15 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
|||||||
*/
|
*/
|
||||||
private $addresses;
|
private $addresses;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current person address.
|
||||||
|
*
|
||||||
|
* This is computed through database and is optimized on database side.
|
||||||
|
*
|
||||||
|
* @var PersonCurrentAddress|null
|
||||||
|
*/
|
||||||
|
private ?PersonCurrentAddress $currentPersonAddress = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fullname canonical. Read-only field, which is calculated by
|
* fullname canonical. Read-only field, which is calculated by
|
||||||
* the database.
|
* the database.
|
||||||
@ -564,6 +574,8 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
|||||||
return $participation->getAccompanyingPeriod();
|
return $participation->getAccompanyingPeriod();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1242,13 +1254,31 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
|||||||
return $this->addresses;
|
return $this->addresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use `getCurrentPersonAddress` instead
|
||||||
|
* @param DateTime|null $from
|
||||||
|
* @return false|mixed|null
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
public function getLastAddress(DateTime $from = null)
|
public function getLastAddress(DateTime $from = null)
|
||||||
{
|
{
|
||||||
$from ??= new DateTime('now');
|
return $this->getCurrentPersonAddress($from);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the address associated with the person at the given date
|
||||||
|
*
|
||||||
|
* @param DateTime|null $at
|
||||||
|
* @return Address|null
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function getCurrentPersonAddress(?\DateTime $at = null): ?Address
|
||||||
|
{
|
||||||
|
$at ??= new DateTime('now');
|
||||||
|
|
||||||
/** @var ArrayIterator $addressesIterator */
|
/** @var ArrayIterator $addressesIterator */
|
||||||
$addressesIterator = $this->getAddresses()
|
$addressesIterator = $this->getAddresses()
|
||||||
->filter(static fn (Address $address): bool => $address->getValidFrom() <= $from)
|
->filter(static fn (Address $address): bool => $address->getValidFrom() <= $at)
|
||||||
->getIterator();
|
->getIterator();
|
||||||
|
|
||||||
$addressesIterator->uasort(
|
$addressesIterator->uasort(
|
||||||
@ -1496,7 +1526,16 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
|||||||
|
|
||||||
public function getCurrentHouseholdAddress(?\DateTimeImmutable $at = null): ?Address
|
public function getCurrentHouseholdAddress(?\DateTimeImmutable $at = null): ?Address
|
||||||
{
|
{
|
||||||
$at = $at === null ? new \DateTimeImmutable('today') : $at;
|
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();
|
$criteria = new Criteria();
|
||||||
$expr = Criteria::expr();
|
$expr = Criteria::expr();
|
||||||
|
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\PersonBundle\Entity\Person;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\Address;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entity which associate person with his current address, through
|
||||||
|
* household participation.
|
||||||
|
*
|
||||||
|
* The computation is optimized on database side.
|
||||||
|
*
|
||||||
|
* The validFrom and validTo properties are the intersection of
|
||||||
|
* household membership and address validity. See @link{PersonHouseholdAddress}
|
||||||
|
*
|
||||||
|
* @ORM\Entity(readOnly=true)
|
||||||
|
* @ORM\Table("view_chill_person_current_address")
|
||||||
|
*/
|
||||||
|
class PersonCurrentAddress
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @ORM\Id
|
||||||
|
* @ORM\OneToOne(targetEntity=Person::class)
|
||||||
|
*/
|
||||||
|
protected Person $person;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\OneToOne(targetEntity=Address::class)
|
||||||
|
*/
|
||||||
|
protected Address $address;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(name="valid_from", type="date_immutable")
|
||||||
|
*/
|
||||||
|
protected \DateTimeImmutable $validFrom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(name="valid_to", type="date_immutable")
|
||||||
|
*/
|
||||||
|
protected ?\DateTimeImmutable $validTo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Person
|
||||||
|
*/
|
||||||
|
public function getPerson(): Person
|
||||||
|
{
|
||||||
|
return $this->person;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Address
|
||||||
|
*/
|
||||||
|
public function getAddress(): Address
|
||||||
|
{
|
||||||
|
return $this->address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This date is the intersection of household membership
|
||||||
|
* and address validity
|
||||||
|
*
|
||||||
|
* @return \DateTimeImmutable
|
||||||
|
*/
|
||||||
|
public function getValidFrom(): \DateTimeImmutable
|
||||||
|
{
|
||||||
|
return $this->validFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This date is the intersection of household membership
|
||||||
|
* and address validity
|
||||||
|
*
|
||||||
|
* @return \DateTimeImmutable|null
|
||||||
|
*/
|
||||||
|
public function getValidTo(): ?\DateTimeImmutable
|
||||||
|
{
|
||||||
|
return $this->validTo;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\Migrations\Person;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create view for PersonCurrentAddress and related indexes
|
||||||
|
*/
|
||||||
|
final class Version20210915093624 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Create view for PersonCurrentAddress and related indexes';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql("CREATE VIEW view_chill_person_current_address AS
|
||||||
|
SELECT
|
||||||
|
cphm.person_id AS person_id,
|
||||||
|
cma.id AS address_id,
|
||||||
|
CASE WHEN cphm.startdate > COALESCE(cma.validfrom, '-infinity'::date) THEN cphm.startdate ELSE cma.validfrom END AS valid_from,
|
||||||
|
CASE WHEN COALESCE(cphm.enddate, 'infinity'::date) < COALESCE(cma.validto, 'infinity'::date) THEN cphm.enddate ELSE cma.validto END AS valid_to
|
||||||
|
FROM chill_person_household_members AS cphm
|
||||||
|
LEFT JOIN chill_person_household_to_addresses AS cphta ON cphta.household_id = cphm.household_id
|
||||||
|
LEFT JOIN chill_main_address AS cma ON cphta.address_id = cma.id
|
||||||
|
WHERE
|
||||||
|
cphm.sharedhousehold IS TRUE
|
||||||
|
AND
|
||||||
|
current_date between cphm.startdate AND coalesce(enddate, 'infinity'::date)
|
||||||
|
AND
|
||||||
|
current_date between cma.validfrom AND coalesce(validto, 'infinity'::date)
|
||||||
|
");
|
||||||
|
|
||||||
|
$this->addSql("CREATE INDEX chill_custom_main_address_filtering_idx ON chill_main_address USING btree (id, validfrom ASC, validto DESC)");
|
||||||
|
$this->addSql("CREATE INDEX chill_custom_person_household_members_sharing_idx ON chill_person_household_members USING btree (person_id, startdate ASC, enddate DESC, household_id) WHERE sharedhousehold IS TRUE");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql("DROP VIEW view_chill_person_current_address");
|
||||||
|
$this->addSql("DROP INDEX chill_custom_main_address_filtering_idx");
|
||||||
|
$this->addSql("DROP INDEX chill_custom_person_household_members_sharing_idx");
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user