Я разрабатываю блог со статьями и системой комментариев, и я хотел бы, чтобы, когда человек в бэкэнде удаляет статью, комментарии к статье также удаляются, потому что комментарий таблицы связан со статьями таблицы и пользователем таблицы.
Я просто хочу удалить статьи и комментарии к ним.
Я пробовал этот код, но он не работает, он выдает такую ошибку:
EntityManager # remove () expects parameter 1 to be an entity object, NULL given.
Я пытаюсь получить комментарии с помощью геттеров и сеттеров, но он не работает и сообщает мне, что этот метод пока не существует в контроллере.
Мой контроллер:
// 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;
}
}
Ошибка говорит: remove () expects parameter 1 to be an entity object, NULL given, а вы делаете if ($comment === null) { $comments = $this->getDoctrine()->getManager(); $comments->remove($comment);, поэтому ваша ошибка здесь: вы используете remove() на $comment, когда $comment === null, и это должен быть объект






Вам не нужен код "настраиваемой логики", просто используйте
// 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');
}
Большое спасибо за ваш ответ, это было пропущено каскадное удаление
С Symfony вы сможете сделать это, просто удалив свою статью, если у вас хорошие отношения между сущностями, не так ли?