Я хочу связать закладки с тегами.Теги могут еще не существовать.
-- see source after questions for complete schema
Bookmark: _id_|…
Tag: _id_|_title_|…
bookmark_tag: _bookmark_id|_tag_id_
Текущее решение:
if tag exists : load tag (id) from db
else : store tag to db // non-atomic operation => duplicates possible
link tag with bookmark
Для каждого тега запрашивается база данных для его поиска и возврата местоположения (id
).Я не уверен, что это лучшее решение, но оно работает.
Более важной является операция неатома , выполненная при сохранении новой закладки.Теги имеют уникальный title
, поэтому сохранение дубликата приведет к исключению (PDOException
), которое закроет Doctrine EntitiyManager
.
При поиске и сохранении тега другой процесс может создатьтег, поэтому первый процесс будет прерван при создании дублирующейся сущности.
Я использую Symfony2 с Doctrine2.DB-Backend - это MySQL, но я надеюсь на общее решение (Doctrine2).
Вопросы
Как бы вы решили эту дилемму?Знаете ли вы лучшее решение?
Может ли Doctrine2 создавать недостающие теги по требованию и использовать существующие, когда addTag(Tag $tag)
- их Bookmark
?
Source / BookmarkController.php
/**
* Creates a new Bookmark entity.
*
* @Route("/create", name="bookmarks_create")
* @Method("post")
* @Template("XBookmarksBundle:Bookmark:new.html.twig")
*/
public function createAction()
{
$entity = new Bookmark();
$request = $this->getRequest();
$form = $this->createForm(new BookmarkType(), $entity);
$form->bindRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
// static tag for testing
$tagTitle = 'Lorem Ipsum';
$result = $this->getDoctrine()->getRepository('XBookmarksBundle:Tag')
->findBy(array('title' => $tagTitle));
// found tag => use it
if (count($result) > 0) {
$tag = $result[0];
}
// create new tag
else {
// FATAL not atomic => tag could exists now
$tag = new Tag();
$tag->setTitle($tagTitle);
}
// add tag to bookmark
$entity->addTag($tag);
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('bookmarks_show', array('id' => $entity->getId())));
}
Source / Bookmark.php
/**
* X\BookmarksBundle\Entity\Bookmark
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="X\BookmarksBundle\Entity\BookmarkRepository")
*/
class Bookmark
{
/**
* @var integer $id
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string $title
*
* @ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* @var string $uri
*
* @ORM\Column(name="uri", type="string", length=255)
*/
private $uri;
/**
* @var datetime $created_at
*
* @ORM\Column(name="created_at", type="datetime")
*/
private $created_at;
/**
* @var datetime $deleted_at
*
* @ORM\Column(name="deleted_at", type="datetime")
*/
private $deleted_at;
/**
* @ORM\ManyToMany(targetEntity="Tag", cascade={"persist", "remove"}) */
private $tags;
public function __construct()
{
$this->tags = new ArrayCollection();
}
public function getTags () {
return $this->tags;
}
public function addTag (Tag $tag) {
$this->tags->add($tag);
}
public function setTags ($tags) {
// TODO
}
/* DEFAULT SETTERS AND GETTERS FOR OTHER ATTRIBUTES */
}
Source / Tag.php
/**
* X\BookmarksBundle\Entity\Tag
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="X\BookmarksBundle\Entity\TagRepository")
*/
class Tag
{
/**
* @var integer $id
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string $title
*
* @ORM\Column(name="title", type="string", length=64, unique=true)
* -- ORM\Id
* -- ORM\GeneratedValue(strategy="NONE")
*/
private $title;
/**
* @var datetime $created_at
*
* @ORM\Column(name="created_at", type="datetime")
*/
private $created_at;
/**
* @var datetime $deleted_at
*
* @ORM\Column(name="deleted_at", type="datetime", nullable=true)
*/
private $deleted_at;
public function __construct () {
$this->created_at = new \DateTime('now');
$this->id=42;
$this->title="le fu";
}
}