Doctrine2 Best Practice, Should Entities use Servi

2019-02-07 11:15发布

I asked a similar question a while back: Using the Data Mapper Pattern, Should the Entities (Domain Objects) know about the Mapper? However, it was generic and I'm really interested in how to accomplish a few things with Doctrine2 specifically.

Here's a simple example model: Each Thing can have a Vote from a User, a User may cast more than one Vote but only the last Vote counts. Because other data (Msssage, etc) is related to the Vote, when the second Vote is placed the original Vote can't just be updated, it needs to be replaced.

Currently Thing has this function:

public function addVote($vote)
{
  $vote->entity = $this;
}

And Vote takes care of setting up the relationship:

public function setThing(Model_Thing $thing)
{
  $this->thing = $thing;
  $thing->votes[] = $this;
} 

It seems to me that ensuring a User only has the last Vote counted is something the Thing should ensure, and not some service layer.

So to keep that in the Model, the new Thing function:

public function addVote($vote)
{
  foreach($this->votes as $v){
    if($v->user === $vote->user){
      //remove vote
    }
  }
  $vote->entity = $this;
}

So how do I remove the Vote from within the Domain Model? Should I relax Vote::setThing() to accept a NULL? Should I involve some kind of service layer that Thing can use to remove the vote? Once the votes start accumulating, that foreach is going to be slow - should a service layer be used to allow Thing to search for a Vote without having to load the entire collection?

I'm definitely leaning toward using a light service layer; however, is there a better way to handle this type of thing with Doctrine2, or am I heading in the right direction?

2条回答
相关推荐>>
2楼-- · 2019-02-07 12:09

I vote for the service layer. I've often struggled with trying to add as much logic on the Entity itself, and simply frustrated myself. Without access to the EntityManager, you're simply not able to perform query logic, and you'll find yourself using a lot of O(n) operations or lazy-loading entire relationship sets when you only need a few records (which is super lame when compared to all the advantages DQL offers).

If you need some assistance getting over the idea that the Anemic Domain Model is always an anti-pattern, see this presentation by Matthew Weier O'Phinney or this question.

And while I could be misinterpreting the terminology, I'm not completely convinced that Entities have to be the only objects allowed in your Domain Model. I would easily consider that the sum of Entity objects and their Services constitutes the Model. I think the anti-pattern arises when you end up writing a service layer that pays little to no attention to separation of concerns.

I've often flirted with the idea of having all my entity objects proxy some methods to the service layer:

public function addVote($vote)
{
   $this->_service->addVoteToThing($vote, $thing);
}

However, since Doctrine does not have any kind callback event system on object hydration, I haven't found an elegant way to inject the service object.

查看更多
混吃等死
3楼-- · 2019-02-07 12:10

My advice would be to put all the query logic into an EntityRepository and then make an interface out of it sort of like:

class BlogPostRepository extends EntityRepository implements IBlogPostRepository {}

that way you can use the interface in your unit-tests for the service objects and no dependency on the EntityManager is required.

查看更多
登录 后发表回答