Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/production-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
push:
branches:
- main
- main-*
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding 'main-' to the production release workflow triggers means that any branch starting with 'main-' will trigger production deployments. This could be risky if developers create branches like 'main-feature' or 'main-hotfix' for development purposes. Consider whether this pattern is intentional and documented. If the intent is to support multiple main branches for different environments, consider using more specific patterns like 'main-v' or documenting the naming convention clearly.

Copilot uses AI. Check for mistakes.
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10

Expand Down
1 change: 1 addition & 0 deletions config/packages/doctrine.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ doctrine:
POINT: App\Doctrine\Functions\PointFunction
MBRContains: App\Doctrine\Functions\MBRContains
ST_MakeEnvelope: App\Doctrine\Functions\STMakeEnvelope
REPLACE: App\Doctrine\Functions\ReplaceFunction
39 changes: 39 additions & 0 deletions src/Doctrine/Functions/ReplaceFunction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace App\Doctrine\Functions;

use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;

/**
* ReplaceFunction ::= "REPLACE" "(" StringPrimary "," StringPrimary "," StringPrimary ")".
*/
class ReplaceFunction extends FunctionNode
{
public mixed $subject;
public mixed $search;
public mixed $replace;

public function parse(Parser $parser): void
{
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->subject = $parser->StringPrimary();
$parser->match(TokenType::T_COMMA);
$this->search = $parser->StringPrimary();
$parser->match(TokenType::T_COMMA);
$this->replace = $parser->StringPrimary();
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}

public function getSql(SqlWalker $sqlWalker): string
{
return 'REPLACE(' .
$this->subject->dispatch($sqlWalker) . ', ' .
$this->search->dispatch($sqlWalker) . ', ' .
$this->replace->dispatch($sqlWalker) .
')';
}
}
2 changes: 1 addition & 1 deletion src/Entity/Cim11.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
use Symfony\Component\Serializer\Annotation\Groups;

#[ORM\Entity(repositoryClass: Cim11Repository::class)]
#[ApiFilter(Cim11Filter::class, properties: ['search', 'ids', 'cim10_code'])]
#[ApiFilter(Cim11Filter::class, properties: ['search', 'ids', 'cim10Code'])]
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change from 'cim10_code' (with underscore) to 'cim10Code' (camelCase) in the ApiFilter properties is a breaking API change. Any clients currently using the 'cim10_code' query parameter will need to update their code to use 'cim10Code' instead. This should be documented in release notes or, if backward compatibility is required, both parameter names should be supported during a deprecation period.

Suggested change
#[ApiFilter(Cim11Filter::class, properties: ['search', 'ids', 'cim10Code'])]
#[ApiFilter(Cim11Filter::class, properties: ['search', 'ids', 'cim10Code', 'cim10_code' => 'cim10Code'])]

Copilot uses AI. Check for mistakes.
#[ORM\Table(name: 'cim_11')]
#[ORM\Index(columns: ['code'])]
#[UniqueEntity(['code', 'whoId'])]
Expand Down
20 changes: 18 additions & 2 deletions src/Repository/InseeCommune1943Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,34 @@
*/
class InseeCommune1943Repository extends ServiceEntityRepository
{
use SearchNormalizationTrait;

public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, InseeCommune1943::class);
}

public function searchByNameAndDate(string $search, DateTime $date): array
{
// Normalize hyphens to spaces at the SQL level for flexible matching
// MySQL collations are typically accent-insensitive by default (utf8mb4_unicode_ci)
return $this->createQueryBuilder('c')
->where('REPLACE(c.nomTypographie, \'-\', \' \') LIKE :search')
->andWhere('(c.dateDebut IS NULL OR c.dateDebut <= :date)')
->andWhere('(c.dateFin IS NULL OR c.dateFin >= :date)')
->setParameter('search', '%' . str_replace('-', ' ', $search) . '%')
->setParameter('date', $date)
->getQuery()
->getResult();
}

public function findByCodeAndDate(string $code, DateTime $date): array
{
return $this->createQueryBuilder('c')
->where('c.nomTypographie LIKE :search')
->where('c.codeCommune = :code')
->andWhere('(c.dateDebut IS NULL OR c.dateDebut <= :date)')
->andWhere('(c.dateFin IS NULL OR c.dateFin >= :date)')
->setParameter('search', "$search%")
->setParameter('code', $code)
->setParameter('date', $date)
->getQuery()
->getResult();
Expand Down
17 changes: 15 additions & 2 deletions src/Repository/InseeCommuneRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,29 @@
*/
class InseeCommuneRepository extends ServiceEntityRepository
{
use SearchNormalizationTrait;

public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, InseeCommune::class);
}

public function searchByName(string $search): array
{
// Normalize hyphens to spaces at the SQL level for flexible matching
// MySQL collations are typically accent-insensitive by default (utf8mb4_unicode_ci)
return $this->createQueryBuilder('c')
->where('REPLACE(c.nomEnClair, \'-\', \' \') LIKE :search')
->setParameter('search', '%' . str_replace('-', ' ', $search) . '%')
->getQuery()
->getResult();
}

public function findByCode(string $code): array
{
return $this->createQueryBuilder('c')
->where('c.nomEnClair LIKE :search')
->setParameter('search', "$search%")
->where('c.codeCommune = :code')
->setParameter('code', $code)
->getQuery()
->getResult();
}
Expand Down
22 changes: 19 additions & 3 deletions src/Repository/InseePays1943Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
*/
class InseePays1943Repository extends ServiceEntityRepository
{
use SearchNormalizationTrait;

public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, InseePays1943::class);
Expand All @@ -25,15 +27,29 @@ public function __construct(ManagerRegistry $registry)
/**
* Note : might consider searching also the libelleOfficiel field.
*
* Search for countries matching a name that existed at a given date.
* Search for countries matching a name.
* Date constraints removed to allow searching for historical countries (e.g., Algeria before 1962).
*/
public function searchByNameAndDate(string $search, DateTime $date): array
{
// Normalize hyphens to spaces at the SQL level for flexible matching
// MySQL collations are typically accent-insensitive by default (utf8mb4_unicode_ci)
return $this->createQueryBuilder('p')
->where('p.libelleCog LIKE :search')
->where('REPLACE(p.libelleCog, \'-\', \' \') LIKE :search')
// Date constraints removed to allow historical country searches (e.g., Algeria before 1962)
->setParameter('search', '%' . str_replace('-', ' ', $search) . '%')
->getQuery()
->getResult();
Comment on lines 33 to +42
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment and docblock state that date constraints were removed to allow searching for historical countries (e.g., Algeria before 1962), but this behavior change is inconsistent with findByCodeAndDate which still enforces date constraints (lines 50-51). This creates an inconsistency where searching by name ignores dates while searching by code respects them. Either both methods should handle dates consistently, or the difference in behavior should be clearly documented and justified.

Copilot uses AI. Check for mistakes.
}

public function findByCodeAndDate(string $code, DateTime $date): array
{
return $this->createQueryBuilder('p')
->where('p.codePays = :code')
// Keep date constraints for find by code
->andWhere('(p.dateDebut IS NULL OR p.dateDebut <= :date)')
->andWhere('(p.dateFin IS NULL OR p.dateFin >= :date)')
->setParameter('search', "%$search%")
->setParameter('code', $code)
->setParameter('date', $date)
->getQuery()
->getResult();
Expand Down
17 changes: 15 additions & 2 deletions src/Repository/InseePaysRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,29 @@
*/
class InseePaysRepository extends ServiceEntityRepository
{
use SearchNormalizationTrait;

public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, InseePays::class);
}

public function searchByName(string $search): array
{
// Normalize hyphens to spaces at the SQL level for flexible matching
// MySQL collations are typically accent-insensitive by default (utf8mb4_unicode_ci)
return $this->createQueryBuilder('p')
->where('REPLACE(p.libelleCog, \'-\', \' \') LIKE :search')
->setParameter('search', '%' . str_replace('-', ' ', $search) . '%')
Comment on lines +28 to +32
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the REPLACE function directly in SQL (e.g., 'REPLACE(p.libelleCog, ...)') prevents the database from using indexes on the libelleCog column, which could significantly impact query performance on large datasets. Consider creating a computed/generated column with pre-normalized values or using full-text search capabilities if performance becomes an issue. The same concern applies to all repository methods using REPLACE in WHERE clauses.

Suggested change
// Normalize hyphens to spaces at the SQL level for flexible matching
// MySQL collations are typically accent-insensitive by default (utf8mb4_unicode_ci)
return $this->createQueryBuilder('p')
->where('REPLACE(p.libelleCog, \'-\', \' \') LIKE :search')
->setParameter('search', '%' . str_replace('-', ' ', $search) . '%')
// Normalize hyphens to spaces at the PHP level for flexible matching
// MySQL collations are typically accent-insensitive by default (utf8mb4_unicode_ci)
$normalized = str_replace('-', ' ', $search);
$patternWithSpaces = '%' . $normalized . '%';
$patternWithHyphens = '%' . str_replace(' ', '-', $normalized) . '%';
return $this->createQueryBuilder('p')
->where('p.libelleCog LIKE :patternWithSpaces OR p.libelleCog LIKE :patternWithHyphens')
->setParameter('patternWithSpaces', $patternWithSpaces)
->setParameter('patternWithHyphens', $patternWithHyphens)

Copilot uses AI. Check for mistakes.
->getQuery()
->getResult();
}

public function findByCode(string $code): array
{
return $this->createQueryBuilder('p')
->where('p.libelleCog LIKE :search')
->setParameter('search', "%$search%")
->where('p.codePays = :code')
->setParameter('code', $code)
->getQuery()
->getResult();
}
Expand Down
18 changes: 18 additions & 0 deletions src/Repository/SearchNormalizationTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace App\Repository;

trait SearchNormalizationTrait
{
/**
* Normalize search term by replacing hyphens with spaces.
*
* Note: Accent normalization is handled by MySQL's collation (utf8mb4_unicode_ci)
* which is accent-insensitive by default, so we don't need to remove accents here.
*/
private function normalizeSearchTerm(string $search): string
{
// Replace hyphens with spaces for flexible matching
return str_replace('-', ' ', $search);
}
Comment on lines +7 to +17
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The normalizeSearchTerm method defined in this trait is never used in any of the repositories that include it (InseeCommuneRepository, InseePaysRepository, InseeCommune1943Repository, InseePays1943Repository). All repositories perform the normalization inline using str_replace directly in their query methods. Consider either removing this unused method or refactoring the repositories to use it consistently.

Suggested change
/**
* Normalize search term by replacing hyphens with spaces.
*
* Note: Accent normalization is handled by MySQL's collation (utf8mb4_unicode_ci)
* which is accent-insensitive by default, so we don't need to remove accents here.
*/
private function normalizeSearchTerm(string $search): string
{
// Replace hyphens with spaces for flexible matching
return str_replace('-', ' ', $search);
}

Copilot uses AI. Check for mistakes.
}
22 changes: 17 additions & 5 deletions src/Service/BirthPlaceService.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,14 @@ public function searchBirthPlaces(string $search): array
/** @var InseePaysRepository $paysRepository */
$paysRepository = $this->em->getRepository(InseePays::class);

$communeResults = $communeRepository->searchByName($search);
$paysResults = $paysRepository->searchByName($search);
// Check if search is a 5-digit code
if (preg_match('/^\d{5}$/', $search)) {
$communeResults = $communeRepository->findByCode($search);
$paysResults = $paysRepository->findByCode($search);
} else {
$communeResults = $communeRepository->searchByName($search);
$paysResults = $paysRepository->searchByName($search);
}

return $this->mapResultsToDTO($communeResults, $paysResults);
}
Expand All @@ -42,7 +48,7 @@ public function getBirthPlaceByCode(string $code, ?string $dateOfBirth): ?BirthP
if ($dateOfBirth) {
try {
$dateOfBirth = new DateTime($dateOfBirth);
} catch (Exception $e) {
} catch (Exception) {
// If the date is invalid, we ignore it and proceed with the search
$dateOfBirth = null;
}
Expand Down Expand Up @@ -97,8 +103,14 @@ public function searchBirthPlacesByDate(string $search, DateTime $date): array
/** @var InseePays1943Repository $pays1943Repository */
$pays1943Repository = $this->em->getRepository(InseePays1943::class);

$communeResults = $commune1943Repository->searchByNameAndDate($search, $date);
$paysResults = $pays1943Repository->searchByNameAndDate($search, $date);
// Check if search is a 5-digit code
if (preg_match('/^\d{5}$/', $search)) {
$communeResults = $commune1943Repository->findByCodeAndDate($search, $date);
$paysResults = $pays1943Repository->findByCodeAndDate($search, $date);
} else {
$communeResults = $commune1943Repository->searchByNameAndDate($search, $date);
$paysResults = $pays1943Repository->searchByNameAndDate($search, $date);
}

return $this->mapResultsToDTO($communeResults, $paysResults);
}
Expand Down
11 changes: 10 additions & 1 deletion src/StateProvider/BirthPlacesProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ private function provideItem(Operation $operation, array $uriVariables = [], arr
throw new RuntimeException('Missing "code" in URI variables');
}

return $this->birthPlaceService->getBirthPlaceByCode($uriVariables['code'], $context['filters']['filters'] ?? null);
$request = $this->requestStack->getCurrentRequest();
if (!$request) {
throw new LogicException('No current request available');
}

$dateOfBirth = $request->query->get('dateOfBirth');

$code = explode('.', $uriVariables['code'])[0];
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code parsing logic using explode('.', $uriVariables['code'])[0] assumes that codes may contain a file extension suffix (e.g., "75056.json"). However, this behavior is not documented or validated. If the intent is to handle format suffixes in the URI, this should be done at the API Platform routing/format negotiation level, not in the state provider. Additionally, there's no validation to ensure that the split actually produces a valid code - if the code already doesn't contain a dot, this works fine, but the logic could be clearer about its intent.

Suggested change
$code = explode('.', $uriVariables['code'])[0];
$code = (string) $uriVariables['code'];
$dotPosition = strpos($code, '.');
if ($dotPosition !== false) {
$code = substr($code, 0, $dotPosition);
if ($code === '') {
throw new RuntimeException('Invalid "code" format in URI variables');
}
}

Copilot uses AI. Check for mistakes.

Comment on lines +91 to +92
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After extracting the code using explode, there's no validation that the resulting code is valid (e.g., exactly 5 digits for communes/countries). An input like '123.json' would result in code='123', which is invalid but would still be passed to the service. Consider adding validation after the explode operation to ensure the code matches expected patterns before querying the database.

Suggested change
$code = explode('.', $uriVariables['code'])[0];
$code = explode('.', (string) $uriVariables['code'])[0];
if (!preg_match('/^\d{5}$/', $code)) {
throw new RuntimeException('Invalid "code" format');
}

Copilot uses AI. Check for mistakes.
return $this->birthPlaceService->getBirthPlaceByCode($code, $dateOfBirth);
}
}
Loading
Loading