dongpiao0731 2017-02-19 21:26
浏览 47
已采纳

Symfony:为什么持续尝试插入对象管理器检索的对象?

Apologies if this has been asked before. I'm still trying to grasp Symfony terms to my searching isn't that good yet!

I have (for the purpose of this question) two entities: Article and Author.

I'm embedding the Author form into the add Article form. I'd like to check if the email already exists, and, if it does, just update that record (the corresponding name) instead of adding a duplicate. I'm using a transformer to do this.

I am able to find an existing Author in my transformer. However, when I persist the Article in the controller, I get the error:

"An exception occurred while executing 'INSERT INTO author... Integrity constraint violation: 1062 Duplicate entry" ... etc...

I'm really confused because my understanding is that persist should update the record that I just retrieved from the database!

Article Controller:

    public function newAction(Request $request){
    $article = new Article();
    $form = $this->createForm('AppBundle\Form\ArticleType', $article);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {

        //Set the timestamps on article/author.
        $date = new \DateTime("now");
        $article->setCreatedDate($date);
        $article->getAuthor()->setCreatedDate($date);

        //Persist to database.
        $em = $this->getDoctrine()->getManager();
        $em->persist($article);
        $em->flush($article);

        return $this->redirectToRoute('article_show', array('id' => $article->getId()));
    }

    return $this->render('article/new.html.twig', array(
        'article' => $article,
        'form' => $form->createView(),
    ));}

Article Type:

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;

class ArticleType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name')->add('description')->add('thumbnail');

        //We'll handle dates. Don't want users to access that.
        //$builder->add('createdDate');

        $builder->add('author', AuthorType::class, array("label" => FALSE));
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\Article'
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_article';
    }


}

Article Entity:

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
/**
 * Article
 *
 * @ORM\Table(name="article")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\ArticleRepository")
 */
class Article
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255, unique=true)
     */
    private $name;

    /**
     * @var string
     *
     * @ORM\Column(name="description", type="text", nullable=true)
     */
    private $description;

    /**
     * @var string
     *
     * @ORM\Column(name="thumbnail", type="string", length=255, nullable=true)
     */
    private $thumbnail;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="created_date", type="datetime")
     */
    private $createdDate;

    /**
     * @ORM\ManyToOne(targetEntity="Author", inversedBy="articles", cascade={"persist"})
     * @ORM\JoinColumn(name="author_id", referencedColumnName="id")
     * @Assert\Valid()
     */
    private $author;


    /**
     * @ORM\OneToMany(targetEntity="Review", mappedBy="article")
     */
    private $reviews;

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

    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     *
     * @return Article
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set description
     *
     * @param string $description
     *
     * @return Article
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get description
     *
     * @return string
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Set thumbnail
     *
     * @param string $thumbnail
     *
     * @return Article
     */
    public function setThumbnail($thumbnail)
    {
        $this->thumbnail = $thumbnail;

        return $this;
    }

    /**
     * Get thumbnail
     *
     * @return string
     */
    public function getThumbnail()
    {
        return $this->thumbnail;
    }

    /**
     * Set createdDate
     *
     * @param \DateTime $createdDate
     *
     * @return Article
     */
    public function setCreatedDate($createdDate)
    {
        $this->createdDate = $createdDate;

        return $this;
    }

    /**
     * Get createdDate
     *
     * @return \DateTime
     */
    public function getCreatedDate()
    {
        return $this->createdDate;
    }

    /**
     * Set authorId
     *
     * @param integer $authorId
     *
     * @return Article
     */
    public function setAuthorId($authorId)
    {
        $this->authorId = $authorId;

        return $this;
    }

    /**
     * Get authorId
     *
     * @return int
     */
    public function getAuthorId()
    {
        return $this->authorId;
    }

    /**
     * Set author
     *
     * @param \AppBundle\Entity\Author $author
     *
     * @return Article
     */
    public function setAuthor(\AppBundle\Entity\Author $author = null)
    {
        $this->author = $author;

        return $this;
    }

    /**
     * Get author
     *
     * @return \AppBundle\Entity\Author
     */
    public function getAuthor()
    {
        return $this->author;
    }

    /**
     * Add review
     *
     * @param \AppBundle\Entity\Review $review
     *
     * @return Article
     */
    public function addReview(\AppBundle\Entity\Review $review)
    {
        $this->reviews[] = $review;

        return $this;
    }

    /**
     * Remove review
     *
     * @param \AppBundle\Entity\Review $review
     */
    public function removeReview(\AppBundle\Entity\Review $review)
    {
        $this->reviews->removeElement($review);
    }

    /**
     * Get reviews
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getReviews()
    {
        return $this->reviews;
    }

    public function __toString() {
        return $this->name;
    }

}

Author Entity:

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

/**
 * Author
 *
 * @ORM\Table(name="author")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\AuthorRepository")
 * @UniqueEntity(fields={"email"}, message="Note: author already existed. Using that record")
 */
class Author
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="email", type="string", length=255, unique=true)
     */
    private $email;

    /**
     * @var string
     *
     * @ORM\Column(name="first_name", type="string", length=255)
     */
    private $firstName;

    /**
     * @var string
     *
     * @ORM\Column(name="last_name", type="string", length=255)
     */
    private $lastName;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="created_date", type="datetime")
     */
    private $createdDate;

    /**
     * @ORM\OneToMany(targetEntity="Review", mappedBy="author")
     */
    private $reviews;

    /**
     * @ORM\OneToMany(targetEntity="article", mappedBy="author")
     */
    private $articles;

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


    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set email
     *
     * @param string $email
     *
     * @return Author
     */
    public function setEmail($email)
    {
        $this->email = $email;

        return $this;
    }

    /**
     * Get email
     *
     * @return string
     */
    public function getEmail()
    {
        return $this->email;
    }

    /**
     * Set firstName
     *
     * @param string $firstName
     *
     * @return Author
     */
    public function setFirstName($firstName)
    {
        $this->firstName = $firstName;

        return $this;
    }

    /**
     * Get firstName
     *
     * @return string
     */
    public function getFirstName()
    {
        return $this->firstName;
    }

    /**
     * Set lastName
     *
     * @param string $lastName
     *
     * @return Author
     */
    public function setLastName($lastName)
    {
        $this->lastName = $lastName;

        return $this;
    }

    /**
     * Get lastName
     *
     * @return string
     */
    public function getLastName()
    {
        return $this->lastName;
    }

    /**
     * Set createdDate
     *
     * @param \DateTime $createdDate
     *
     * @return Author
     */
    public function setCreatedDate($createdDate)
    {
        $this->createdDate = $createdDate;

        return $this;
    }

    /**
     * Get createdDate
     *
     * @return \DateTime
     */
    public function getCreatedDate()
    {
        return $this->createdDate;
    }

    /**
     * Add review
     *
     * @param \AppBundle\Entity\Review $review
     *
     * @return Author
     */
    public function addReview(\AppBundle\Entity\Review $review)
    {
        $this->reviews[] = $review;

        return $this;
    }

    /**
     * Remove review
     *
     * @param \AppBundle\Entity\Review $review
     */
    public function removeReview(\AppBundle\Entity\Review $review)
    {
        $this->reviews->removeElement($review);
    }

    /**
     * Get reviews
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getReviews()
    {
        return $this->reviews;
    }

    /**
     * Add article
     *
     * @param \AppBundle\Entity\article $article
     *
     * @return Author
     */
    public function addarticle(\AppBundle\Entity\article $article)
    {
        $this->articles[] = $article;

        return $this;
    }

    /**
     * Remove article
     *
     * @param \AppBundle\Entity\article $article
     */
    public function removearticle(\AppBundle\Entity\article $article)
    {
        $this->articles->removeElement($article);
    }

    /**
     * Get articles
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getarticles()
    {
        return $this->articles;
    }

    /**
     *Return boolean depending on if the author has already reviewed the article
     * @param \AppBundle\Entity\Author $author
     * @return bool
     */
    public function hasarticle(\AppBundle\Entity\article $article)
    {
        return $this->getarticles()->contains($article);
    }

    public function __toString() {
        return $this->email;
    }

}

Thanks so much in advance for any help you can give. Also, is this the best way to handle duplicate emails? I'd like to update the firstname/lastname as well instead of just letting leaving the existing name in the database. Should I update the name in the controller before persisting or should that somehow be done in the transformer?

Thanks!

  • 写回答

2条回答 默认 最新

  • duanniying2342 2017-02-19 21:41
    关注

    Doctrine tries to insert new row into the db because you are calling persist($article). If you meant to update the row then just call flush():

    $this->getDoctrine()->getManager()->flush();
    

    LE

    For this example, I'm using two lighter entities:

    1) AppBundle/Entity/Article.php entity contains only id, name, and author, with:

    /**
     * @ORM\ManyToOne(targetEntity="Author", inversedBy="articles")
     */
    private $author;
    
    public function setAuthor(\AppBundle\Entity\Author $author = null)
    {
        $this->author = $author;
        return $this;
    }
    
    //getAuthor()...
    //getters for id and name + setter for name
    

    2) AppBundle/Entity/Author.php entity contains only id, email, and articles:

    /**
     * @ORM\OneToMany(targetEntity="Article", mappedBy="author")
     */
    private $articles;
    
    //getters for id and email + setter for email
    
    public function __construct()
    {
        $this->articles = new \Doctrine\Common\Collections\ArrayCollection();
    }
    
    public function addArticle(\AppBundle\Entity\Article $article)
    {
        $this->articles[] = $article;
        return $this;
    }
    
    public function removeArticle(\AppBundle\Entity\Article $article)
    {
        $this->articles->removeElement($article);
    }
    
    public function getArticles()
    {
        return $this->articles;
    }
    

    3) AppBundle/Form/ArticleType.php:

    $builder
        ->add('name')
        ->add('author', EntityType::class, [
            'class' => 'AppBundle:Author',
            'placeholder' => ' ',
            'query_builder' => function(EntityRepository $er) {
                return $er->createQueryBuilder('a');
            },
            'choice_label' => function($author) {
                return $author->getEmail();
            },
            'multiple' => false,
            'expanded' => false
        ])
    ;
    

    4) AppBundle/Controller/ArticleController.php

    /**
     * @Route("/{id}/edit", name="article_edit")
     * @Method({"GET", "POST"})
     */
    public function editAction(Request $request, Article $article)
    {
        $form = $this->createForm('AppBundle\Form\ArticleType', $article);
        $form->handleRequest($request);
    
        if ($form->isSubmitted() && $form->isValid()) {
            $this->getDoctrine()->getManager()->flush();
    
            return $this->redirectToRoute('article_edit', array('id' => $article->getId()));
        }
    
        return $this->render('article/edit.html.twig', array(
            'form' => $form->createView(),
        ));
    }
    

    5) app/Resources/views/article/edit.html.twig

    {{ form(form, { attr:{ 'id':'form' }) }}
    <button type="submit" form="form">Update</button>
    

    Basically, this code is generated by the bin/console doctrine:generate:crud AppBundle:Article

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥20 求数据集和代码#有偿答复
  • ¥15 关于下拉菜单选项关联的问题
  • ¥15 如何修改pca中的feature函数
  • ¥20 java-OJ-健康体检
  • ¥15 rs485的上拉下拉,不会对a-b<-200mv有影响吗,就是接受时,对判断逻辑0有影响吗
  • ¥15 使用phpstudy在云服务器上搭建个人网站
  • ¥15 应该如何判断含间隙的曲柄摇杆机构,轴与轴承是否发生了碰撞?
  • ¥15 vue3+express部署到nginx
  • ¥20 搭建pt1000三线制高精度测温电路
  • ¥15 使用Jdk8自带的算法,和Jdk11自带的加密结果会一样吗,不一样的话有什么解决方案,Jdk不能升级的情况