【问题标题】:Symfony delete articles and commentsSymfony 删除文章和评论
【发布时间】:2018-06-15 01:03:50
【问题描述】:

我正在开发一个带有文章的博客和一个 cmets 系统,我希望当后端的人删除一篇文章时,该文章的 cmets 也会被删除,因为表格评论与表格文章和表格用户有关.

我只想删除文章及其 cmets。

我尝试了这段代码,但它不起作用,它给了我这样的错误:

EntityManager #remove() 期望参数1为实体对象,给定NULL。

我尝试使用 getter 和 setter 获取 cmets,但它不起作用,并告诉我该方法在控制器中不存在。

我的控制器:

// remove an article
/**
 * @Route("admin/supprimer/{id}")
 * @param int $id
 * @return Response
 */
public function delete(int $id): Response
{
    $comment = $this->getDoctrine()->getRepository(Comments::class)->find($id);
         
    if ($comment === null) {
        $comments = $this->getDoctrine()->getManager();
        $comments->remove($comment);
        $comments->flush();
    }
 
    $article = $this->getDoctrine()
        ->getRepository(Articles::class)
        ->find($id);

    $manager = $this->getDoctrine()->getManager();
 
    $manager->remove($article);
    $manager->flush();
    $this->addFlash('deleteArticle', 'L\'article a bien étais supprimer');

    return $this->redirectToRoute('app_backoffice_admin');
}

评论实体:

namespace App\Entity;
 
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
 
/**
 * @ORM\Entity(repositoryClass="App\Repository\CommentsRepository")
 */
class Comments
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;
 
    /**
     * @ORM\Column(type="text", nullable=false)
     */
    private $commentsContent;
 
    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="comments")
     * @ORM\JoinColumn(nullable=false)
     */
    private $userComments;
 
    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Articles", inversedBy="comments")
     */
    private $articleComments;
 
    public function __construct()
    {
        $this->userComments = new ArrayCollection();
        $this->articleComments = new ArrayCollection();
    }
 
    public function getId()
    {
        return $this->id;
    }
 
    public function getCommentsContent(): ?string
    {
        return $this->commentsContent;
    }
 
    public function setCommentsContent(?string $commentsContent): self
    {
        $this->commentsContent = $commentsContent;
 
        return $this;
    }
 
    public function getUserComments(): ?User
    {
        return $this->userComments;
    }
 
    public function setUserComments(?User $userComments): self
    {
        $this->userComments = $userComments;
 
        return $this;
    }
 
    public function getArticleComments(): ?Articles
    {
        return $this->articleComments;
    }
 
    public function setArticleComments(?Articles $articleComments): self
    {
        $this->articleComments = $articleComments;
 
        return $this;
    }
}

文章实体:

namespace App\Entity;
 
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\Validator\Constraints as Assert;
use Gedmo\Mapping\Annotation as Gedmo;
 
 
/**
 * @ORM\Entity(repositoryClass="App\Repository\ArticlesRepository")
 * @ORM\HasLifecycleCallbacks()
 * @Vich\Uploadable
 */
class Articles
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;
 
    /**
     * @ORM\Column(type="string", length=255)
     * @Assert\Length(
     *     min = 5,
     *     max = 255,
     *     minMessage = "le contenu de titre doit avoir au minimum {{ limit }} carctère",
     *     maxMessage = "le contenu de titre ne doit dépasser {{ limit }} carctère"
     * )
     */
    private $nameArticle;
 
    /**
     * @ORM\Column(type="text", nullable=false)
     * @Assert\Length(
     *     min = 50,
     *     minMessage = "le contenu de titre doit avoir au minimum {{ limit }} carctère",
     * )
     */
    private $articleContent;
 
    /**
     * @var \DateTime
     * @Gedmo\Timestampable(on="create")
     * @ORM\Column(name="created_at", type="datetime", nullable=false)
     */
    private $createdAt;
 
    /**
     * @var \DateTime
     * @Gedmo\Timestampable(on="update")
     * @ORM\Column(name="updated_at", type="datetime", nullable=false)
     */
    private $updatedAt;
 
    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Category", inversedBy="articles", cascade={"persist"})
     * @ORM\JoinColumn(nullable=false)
     */
    private $category;
 
    /**
     * NOTE: This is not a mapped field of entity metadata, just a simple property.
     *
     * @Vich\UploadableField(mapping="articles_image", fileNameProperty="imageName", size="imageSize")
     *
     * @var File
     */
    private $imageFile;
 
    /**
     * @ORM\Column(type="string", length=255)
     *
     * @var string
     */
    private $imageName;
 
    /**
     * @ORM\Column(type="integer")
     *
     * @var integer
     */
    private $imageSize;
 
    /**
     * @ORM\Column(type="text")
     */
    private $introduction;
 
    /**
     * @Gedmo\Slug(fields={"nameArticle"},separator="-", updatable=true, unique=true)
     * @ORM\Column(type="string", length=255)
     */
    private $slug;
 
    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Comments", mappedBy="articleComments")
     */
    private $comments;
 
 
    public function __construct()
    {
        $this->createdAt = new \DateTime("now", new \DateTimeZone('Europe/Paris'));
        $this->comments = new ArrayCollection();
    }
 
 
    public function getId()
    {
        return $this->id;
    }
 
    public function getNameArticle(): ?string
    {
        return $this->nameArticle;
    }
 
    public function setNameArticle(string $nameArticle): self
    {
        $this->nameArticle = $nameArticle;
 
        return $this;
    }
 
    public function getArticleContent(): ?string
    {
        return $this->articleContent;
    }
 
    public function setArticleContent(string $articleContent): self
    {
        $this->articleContent = $articleContent;
 
        return $this;
    }
 
    /**
     * Get createdAt
     *
     * @return \DateTime
     */
    public function getCreatedAt()
    {
        return $this->createdAt;
    }
 
    /**
     * Get updatedAt
     *
     * @return \DateTime
     */
    public function getUpdatedAt()
    {
        return $this->updatedAt;
    }
 
    public function getCategory(): ?Category
    {
        return $this->category;
    }
 
    public function setCategory(?Category $category): self
    {
        $this->category = $category;
 
        return $this;
    }
 
    /**
     * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
     * @throws \Exception
     */
    public function setImageFile(?File $image = null): void
    {
        $this->imageFile = $image;
 
        if (null !== $image) {
            // It is required that at least one field changes if you are using doctrine
            // otherwise the event listeners won't be called and the file is lost
            $this->updatedAt = new \DateTimeImmutable();
        }
    }
 
    public function getImageFile(): ?File
    {
        return $this->imageFile;
    }
 
    public function setImageName(?string $imageName): void
    {
        $this->imageName = $imageName;
    }
 
    public function getImageName(): ?string
    {
        return $this->imageName;
    }
 
    public function setImageSize(?int $imageSize): void
    {
        $this->imageSize = $imageSize;
    }
 
    public function getImageSize(): ?int
    {
        return $this->imageSize;
    }
 
    public function getIntroduction(): ?string
    {
        return $this->introduction;
    }
 
    public function setIntroduction(string $introduction): self
    {
        $this->introduction = $introduction;
 
        return $this;
    }
 
    public function getSlug(): ?string
    {
        return $this->slug;
    }
 
    public function setSlug(string $slug): self
    {
        $this->slug = $slug;
 
        return $this;
    }
 
    /**
     * @return Collection|Comments[]
     */
    public function getComments(): Collection
    {
        return $this->comments;
    }
 
    public function addComment(Comments $comment): self
    {
        if (!$this->comments->contains($comment)) {
            $this->comments[] = $comment;
            $comment->setArticleComments($this);
        }
 
        return $this;
    }
 
    public function removeComment(Comments $comment): self
    {
        if ($this->comments->contains($comment)) {
            $this->comments->removeElement($comment);
            // set the owning side to null (unless already changed)
            if ($comment->getArticleComments() === $this) {
                $comment->setArticleComments(null);
            }
        }
 
        return $this;
    }
 
}

用户实体:

namespace App\Entity;
 
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use Symfony\Component\Validator\Constraints as Assert;
 
/**
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 * @ORM\Table(name="fos_user")
 */
class User extends BaseUser
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;
 
 
    /**
     * @Assert\Regex("/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).{6,}$/",
     *  message = "Votre mot de passe doit contenir minimum 6 carctère avec une miniscule majuscule un chiffre "
     * )
     *
     * @var string
     */
    //protected $password;
 
    /**
     * @Assert\Email(
     *     message = "l'adresse mail n'est pas valide"
     * )
     * @var string
     */
    protected $email;
 
 
    /**
     * @ORM\Column(type="string", length=255)
     * @var string
     */
    protected $age;
 
    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Comments", mappedBy="userComments", orphanRemoval=true)
     */
    private $comments;
 
    public function __construct()
    {
        parent::__construct();
        $this->comments = new ArrayCollection();
    }
 
 
    public function getAge(): ?string
    {
        return $this->age;
    }
 
    public function setAge(string $age): self
    {
        $this->age = $age;
 
        return $this;
    }
 
 
    /**
     * Place un rôle unique à l'utilisateur (supprimer tous les anciens rôles)
     * @param string $userRole
     */
    public function setRole(string $userRole)
    {
        // Vider les rôles
        foreach ($this->getRoles() as $role) {
            $this->removeRole($role);
        }
        // Ajout le rôle unique passé en paramètre
        $this->addRole($userRole);
    }
 
    /**
     * @return Collection|Comments[]
     */
    public function getComments(): Collection
    {
        return $this->comments;
    }
 
    public function addComment(Comments $comment): self
    {
        if (!$this->comments->contains($comment)) {
            $this->comments[] = $comment;
            $comment->setUserComments($this);
        }
 
        return $this;
    }
 
    public function removeComment(Comments $comment): self
    {
        if ($this->comments->contains($comment)) {
            $this->comments->removeElement($comment);
            // set the owning side to null (unless already changed)
            if ($comment->getUserComments() === $this) {
                $comment->setUserComments(null);
            }
        }
 
        return $this;
    }
}

【问题讨论】:

  • 使用 Symfony,如果您的实体关系良好,您应该可以通过删除您的文章来做到这一点,不是吗?
  • 错误说:remove () expects parameter 1 to be an entity object, NULL given 而你做的是if($comment === null) { $comments = $this->getDoctrine()->getManager(); $comments->remove($comment);,所以你的错误就在这里:当$comment === null 时,你在$comment 上使用remove(),它应该是一个实体跨度>

标签: php symfony doctrine


【解决方案1】:

您不需要任何“自定义逻辑”代码,只需使用

// Articles.php

/**
 * @ORM\OneToMany(targetEntity="App\Entity\Comments", mappedBy="articleComments", cascade={"remove"})
 */
private $comments;

因此,当您删除 article 时,相关的 cmets 也会被删除。

这个注解是一个ORM on,所以它只在你的应用程序逻辑中起作用。如果你想把它也放在@DBMS 级别,只需添加

@ORM\JoinColumn(name="comments_id", referencedColumnName="id", onDelete="CASCADE")

你将拥有两者。

只是一个侧面说明:在 db 表中使用复数名称并不常见。在我的 JoinColumn 示例中,我使用了 comments(复数),但您必须验证名称是否与实际列名匹配,或者至少与此处所需的名称匹配。

回到您的问题,您正在验证评论是否为null 并尝试将其删除。这里有很多错误:首先您在寻找文章的路径上,并且您正在搜索以文章id 作为主键的 cmets(因此在概念上是错误的)。

其次,您正在尝试删除 null 变量。

你可以在这里做的是去掉那个控制器的所有代码注释代码,做这样的事情

/**
 * @Route("admin/supprimer/{id}")
 * @param int $id
 * @return Response
 */
 public function delete(int $id): Response {
   $article = $this->getDoctrine()
     ->getRepository(Articles::class)
     ->find($id);

   $manager = $this->getDoctrine()->getManager();

   foreach ($article->getComments() as $comment) {
     $manager->remove($comment);
   }

   $manager->remove($article);
   $manager->flush();

   $this->addFlash('deleteArticle', 'L\'article a bien étais supprimer');

   return $this->redirectToRoute('app_backoffice_admin');
 }

最后但同样重要的是,您可以直接输入Article 的提示,它将由Symfony ParamConvert 解决

/**
 * @Route("admin/supprimer/{id}")
 * @param int $id
 * @return Response
 */
 public function delete(Article $article): Response {
   $manager = $this->getDoctrine()->getManager();

   foreach ($article->getComments() as $comment) {
     $manager->remove($comment);
   }

   $manager->remove($article);
   $manager->flush();

   $this->addFlash('deleteArticle', 'L\'article a bien étais supprimer');

   return $this->redirectToRoute('app_backoffice_admin');
 }

【讨论】:

  • 非常感谢您的回答,这是错过的级联删除
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-26
  • 1970-01-01
  • 2021-05-01
  • 1970-01-01
  • 2011-12-18
  • 1970-01-01
  • 2015-09-14
相关资源
最近更新 更多