Skip to content

Commit 78b4f01

Browse files
committed
Ajout d'une entité parente pour l'id
1 parent fba0907 commit 78b4f01

23 files changed

Lines changed: 196 additions & 131 deletions

File tree

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
---
2+
Id: ADR-003
3+
Date: 2026-06-29
4+
Statut: Proposé
5+
---
6+
7+
# Les entités Doctrine héritent d'une classe parente
8+
9+
## Contexte
10+
11+
Avec l'ajout de la baseline PHPStan et le passage au niveau 10, il y a pas mal d'endroits dans le code où l'id nullable
12+
des entités pose problème.
13+
14+
Par exemple, quand on récupère une liste d'entités depuis un repository, on sait que l'id est présent, mais pas PHPStan
15+
car la propriété reste nullable dans la classe de l'entité.
16+
17+
Cela force des vérifications qui n'apportent pas grand chose et rendent le code plus difficile à lire et naviguer.
18+
19+
Par exemple :
20+
21+
```php
22+
#[ORM\Entity]
23+
#[ORM\Table(name: 'exemple')]
24+
class Entity
25+
{
26+
#[ORM\Id]
27+
#[ORM\GeneratedValue]
28+
#[ORM\Column]
29+
public ?int $id = null;
30+
31+
#[ORM\Column(length: 255, nullable: true)]
32+
public string $foo = null;
33+
}
34+
35+
class ExampleRepository
36+
{
37+
/**
38+
* @return array<Entity>
39+
*/
40+
public function all(): array { /* return ... */ }
41+
}
42+
43+
$entities = $exempleRepository->all();
44+
45+
$map = [];
46+
foreach ($entities as $entity) {
47+
// Cette ligne va déclencher une erreur PHPStan car l'id pourrait être nullable,
48+
// alors qu'on sait ici que ce n'est pas le cas.
49+
$map[$entity->id] = $entity->foo;
50+
51+
// Il faudrait faire ça à chaque fois :
52+
if ($entity->id === null) {
53+
continue;
54+
}
55+
56+
$map[$entity->id] = $entity->foo;
57+
}
58+
```
59+
60+
## Décision
61+
62+
Les entités Doctrine héritent d'une classe abstraite contenant l'id et une méthode pour vérifier sa présence.
63+
64+
Cela permet à PHPStan de mieux analyser le code, tout en conservant une certaine sécurité. Si une entité n'est pas
65+
persistée et qu'on tente de lire son id, cela déclenche une erreur.
66+
67+
### Détails d'implémentation
68+
69+
```php
70+
use AppBundle\Doctrine\Entity;
71+
use Doctrine\ORM\Mapping as ORM;
72+
73+
#[ORM\Entity]
74+
#[ORM\Table(name: 'exemple')]
75+
class Exemple extends Entity
76+
{
77+
#[ORM\Column(length: 255, nullable: false)]
78+
public string $nonNullbale;
79+
80+
#[ORM\Column(length: 255, nullable: true)]
81+
public ?string $nullable = null;
82+
}
83+
```
84+
85+
Et à l'utilisation :
86+
87+
```php
88+
if ($exemple->isPersisted()) {
89+
// $exemple->id est initialisé et non-null
90+
}
91+
```
92+
93+
## Alternatives considérées
94+
95+
1. **Un trait** : C'est plus difficile et lent à analyser pour PHPStan qu'une classe parente.
96+
2. **Vérifier l'id à chaque fois** : Le code devient moins lisible pour peu d'intérêt.
97+
98+
## Conséquences
99+
100+
### Positives
101+
102+
Quand on récupère une ou plusieurs entités depuis la base de données, plus besoin de vérifier la présence de l'id dans
103+
l'instance.
104+
105+
Si on tente d'accéder à l'id d'une entité à un endroit non vérifié, une erreur survient fort et au bon endroit (au lieu
106+
de trimballer un `null` plus loin dans le code).
107+
108+
### Négatives
109+
110+
Toutes les entités doivent hériter d'une classe parente.
111+
112+
Cela ne fonctionne qu'avec des entités qui ont un id entier auto-incrément.
113+
114+
## Références
115+
116+
Analyse des traits par PHPStan : https://phpstan.org/blog/how-phpstan-analyses-traits
117+
118+
Exemple PHPStan : https://phpstan.org/r/3f3354d7-d6f5-4493-bfbd-3f7a37cf8d32

phpstan-baseline.php

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -115,18 +115,6 @@
115115
'count' => 1,
116116
'path' => __DIR__ . '/sources/Afup/Corporate/Page.php',
117117
];
118-
$ignoreErrors[] = [
119-
'message' => '#^Parameter \\#1 \\$parentId of method AppBundle\\\\Site\\\\Entity\\\\Repository\\\\FeuilleRepository\\:\\:getFeuillesEnfant\\(\\) expects int, int\\|null given\\.$#',
120-
'identifier' => 'argument.type',
121-
'count' => 3,
122-
'path' => __DIR__ . '/sources/Afup/Corporate/Page.php',
123-
];
124-
$ignoreErrors[] = [
125-
'message' => '#^Possibly invalid array key type int\\|null\\.$#',
126-
'identifier' => 'offsetAccess.invalidOffset',
127-
'count' => 1,
128-
'path' => __DIR__ . '/sources/Afup/Corporate/Page.php',
129-
];
130118
$ignoreErrors[] = [
131119
'message' => '#^Cannot access offset \'elements\' on mixed\\.$#',
132120
'identifier' => 'offsetAccess.nonOffsetAccessible',

sources/AppBundle/Accounting/Entity/Account.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,13 @@
44

55
namespace AppBundle\Accounting\Entity;
66

7+
use AppBundle\Doctrine\Entity;
78
use Doctrine\ORM\Mapping as ORM;
89

910
#[ORM\Entity]
1011
#[ORM\Table(name: 'compta_compte')]
11-
class Account
12+
class Account extends Entity
1213
{
13-
#[ORM\Id]
14-
#[ORM\GeneratedValue]
15-
#[ORM\Column]
16-
public ?int $id = null;
17-
1814
#[ORM\Column(name: 'nom_compte', length: 45, nullable: false)]
1915
public string $name;
2016

sources/AppBundle/Accounting/Entity/Category.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,13 @@
44

55
namespace AppBundle\Accounting\Entity;
66

7+
use AppBundle\Doctrine\Entity;
78
use Doctrine\ORM\Mapping as ORM;
89

910
#[ORM\Entity]
1011
#[ORM\Table(name: 'compta_categorie')]
11-
class Category
12+
class Category extends Entity
1213
{
13-
#[ORM\Id]
14-
#[ORM\GeneratedValue]
15-
#[ORM\Column]
16-
public ?int $id = null;
17-
1814
#[ORM\Column(name: 'categorie', length: 255, nullable: false)]
1915
public string $name;
2016

sources/AppBundle/Accounting/Entity/Event.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,13 @@
44

55
namespace AppBundle\Accounting\Entity;
66

7+
use AppBundle\Doctrine\Entity;
78
use Doctrine\ORM\Mapping as ORM;
89

910
#[ORM\Entity]
1011
#[ORM\Table(name: 'compta_evenement')]
11-
class Event
12+
class Event extends Entity
1213
{
13-
#[ORM\Id]
14-
#[ORM\GeneratedValue]
15-
#[ORM\Column]
16-
public ?int $id = null;
17-
1814
#[ORM\Column(name: 'evenement', length: 50, nullable: false)]
1915
public string $name;
2016

sources/AppBundle/Accounting/Entity/Operation.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,13 @@
44

55
namespace AppBundle\Accounting\Entity;
66

7+
use AppBundle\Doctrine\Entity;
78
use Doctrine\ORM\Mapping as ORM;
89

910
#[ORM\Entity]
1011
#[ORM\Table(name: 'compta_operation')]
11-
class Operation
12+
class Operation extends Entity
1213
{
13-
#[ORM\Id]
14-
#[ORM\GeneratedValue]
15-
#[ORM\Column]
16-
public ?int $id = null;
17-
1814
#[ORM\Column(name: 'operation', length: 255, nullable: false)]
1915
public string $name;
2016
}

sources/AppBundle/Accounting/Entity/Payment.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,13 @@
44

55
namespace AppBundle\Accounting\Entity;
66

7+
use AppBundle\Doctrine\Entity;
78
use Doctrine\ORM\Mapping as ORM;
89

910
#[ORM\Entity]
1011
#[ORM\Table(name: 'compta_reglement')]
11-
class Payment
12+
class Payment extends Entity
1213
{
13-
#[ORM\Id]
14-
#[ORM\GeneratedValue]
15-
#[ORM\Column]
16-
public ?int $id = null;
17-
1814
#[ORM\Column(name: 'reglement', length: 50, nullable: false)]
1915
public string $name;
2016

sources/AppBundle/Accounting/Entity/Produit.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,13 @@
55
namespace AppBundle\Accounting\Entity;
66

77
use AppBundle\Accounting\TvaTaux;
8+
use AppBundle\Doctrine\Entity;
89
use Doctrine\ORM\Mapping as ORM;
910

1011
#[ORM\Entity]
1112
#[ORM\Table(name: 'compta_produit')]
12-
class Produit
13+
class Produit extends Entity
1314
{
14-
#[ORM\Id]
15-
#[ORM\GeneratedValue]
16-
#[ORM\Column]
17-
public ?int $id = null;
18-
1915
#[ORM\Column(length: 255, nullable: false)]
2016
public string $reference;
2117

sources/AppBundle/Accounting/Entity/Rule.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,13 @@
44

55
namespace AppBundle\Accounting\Entity;
66

7+
use AppBundle\Doctrine\Entity;
78
use Doctrine\ORM\Mapping as ORM;
89

910
#[ORM\Entity]
1011
#[ORM\Table(name: 'compta_regle')]
11-
class Rule
12+
class Rule extends Entity
1213
{
13-
#[ORM\Id]
14-
#[ORM\GeneratedValue]
15-
#[ORM\Column]
16-
public ?int $id = null;
17-
1814
#[ORM\Column(length: 255, nullable: false)]
1915
public string $label;
2016

sources/AppBundle/AssembleeGenerale/Entity/Presence.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,14 @@
66

77
use AppBundle\AssembleeGenerale\Enum\PresenceEtat;
88
use AppBundle\Association\Entity\Utilisateur;
9+
use AppBundle\Doctrine\Entity;
910
use AppBundle\Doctrine\Type\UnixTimestampType;
1011
use Doctrine\ORM\Mapping as ORM;
1112

1213
#[ORM\Entity]
1314
#[ORM\Table(name: 'afup_presences_assemblee_generale')]
14-
class Presence
15+
class Presence extends Entity
1516
{
16-
#[ORM\Id]
17-
#[ORM\GeneratedValue]
18-
#[ORM\Column]
19-
public ?int $id = null;
20-
2117
#[ORM\Column(type: UnixTimestampType::NAME, nullable: false)]
2218
public \DateTime $date;
2319

0 commit comments

Comments
 (0)