1

I am developing a blog with articles and a system of comments and I would like that when the person in the backend deletes an article, the article's comments are deleted too because the table comment is in relation with the table articles and table user.

I just want to delete the articles and its comments.

I tried this code but it does not work, it gives me an error like this:

EntityManager # remove () expects parameter 1 to be an entity object, NULL given.

I try to get the comments with getters and setters but it does not work and tells me that this method does not exist in the controller while.

My controller:

// 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');
}

Comments Entity:

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

Articles Entity:

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

User Entity:

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;
    }
}
Jason Roman
  • 8,146
  • 10
  • 35
  • 40
Crown Backend
  • 170
  • 1
  • 4
  • 20
  • With Symfony you should be able to do it just by deleting your article if your entities relation are good, no? – Mickaël Leger Jun 14 '18 at 10:29
  • The error say : `remove () expects parameter 1 to be an entity object, NULL given` and you do `if($comment === null) { $comments = $this->getDoctrine()->getManager(); $comments->remove($comment);`, so your error is just here : you use `remove()` on `$comment` when `$comment === null` and it should be an entity – Mickaël Leger Jun 14 '18 at 10:31

1 Answers1

4

You don't need any "custom logic" code, just use

// Articles.php

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

So when you remove an article, also related comments will be removed.

This annotation is an ORM on, so it works only inside your application logic. If you want to put this also @DBMS level, just add

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

and you'll have both.

Just one side note: it's uncommon to have plural names form db table. In my example with JoinColumn I've used comments (plural) but you must verify the name match with the real column name, or at least with the name you want here.

Returning to your question, you're verifying if the comment is null and try to remove it. There are a lot of mistake here: first you are on the route for articles and you're searching comments with article id as primary key (so it's wrong conceptually).

Second, you're trying to remove a null variable.

What you can do here is get rid of all the code comment code of that controller and do something like this

/**
 * @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');
 }

Last, but not least, you can directly typehint for Article and it will be resolved by 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');
 }
DonCallisto
  • 29,419
  • 9
  • 72
  • 100