学说实体创造策略

时间:2014-03-20 02:46:44

标签: php doctrine-orm

在Doctrine中使用实体时,添加某些"帮助"非常明显。实体的方法。举个例子;

class Post {
   protected $tags;

   public function __construct() {
      $this->tags = new ArrayCollection();
   }

   public function getTags() {
      return $this->tags;
   }

   public function addTag( $tagName ) {
      $tag = new Tag();
      $tag->setName( $tagName );

      $this->getTags()->add( $tag );
   }
}

这本身就有效,它可以提供干净的代码:

$post = new Post();
$post->addTag('Economy');

但是,如果Tag是多对多关系,并且在向帖子添加标记时我们想检查标记是否已存在,则会出现问题。例如。一个帖子可能没有标签' Economy'因此,从帖子的角度来看,将其添加到帖子中将是一个新标签。但是,如果标签经济'已存在于数据库中我们遇到了问题。我们希望我们的实体尽可能成为POPO,即不要引用实体经理或存储库。

解决这个问题的好策略是什么?

2 个答案:

答案 0 :(得分:0)

您需要在实体外部执行此操作。例如......

class Post {
    // ...

    public function addTag(Tag $tag) {
        $this->getTags()->add($tag);
    }
}

并且说,控制器...

$post = new Post();

// I can't remember if this returns null for no records found or throws
// an exception. Needs checking
$tag = $em->getRepository('Tag')->findOneByName('Economy');
if ($tag == null) {
    $tag = new Tag();
    $tag->setName('Economy');

    $em->persist($tag); // don't need to do this if you use cascade persist
}
$post->addTag($tag);

答案 1 :(得分:0)

我只是和别人谈论这件事。通过使用生命周期事件,这个问题实际上可以非常优雅地解决。

如果在Post实体上实现了预先持久的生命周期事件,则可以从生命周期事件对象访问实体管理器。从实体管理器可以请求存储库,并且可以调用检查重复项的添加标记的方法。

/**
 * @ORM\Entity(repositoryClass="PostRepository")
 * @ORM\HasLifecycleCallbacks()
 */
class Post {

    public function onPrePersist( LifecycleEventArgs $event ) {
       $entityManager = $event->getEntityManager();

       $repository = $entityManager->getRepository( get_class( $this ) );
       $repository->syncTags( $this );
    }

}

同时,在PostRepository

class PostRepository {

   public function syncTags( Post $post ) {
      $em = $this->getEntityManager();

      $tagsRepository = $em->getRepository('Tag');

      $tags = $post->getTags();

      foreach( $tags as $tag ) {
         $tagId = $tag->getId();

          if( empty( $tagId ) ) {
            $existingTag = $tagsRepository->findOneByName( $tag->getName() );

            if( ! empty( $existingTag ) ) {
               $tags->removeElement( $tag );
               $tags->add( $existingTag );
            }
         }
      }
   }

}

上面的syncTags方法基本上遍历Post标签。如果标签没有Id,我们可以假设它以前从未被保留过,因此被添加为新标签。然后我们检查是否已经有一个带有该名称的标签。如果没有,我们可以将其保留为新标记,否则我们会删除副本并添加现有标记实体。

我们现在可以保持标签添加逻辑尽可能简单:

$post = new Post();
$post->addTag('Economy');

如果'经济'标签已经存在,则会使用它,否则将创建新标签。