Bonjour,
Je cherche a supprimer une image presente dans mon form annonce
ayant pour cela dans adtype mis ma collection images a allow_delete.
Mon javascript supprime bien le tag visé par le bouton supprimer.
Le dump me renvoie bien le tableau d'image avec le nb d'elements conservé mais a l'affichage de mon getAnnonce toutes les images sont presentes y compris en bdd et je n'ai aucun message d'erreur.
A l'inverse l'ajout d'image fonctionne tres bien
Je suis sur Symfony 5
Merci d'avance
Ad/Entity
<?php
namespace App\Entity;
use App\Entity\Image;
use Cocur\Slugify\Slugify;
use App\Repository\AdRepository;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* @ORM\Entity(repositoryClass=AdRepository::class)
* @ORM\HasLifecycleCallbacks()
* @UniqueEntity(
* fields = {"title"},
* message ="Une autre annonce à déjà ce titre merci de la modifier"
* )
*/
class Ad
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* title
*
* @ORM\Column(type="string", length=255)
*
*@Assert\Length(
* min=10,
* max=50,
* minMessage="Le titre doit au minimum contenir {{ limit }} caractères",
* maxMessage="Le titre ne peut contenir plus de {{ limit }} caractères"
*)
* @var string
*/
private $title;
/**
* @ORM\Column(type="string", length=255)
*/
private $slug;
/**
* @ORM\Column(type="float")
*
* @Assert\NotNull
* @Assert\Regex(
* pattern = "/(^[0-9]+)\W?([0-9]{0,2}$)/i",
* match=true,
* message ="Le prix ne peut contenir que des nombres")
*
*/
private $price;
/**
* @ORM\Column(type="text")
*/
private $introduction;
/**
* @ORM\Column(type="text")
*/
private $content;
/**
* @ORM\Column(type="string", length=255)
*
* @Assert\Url( message="l'url {{ value }}saisi n'est pas correcte")
*/
private $coverImage;
/**
* @ORM\Column(type="integer")
*/
private $rooms;
/**
* @ORM\OneToMany(targetEntity=Image::class, mappedBy="ad", cascade={"persist"})
*
* @Assert\Valid
*/
private $images;
public function __construct()
{
$this->images = new ArrayCollection();
}
/**
* This function initialize a new slug in case is empty
*
* @return void
*
* @ORM\PrePersist
* @ORM\PreUpdate
*/
public function initializeSlug()
{
if (empty($this->slug)) {
$slugTitle = new Slugify();
$this->setSlug($slugTitle->slugify($this->title));
}
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getSlug(): ?string
{
return $this->slug;
}
public function setSlug(string $slug): self
{
$this->slug = $slug;
return $this;
}
public function getPrice(): ?float
{
return $this->price;
}
public function setPrice(float $price): self
{
$this->price = $price;
return $this;
}
public function getIntroduction(): ?string
{
return $this->introduction;
}
public function setIntroduction(string $introduction): self
{
$this->introduction = $introduction;
return $this;
}
public function getContent(): ?string
{
return $this->content;
}
public function setContent(string $content): self
{
$this->content = $content;
return $this;
}
public function getCoverImage(): ?string
{
return $this->coverImage;
}
public function setCoverImage(string $coverImage): self
{
$this->coverImage = $coverImage;
return $this;
}
public function getRooms(): ?int
{
return $this->rooms;
}
public function setRooms(int $rooms): self
{
$this->rooms = $rooms;
return $this;
}
/**
* @return Collection|Image[]
*/
public function getImages(): Collection
{
return $this->images;
}
public function addImage(Image $image): self
{
if (!$this->images->contains($image)) {
$this->images[] = $image;
$image->setAd($this);
}
return $this;
}
public function removeImage(Image $image): self
{
if ($this->images->contains($image)) {
$this->images->removeElement($image);
//set the owning side to null (unless already changed)
if ($image->getAd() === $this) {
$image->setAd(null);
}
}
return $this;
}
/**
* Set the value of images
*
* @return self
*/
public function setImages(?ArrayCollection $images)
{
$this->images = $images;
}
}
Image/Entity
<?php
namespace App\Entity;
use App\Repository\ImageRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity(repositoryClass=ImageRepository::class)
*/
class Image
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*
* @Assert\Url(message="Ceci n'est pas une url valide veuillez la modifier merci!")
*/
private $url;
/**
* @ORM\Column(type="string", length=255)
*
* @Assert\Length(min=10, minMessage="Le titre doit faire au moins {{ limit }} caractères")
*/
private $caption;
/**
* @ORM\ManyToOne(targetEntity=Ad::class, inversedBy="images")
* @ORM\JoinColumn(nullable=false)
*/
private $ad;
public function getId(): ?int
{
return $this->id;
}
public function getUrl(): ?string
{
return $this->url;
}
public function setUrl(string $url): self
{
$this->url = $url;
return $this;
}
public function getCaption(): ?string
{
return $this->caption;
}
public function setCaption(string $caption): self
{
$this->caption = $caption;
return $this;
}
public function getAd(): ?Ad
{
return $this->ad;
}
public function setAd(?Ad $ad): self
{
$this->ad = $ad;
return $this;
}
}
Ma View Twig
{% extends 'base.html.twig' %}
{% block title %}{{titre}}{% endblock %}
{% form_theme form _self %}
{% block body %}
<h1 class="text-center mb-5 pb-5 border-bottom border-grey mx-auto">{{titre}}</h1>
{{ form_start(form, {'attr':{'class':'form-group'}}) }}
{{ form_widget(form) }}
<div class="d-flex justify-content-end">
<input type="submit" class="btn btn-primary form-group"
{% if button_label is defined %}
value="{{ button_label }}"
{% else %}
value="Créer cette annonce"
{% endif %} >
</div>
{{ form_end(form) }}
{% endblock %}
{% block _ad_images_widget %}
<p>Utlisez ces champs pour rajouter des images</p>
{{form_widget(form)}}
<div class="form-group">
<button type="button"
name=""
id="btn-add"
class="btn btn-primary ">
Ajouter une image
</button>
</div>
{% endblock %}
{# suppression des labels des champs dans twig #}
{# {cette suppression est commentée car réalisée dans les options du form} #}
{# {% block _ad_images_entry_label %}
{{form_label(form,null,{'label_attr':{'class':'d-none'}})}}
{% endblock %} #}
{% block _ad_images_entry_widget %}
<div class="row">
<div class="col d-inline">
{{ form_errors(form.caption) }}
{{ form_widget(form.caption) }}
</div>
<div class="col d-inline">
{{ form_errors(form.url) }}
{{ form_widget(form.url) }}
</div>
<div class="col d-inline">
<button type='button' class="btn btn-danger btn-delete">
Supprimer
</button>
</div>
</div>
{% endblock %}
{% block javascripts %}
<script>
$(document).ready(function(){
// Add an image when newImage addbutton is typed
$(document).on('click','#btn-add',function(){
let tmpl = $('#ad_images').data('prototype').replace(/__name__/g,count);
$('#ad_images').append(tmpl);
});
// Delete the image choosed by the user when press delete button
$(document).on('click','.btn-delete',function(e){
let result = confirm('Confirmez-vous la suppression de cette image ?');
if (result == true){
if ($(this).closest('fieldset.form-group').attr('id')){
$(this).closest('fieldset.form-group').remove();
} else {
$('fieldset.form-group:last').remove();
}
}
});
function count(){
let count = $('#ad_images .row').length;
for(let i=0;i<=count;i++){
if(!$('fieldset.form-group:nth-child('+i+')').attr('id')){
$('fieldset.form-group:nth-child('+i+')').attr('id', "block_"+i);
}
}
}
count();
});
</script>
{% endblock %}
Ma Method dans le controller
/**
* This method edit the choosen ad form to be modifyed
*
* @Route("/ad/edit/{slug}", name="edit_ad")
*
* @return Ad
*/
public function editAction(Request $request, Ad $ad, EntityManagerInterface $manager)
{
$form = $this->createForm(AdType::class, $ad);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
foreach ($ad->getImages() as $image) {
$image->setAd($ad);
$manager->persist($image);
}
$manager->persist($ad);
$manager->flush();
$this->addFlash('success', "Les modifications de l'annonce ont bien été prises en compte");
return $this->redirectToRoute('get_ad', ["slug"=>$ad->getSlug()]);
}
return $this->render(
"ad/forms_ad.html.twig",
[
'titre'=>'Modification de l\'annonce: '.$ad->getTitle(),
'button_label'=> "Modifier cette annonce",
'ad'=>$ad,
'form'=>$form->createView()
]
);
}
Mon FormType Annonce
<?php
namespace App\Form;
use App\Entity\Ad;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\UrlType;
class AdType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'title',
TextType::class,
$this->getConfiguration("Titre de l'annonce", 'Saisissez votre titre')
)
->add(
'introduction',
TextType::class,
$this->getConfiguration("Brève présentation", 'Petite phrase d\'introduction')
)
->add(
'coverImage',
UrlType::class,
$this->getConfiguration("Image de couverture", 'Url de l\'image de couverture')
)
->add(
'content',
TextType::class,
$this->getConfiguration("Contenu de l'annonce", 'Saisissez un contenu complet pour l\'annonce')
)
->add(
'price',
MoneyType::class,
$this->getConfiguration("Prix de la location / nuit", 'Saisissez le prix par nuit')
)
->add(
'rooms',
IntegerType::class,
$this->getConfiguration("Nombre de chambres à proposer", 'De combien de chambres disposez-vous ?')
)
->add(
'images',
CollectionType::class,
[
'label'=>"Images complémentaires",
'label_attr'=>["class"=>"font-weight-bold border-top my-4 border-grey"],
'entry_type'=> ImageType::class,
'entry_options'=>['label'=>false],
'allow_add'=> true,
'allow_delete' => true
]
)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Ad::class,
]);
}
/**
* This function returns the label and the placeholder for each field
*
* @param string $label
* @param string $placeholder
* @return array
*/
private function getConfiguration(string $label, string $placeholder, $required = true):array
{
return [
'required'=>$required,
'label'=>$label,
'attr'=>[
'placeholder'=>$placeholder
]
];
}
}
Mon Form Image
<?php
namespace App\Form;
use App\Entity\Image;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\UrlType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class ImageType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'url',
UrlType::class,
[
'attr'=>["placeholder"=>"Url de l'image"]
]
)
->add(
'caption',
TextType::class,
[
'attr'=>["placeholder"=>"Nom de l'image"]
]
)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Image::class,
]);
}
}
J'ai réecris ma méthode edit du controlleur
J'initialise un tableau qui reprends les images présentes en bdd
Je compare ce tableau a ma request
Je supprime de ma bdd les images n'appartenant pas a ma request
Je valide les images de ma request
C'est plus lourd mais efficace.
/**
* This method edit the choosen ad form to be modifyed
*
* @Route("/ad/edit/{slug}", name="edit_ad")
*
* @return Ad
*/
public function editAction(Request $request, Ad $ad, EntityManagerInterface $manager)
{
//Tableau qui reprend les images présentes en bdd
$originalImages = new ArrayCollection();
foreach ($ad->getImages() as $image) {
$originalImages->add($image);
}
$form = $this->createForm(AdType::class, $ad);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
//Comparaison et suppression des images de ma request non présentes en bdd
foreach ($originalImages as $image) {
if (false === $ad->getImages()->contains($image)) {
$ad->removeImage($image);
$manager->remove($image);
$manager->flush();
}
}
//Validation des images de ma request
foreach ($ad->getImages() as $image) {
$image->setAd($ad);
$manager->persist($image);
}
$manager->persist($ad);
$manager->flush();
$this->addFlash('success', "Les modifications de l'annonce ont bien été prises en compte");
return $this->redirectToRoute('get_ad', ["slug"=>$ad->getSlug()]);
}
return $this->render(
"ad/forms_ad.html.twig",
[
'titre'=>'Modification de l\'annonce: '.$ad->getTitle(),
'button_label'=> "Modifier cette annonce",
'form'=>$form->createView()
]
);
}