Doctrine2 les meilleures pratiques, les entités utilisent les services devraient-ils?
-
11-10-2019 - |
Question
J'ai posé une question similaire un certain temps: Utilisation du modèle Data Mapper, lorsque les entités (objets de domaine) connaissent le mappeur Cependant, il est générique et Je suis vraiment intéressé par la façon d'accomplir quelques choses avec Doctrine2 spécifiquement .
Voici un modèle simple exemple: Chaque Thing
peut avoir un Vote
d'un User
, un User
peut recevoir plus d'une Vote
mais seulement les derniers comptes de Vote
. Parce que d'autres données (Msssage
, etc.) est liée à la Vote
, lorsque le second Vote
est placé le Vote
d'origine ne peut pas simplement être mis à jour, il doit être remplacé.
Actuellement Thing
a cette fonction:
public function addVote($vote)
{
$vote->entity = $this;
}
Et Vote
prend soin de mettre en place la relation:
public function setThing(Model_Thing $thing)
{
$this->thing = $thing;
$thing->votes[] = $this;
}
Il me semble que, pour assurer un User
a seulement le dernier Vote
compté est quelque chose que le Thing
devrait assurer, et pas une couche de service .
Donc, pour maintenir que dans le modèle, la nouvelle fonction Thing
:
public function addVote($vote)
{
foreach($this->votes as $v){
if($v->user === $vote->user){
//remove vote
}
}
$vote->entity = $this;
}
Comment puis-je supprimer le Vote
à partir du modèle de domaine? Devrais-je détends Vote::setThing()
d'accepter un NULL
? Dois-je comprendre une sorte de couche de service qui Thing
peut utiliser pour supprimer le vote? Une fois que les votes commencent à accumuler, que foreach
va être lente - si une couche de service soit utilisé pour permettre Thing
de rechercher un Vote
sans avoir à charger toute la collection
Je penche définitivement vers l'utilisation d'une couche de service lumière; cependant, est-il une meilleure façon de gérer ce genre de chose avec Doctrine2, ou suis-je dirige dans la bonne direction?
La solution
Je vote pour la couche de service. J'ai souvent eu du mal à essayer d'ajouter autant de logique sur l'entité elle-même, et moi-même simplement frustré. Sans accès à l'EntityManager, vous êtes tout simplement pas en mesure d'effectuer la requête logique, et vous vous trouverez en utilisant beaucoup d'O (n) opérations ou toute ensembles de relation de chargement paresseux lorsque vous avez seulement besoin de quelques enregistrements (ce qui est super boiteux par rapport à tous les avantages dql offres).
Si vous avez besoin d'aide pour passer au-dessus de l'idée que le modèle de domaine anémiques est toujours un anti-modèle, voir cette présentation par Matthew Weier O'Phinney ou cette question .
Et alors que je pourrais être mal interprété la terminologie, je ne suis pas complètement convaincu que les entités doivent être les seuls objets autorisés dans votre modèle de domaine. Je facilement considérer que la somme des objets d'entité et de leurs services constitue le modèle. Je pense que l'anti-modèle se pose lorsque vous finissez par écrire une couche de service qui paie peu ou pas d'attention à la séparation des préoccupations.
J'ai souvent flirté avec l'idée d'avoir tous les objets de mon entité proxy certaines méthodes de la couche de service:
public function addVote($vote)
{
$this->_service->addVoteToThing($vote, $thing);
}
Cependant, étant donné que la doctrine n'a pas système d'événements de rappel type sur l'hydratation de l'objet, je n'ai pas trouvé une façon élégante d'injecter l'objet de service.
Autres conseils
Mon conseil serait de mettre toute la logique de requête dans un EntityRepository puis faire une interface sortir un peu comme:
class BlogPostRepository extends EntityRepository implements IBlogPostRepository {}
cette façon, vous pouvez utiliser l'interface dans vos tests unitaires pour les objets de service et aucune dépendance à l'égard EntityManager est nécessaire.