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