Newer
Older
# Ne soyez plus l'esclave de Doctrine
---
## Hello!
<div style="width: 50%; float: left;">
Grégoire Paris
</div>
<div style="width: 50%; float: right;">
Maxime Veber
</div>
---
### Une application classique
<pre style="width: 50%; float: left;">
.
├── AppBundle
├── Admin
├── AppBundle.php
├── Controller
├── DataFixtures
├── DependencyInjection
├── Entity
├── Form
├── Listeners
├── OAuth
├── Resources
├── Tag
└── Twig
</pre>
---
### Vous avez dit entité ?
class TypicalArticle
{
private $id;
private $content;
public function getId() { return $this->id; }
public function getContent() { return $this->content; }
public function setContent($content)
{
$this->content = $content;
return $this;
}
}
```
Notes:
- Pas de validation
- Pas de règles métier
- Aucune logique (pas de tests nécessaires)
---
### Nous aimons le DDD
- Séparer le **domaine** de l'infrastructure
- Représenter les **règles métier** dans les entités
- Avoir une API expressive
---
### Doctrine n'a pas besoin de setters
```php
class NotThatTypicalArticle
private $id;
private $content;
public function __construct(string $content)
{
$this->content = $content;
}
// getters
Notes:
- intégrité du domaine
- Pas de setters par défaut, ni Doctrine ni sf n'en ont besoin
### Les règles métier
```php
class Article
{
private $id;
private $content;
public function __construct(string $content)
{
if (empty($content)) {
throw new ArticleHasNoContent();
}
$this->content = $content;
}
// getters
}
```
Note:
- Impossible de persister une entité invalide
- Validation compliquée quand on a trop de propriétés
- Pour qu'on puisse utiliser des constructeurs sans que ça crée de conflit avec
Doctrine, Doctrine utilise de la réflection ou de la désérialisation pour
hydrater les entités.
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
---
### Les value objects
```php
class ArticleContent
{
private $content;
private $lastModification;
public function __construct(string $content)
{
if (empty($content)) {
throw new ArticleHasNoContent();
}
$this->content = $content;
$this->lastModification = new \DatetimeImmutable();
}
}
```
Note:
- Déportation de la validation dans les value objects
- Début d'arborescence
- Doctrine Embeddables
- Custom types
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
---
### Les embeddables
```php
use Doctrine\ORM\Annotation as ORM;
class NiceArticle
{
private $uuid;
/** @ORM\Embedded(class = "ArticleContent") */
private $articleContent;
}
```
Note:
- À utiliser en cas de Value Object composite
- Des soucis avec la nullabilité, contournables avec un package
- Ne peuvent contenir des colonnes complexes
---
### Les custom types
```php
use Doctrine\DBAL\Platforms\AbstractPlatform as P;
final class ArticleContentType extends Type
{
public function convertToPHPValue($value, P $p): ArticleId
{
return new ArticleId($value);
}
public function convertToDatabaseValue($value, P $p): string
{
return (string) $value;
}
}
```
Note:
- Permet de contrôler qu'on respecte toujours les règles métier à
l'hydratation. Si ça crashe, c'est qu'il manque des migrations.
- La méthode `getName()` fait doublon avec le nom utilisé lors de
l'enregistrement du type dans le registre de type, et disparaître dès Doctrine 3
---
### Les constructeurs nommés
```php
class BetterArticle
{
public static function createFromNative(string $content)
{
return new self(new ArticleContent($content));
}
}
```
---