У меня проблема с загрузкой фото в форму. Я могу сохранить свою форму с помощью Doctrine в SQL, но когда я хочу сохранить несколько изображений, появляется это сообщение об ошибке:
ContextErrorException HTTP 500 Внутренняя ошибка сервера Предупреждение:
spl_object_hash () ожидает, что параметр 1 будет объектом, задан массив
РОДИТЕЛЬ: WebApp.php
<?php
namespace MO\WebAppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
//pour le slug
use Gedmo\Mapping\Annotation as Gedmo;
use Vich\UploaderBundle\Mapping\Annotation as Vich;/**
* WebApp
*
* @ORM\Table(name="mo_travel")
* @ORM\Entity(repositoryClass="MO\WebAppBundle\Repository\WebAppRepository")
* @ORM\HasLifecycleCallbacks()
*
*
*/
class WebApp
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var \DateTime
*
* @ORM\Column(name="date", type="datetime")
*/
private $date;
/**
* @var string
*
* @ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* @var string
*
* @ORM\Column(name="author", type="string", length=255)
*/
private $author;
/**
* @var string
*
* @ORM\Column(name="content", type="text")
*/
private $content;
/**
* @ORM\Column(name="published", type="boolean")
*/
private $published = true;
/**
* @ORM\OneToMany(targetEntity="MO\WebAppBundle\Entity\Image", cascade={"persist"}, mappedBy="webapp"))
* @ORM\JoinTable(name="mo_travel_image")
*/
private $image;
/**
* @ORM\ManyToMany(targetEntity="MO\WebAppBundle\Entity\Category", cascade={"persist"})
* @ORM\JoinTable(name="mo_travel_category")
*/
private $categories;
/**
* @Gedmo\Slug(fields={"title"})
* @ORM\Column(name="slug", type="string", length=255, unique=true)
*/
private $slug;
/**
* @ORM\Column(name="updated_at", type="datetime", nullable=true)
* @var \DateTime
*/
private $updatedAt;
/**
* WebApp constructor.
*/
public function __construct()
{
// Par défaut, la date de l'annonce est la date d'aujourd'hui
$this->date = new \Datetime();
$this->categories = new ArrayCollection();
$this->image = new ArrayCollection();
}
/**
* @ORM\PreUpdate
*/
public function updateDate()
{
$this->setUpdatedAt(new \Datetime());
}
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set date
*
* @param \DateTime $date
*
* @return WebApp
*/
public function setDate($date)
{
$this->date = $date;
return $this;
}
/**
* Get date
*
* @return \DateTime
*/
public function getDate()
{
return $this->date;
}
/**
* Set title
*
* @param string $title
*
* @return WebApp
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set author
*
* @param string $author
*
* @return WebApp
*/
public function setAuthor($author)
{
$this->author = $author;
return $this;
}
/**
* Get author
*
* @return string
*/
public function getAuthor()
{
return $this->author;
}
/**
* Set content
*
* @param string $content
*
* @return WebApp
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* @return string
*/
public function getContent()
{
return $this->content;
}
/**
* @param boolean $published
*
* @return WebApp
*/
public function setPublished($published)
{
$this->published = $published;
}
/**
* @return mixed
*/
public function getPublished()
{
return $this->published;
}
public function setImage(Image $image = null)
{
$this->image = $image;
}
public function getImage()
{
return $this->image;
}
/**
* Add category
*
* @param \MO\WebAppBundle\Entity\Category $category
*
* @return WebApp
*/
public function addCategory(\MO\WebAppBundle\Entity\Category $category)
{
$this->categories[] = $category;
return $this;
}
/**
* Remove category
*
* @param \MO\WebAppBundle\Entity\Category $category
*/
public function removeCategory(\MO\WebAppBundle\Entity\Category $category)
{
$this->categories->removeElement($category);
}
/**
* Get categories
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getCategories()
{
return $this->categories;
}
/**
* @return mixed
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* @param mixed $updatedAt
*
* @return WebApp
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
}
/**
* Set slug
*
* @param string $slug
*
* @return WebApp
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* @return string
*/
public function getSlug()
{
return $this->slug;
}
}
ДЕТИ: Image.php
<?php
namespace MO\WebAppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* @ORM\Table(name="mo_image")
* @ORM\Entity
* @ORM\HasLifecycleCallbacks
*/
class Image
{
/**
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(name="url", type="string", length=255)
*/
private $url;
/**
* @ORM\Column(name="alt", type="string", length=255)
*/
private $alt;
/**
* @var UploadedFile
*/
private $file;
// On ajoute cet attribut pour y stocker le nom du fichier temporairement
private $tempFilename;
/**
* @ORM\PrePersist()
* @ORM\PreUpdate()
*/
public function preUpload()
{
// Si jamais il n'y a pas de fichier (champ facultatif), on ne fait rien
if (null === $this->file) {
return;
}
// Le nom du fichier est son id, on doit juste stocker également son extension
// Pour faire propre, on devrait renommer cet attribut en « extension », plutôt que « url »
$this->url = $this->file->guessExtension();
// Et on génère l'attribut alt de la balise <img>, à la valeur du nom du fichier sur le PC de l'internaute
$this->alt = $this->file->getClientOriginalName();
}
/**
* @ORM\PostPersist()
* @ORM\PostUpdate()
*/
public function upload()
{
// Si jamais il n'y a pas de fichier (champ facultatif), on ne fait rien
if (null === $this->file) {
return;
}
// Si on avait un ancien fichier (attribut tempFilename non null), on le supprime
if (null !== $this->tempFilename) {
$oldFile = $this->getUploadRootDir().'/'.$this->id.'.'.$this->tempFilename;
if (file_exists($oldFile)) {
unlink($oldFile);
}
}
// On déplace le fichier envoyé dans le répertoire de notre choix
$this->file->move(
$this->getUploadRootDir(), // Le répertoire de destination
$this->id.'.'.$this->url // Le nom du fichier à créer, ici « id.extension »
);
}
/**
* @ORM\PreRemove()
*/
public function preRemoveUpload()
{
// On sauvegarde temporairement le nom du fichier, car il dépend de l'id
$this->tempFilename = $this->getUploadRootDir().'/'.$this->id.'.'.$this->url;
}
/**
* @ORM\PostRemove()
*/
public function removeUpload()
{
// En PostRemove, on n'a pas accès à l'id, on utilise notre nom sauvegardé
if (file_exists($this->tempFilename)) {
// On supprime le fichier
unlink($this->tempFilename);
}
}
public function getUploadDir()
{
// On retourne le chemin relatif vers l'image pour un navigateur (relatif au répertoire /web donc)
return 'uploads/img';
}
protected function getUploadRootDir()
{
// On retourne le chemin relatif vers l'image pour notre code PHP
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
public function getWebPath()
{
return $this->getUploadDir().'/'.$this->getId().'.'.$this->getUrl();
}
/**
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @param string $url
*/
public function setUrl($url)
{
$this->url = $url;
}
/**
* @return string
*/
public function getUrl()
{
return $this->url;
}
/**
* @param string $alt
*/
public function setAlt($alt)
{
$this->alt = $alt;
}
/**
* @return string
*/
public function getAlt()
{
return $this->alt;
}
/**
* @return UploadedFile
*/
public function getFile()
{
return $this->file;
}
/**
* @param UploadedFile $file
*/
// On modifie le setter de File, pour prendre en compte l'upload d'un fichier lorsqu'il en existe déjà un autre
public function setFile(UploadedFile $file)
{
$this->file = $file;
// On vérifie si on avait déjà un fichier pour cette entité
if (null !== $this->url) {
// On sauvegarde l'extension du fichier pour le supprimer plus tard
$this->tempFilename = $this->url;
// On réinitialise les valeurs des attributs url et alt
$this->url = null;
$this->alt = null;
}
}
}
WebAppType.php
<?php
namespace MO\WebAppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class WebAppType extends AbstractType
{
/**
* Réutilisabilité de notre formulaire
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('date', DateTimeType::class)
->add('title', TextType::class)
->add('author', TextType::class)
->add('content', TextareaType::class)
->add('image',ImageType::class, array("data_class" => null))
/*
** - 1er argument : nom du champ, ici « categories », car c'est le nom de l'attribut
** - 2e argument : type du champ, ici « CollectionType » qui est une liste de quelque chose
** - 3e argument : tableau d'options du champ
*/
->add('categories', EntityType::class, array(
'class' => 'MOWebAppBundle:Category',
'choice_label' => 'name',
'expanded' => true,
'multiple' => true,
))
->add('save', SubmitType::class);
// On ajoute une fonction qui va écouter un évènement de formulaire
$builder->addEventListener(
FormEvents::PRE_SET_DATA, // 1er argument : L'évènement qui nous intéresse : ici, PRE_SET_DATA
function(FormEvent $event) { // 2e argument : La fonction à exécuter lorsque l'évènement est déclenché
// On récupère notre objet WebApp (travel) sous-jacent
$travel = $event->getData();
// Cette condition est importante, on en reparle plus loin
if (null === $travel) {
return; // On sort de la fonction sans rien faire lorsque $advert vaut null
}
// Si l'annonce n'est pas publiée, ou si id null
if (!$travel->getPublished() || null === $travel->getId()) {
// Alors on ajoute le champ published
$event->getForm()->add('published', CheckboxType::class, array('required' => false));
} else {
// Sinon, on le supprime
$event->getForm()->remove('published');
}
}
);
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MO\WebAppBundle\Entity\WebApp'
));
}
}
ImageType.php
<?php
namespace MO\WebAppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ImageType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file', FileType::class, array(
'multiple' => true ))
->add('url', TextType::class)
->add('alt', TextType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MO\WebAppBundle\Entity\Image'
));
}
}
Большое спасибо за ваши ответы.
Задача ещё не решена.
Других решений пока нет …