Атомарный поиск и создание сущностей по требованию с помощью SQL / Doctrine2 - PullRequest
1 голос
/ 31 августа 2011

Я хочу связать закладки с тегами.Теги могут еще не существовать.

-- 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";
    }
}

1 Ответ

1 голос
/ 01 сентября 2011

Вы можете сделать это так:

// ...
// create new tag
else {
    try {
        $tag = new Tag();
        $tag->setTitle($tagTitle);
        $em->persist($tag);
        $em->flush();
    } catch (PDOException $e) {
        // also you could check for exception code here
        $result = $this->getDoctrine()->getRepository('XBookmarksBundle:Tag')
            ->findBy(array('title' => $tagTitle));

        // found tag
        if (count($result) > 0) {
            $tag = $result[0];
        } else {
            throw new Exception("Something went wrong");
        }

    }
}
// ...
...