# 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é ?

```php
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.

---
### 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

---
### 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));
    }
}

```

---
# Emoji test

💩

Note:
speaker notes FTW!