我正在开发一个包含文章和评论系统的博客,我希望当后端的人删除文章时,文章的评论也会被删除,因为表评论与表文章和表用户有关。
我只想删除文章及其评论。
我试过这段代码,但它不起作用,它给我一个这样的错误:
EntityManager #remove()要求参数1为实体对象,给定NULL。
我尝试用getter和setter获取注释,但它不起作用并且告诉我这个方法在控制器中不存在。
我的控制器:
// 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;
}
}
您不需要任何“自定义逻辑”代码,只需使用即可
// Articles.php
/**
* @ORM\OneToMany(targetEntity="App\Entity\Comments", mappedBy="articleComments", cascade={"remove"})
*/
private $comments;
因此,当您删除article
时,也会删除相关注释。
这个注释是一个ORM
,所以它只适用于你的应用程序逻辑。如果你想把它也放在@DBMS级别,只需添加即可
@ORM\JoinColumn(name="comments_id", referencedColumnName="id", onDelete="CASCADE")
你会两个都有。
只是一个注意事项:从db表中创建复数名称并不常见。在我使用JoinColumn
的例子中,我使用了comments
(复数),但你必须验证名称是否与真实列名匹配,或者至少与你想要的名称相匹配。
回到你的问题,你要验证评论是否是null
并尝试删除它。这里有很多错误:首先你是文章的路线,你正在用文章id
作为主键搜索评论(所以在概念上是错误的)。
其次,您正在尝试删除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');
}