Resolve "Lors de l'import de code postaux, les codes absents de l'import depuis la même source ne sont pas supprimés"

This commit is contained in:
2026-02-23 20:05:04 +00:00
parent 966f9f7e33
commit 030553a4de
4 changed files with 185 additions and 12 deletions

View File

@@ -19,31 +19,66 @@ use Doctrine\DBAL\Statement;
*/
class PostalCodeBaseImporter
{
private const QUERY = <<<'SQL'
private const CREATE_TEMP_TABLE = <<<'SQL'
CREATE TEMPORARY TABLE chill_main_postal_code_temp (
countrycode VARCHAR(10),
label VARCHAR(255),
code VARCHAR(100),
refpostalcodeid VARCHAR(255),
postalcodeSource VARCHAR(255),
lon FLOAT,
lat FLOAT,
srid INT
)
SQL;
private const INSERT_TEMP = <<<'SQL'
INSERT INTO chill_main_postal_code_temp
(countrycode, label, code, refpostalcodeid, postalcodeSource, lon, lat, srid)
VALUES
{{ values }}
SQL;
private const UPSERT = <<<'SQL'
WITH g AS (
SELECT DISTINCT
country.id AS country_id,
g.*
FROM (VALUES
{{ values }}
) AS g (countrycode, label, code, refpostalcodeid, postalcodeSource, lon, lat, srid)
JOIN country ON country.countrycode = g.countrycode
temp.*
FROM chill_main_postal_code_temp temp
JOIN country ON country.countrycode = temp.countrycode
)
INSERT INTO chill_main_postal_code (id, country_id, label, code, origin, refpostalcodeid, postalcodeSource, center, createdAt, updatedAt)
INSERT INTO chill_main_postal_code (id, country_id, label, code, origin, refpostalcodeid, postalcodeSource, center, createdAt, updatedAt, deletedAt)
SELECT
nextval('chill_main_postal_code_id_seq'),
g.country_id,
g.label AS glabel,
g.label,
g.code,
0,
g.refpostalcodeid,
g.postalcodeSource,
CASE WHEN (g.lon::float != 0.0 AND g.lat::float != 0.0) THEN ST_Transform(ST_setSrid(ST_point(g.lon::float, g.lat::float), g.srid::int), 4326) ELSE NULL END,
CASE WHEN (g.lon != 0.0 AND g.lat != 0.0) THEN ST_Transform(ST_setSrid(ST_point(g.lon, g.lat), g.srid), 4326) ELSE NULL END,
NOW(),
NOW()
NOW(),
NULL
FROM g
ON CONFLICT (code, refpostalcodeid, postalcodeSource) WHERE refpostalcodeid IS NOT NULL DO UPDATE
SET label = excluded.label, center = excluded.center, updatedAt = CASE WHEN NOT st_equals(excluded.center, chill_main_postal_code.center) OR excluded.label != chill_main_postal_code.label THEN NOW() ELSE chill_main_postal_code.updatedAt END
SET label = excluded.label,
center = excluded.center,
deletedAt = NULL,
updatedAt = CASE WHEN NOT st_equals(excluded.center, chill_main_postal_code.center) OR excluded.label != chill_main_postal_code.label OR chill_main_postal_code.deletedAt IS NOT NULL THEN NOW() ELSE chill_main_postal_code.updatedAt END
SQL;
private const DELETE_MISSING = <<<'SQL'
UPDATE chill_main_postal_code
SET deletedAt = NOW(), updatedAt = NOW()
WHERE postalcodeSource = ?
AND deletedAt IS NULL
AND NOT EXISTS (
SELECT 1 FROM chill_main_postal_code_temp temp
WHERE temp.code = chill_main_postal_code.code
AND temp.refpostalcodeid = chill_main_postal_code.refpostalcodeid
AND temp.postalcodeSource = chill_main_postal_code.postalcodeSource
)
SQL;
private const VALUE = '(?, ?, ?, ?, ?, ?, ?, ?)';
@@ -55,11 +90,26 @@ class PostalCodeBaseImporter
private array $waitingForInsert = [];
private bool $isInitialized = false;
private ?string $currentSource = null;
public function __construct(private readonly Connection $defaultConnection) {}
public function finalize(): void
{
$this->doInsertPending();
if ($this->isInitialized && null !== $this->currentSource) {
$this->defaultConnection->transactional(function (Connection $connection): void {
$connection->executeStatement(self::UPSERT);
$connection->executeStatement(self::DELETE_MISSING, [$this->currentSource]);
});
$this->deleteTemporaryTable();
}
$this->isInitialized = false;
$this->currentSource = null;
}
public function importCode(
@@ -72,6 +122,14 @@ class PostalCodeBaseImporter
float $centerLon,
int $centerSRID,
): void {
if (!$this->isInitialized) {
$this->initialize($refPostalCodeSource);
}
if ($this->currentSource !== $refPostalCodeSource) {
throw new \LogicException('Cannot store postal codes from different sources during same import. Execute finalize to commit inserts before changing the source');
}
$this->waitingForInsert[] = [
$countryCode,
$label,
@@ -88,10 +146,32 @@ class PostalCodeBaseImporter
}
}
private function initialize(string $source): void
{
$this->currentSource = $source;
$this->deleteTemporaryTable();
$this->createTemporaryTable();
$this->isInitialized = true;
}
private function createTemporaryTable(): void
{
$this->defaultConnection->executeStatement(self::CREATE_TEMP_TABLE);
}
private function deleteTemporaryTable(): void
{
$this->defaultConnection->executeStatement('DROP TABLE IF EXISTS chill_main_postal_code_temp');
}
private function doInsertPending(): void
{
if ([] == $this->waitingForInsert) {
return;
}
if (!\array_key_exists($forNumber = \count($this->waitingForInsert), $this->cachingStatements)) {
$sql = strtr(self::QUERY, [
$sql = strtr(self::INSERT_TEMP, [
'{{ values }}' => implode(
', ',
array_fill(0, $forNumber, self::VALUE)