Bonjour,

Voila je rencontre un petit problème avec mon code.

Ce que je fais

Je suis en train de developper une application permettant de faire des annonces. Pour cela j'utilise le Bundle FOSRestBundle de Symfony pour realiser L'API REST.

Pour créer une annonce, l'utilisateur doit saisir le titre, la description de l'annonce et une photo. J'ai utilisé le Bundle VichUploaderBundle pour réaliser l'upload de la photo au niveau de mon API Rest. Ce Bundle permet de ne persister que le nom de la photo et de sauvegarder l'image dans un répertoire du projet.

J'arrive à persister le titre et la description de l'annonce mais pas le nom de la photo. On dirait que la photo n'est pas envoyée au Contrôleur et le repertoire qui doit être créé dans le dossier web pour sauvegarder les photos n'est pas également créé. Lorsque j'effectue un test sur Postman, j'ai des erreurs.

Voici les différents fichiers:

L'entité:

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;

/**
 * Annonce
 *
 * @ORM\Table(name="annonce")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\AnnonceRepository")
 * @Vich\Uploadable
 */
class Annonce
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="titre", type="string", length=100)
     */
    private $titre;

    /**
     * @var string
     *
     * @ORM\Column(name="description", type="string", length=255)
     */
    private $description;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     * @var string
     */
    private $image;

    /**
     * @Vich\UploadableField(mapping="annonce_images", fileNameProperty="image")
     * @var File
     */
    private $imageFile;

    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set titre
     *
     * @param string $titre
     *
     * @return Annonce
     */
    public function setTitre($titre)
    {
        $this->titre = $titre;

        return $this;
    }

    /**
     * Get titre
     *
     * @return string
     */
    public function getTitre()
    {
        return $this->titre;
    }

    /**
     * Set description
     *
     * @param string $description
     *
     * @return Annonce
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get description
     *
     * @return string
     */
    public function getDescription()
    {
        return $this->description;
    }

    public function setImageFile(File $image = null)
    {
        $this->imageFile = $image;
    }

    public function getImageFile()
    {
        return $this->imageFile;
    }

    public function setImage($image)
    {
        $this->image = $image;
    }

    public function getImage()
    {
        return $this->image;
    }
}

Voici le Formulaire:

<?php

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichFileType;

class AnnonceType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('titre')
            ->add('description')
            ->add('imageFile', VichFileType::class);
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\Annonce',
            'csrf_protection' => false
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_annonce';
    }

}

Voici le Controleur:

<?php

namespace AppBundle\Controller;

use AppBundle\Entity\Annonce;
use AppBundle\Form\AnnonceType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use FOS\RestBundle\Controller\Annotations as Rest; // alias pour toutes les annotations
use Symfony\Component\HttpFoundation\Request;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;

class AnnonceController extends Controller
{

    /**
     *  @ApiDoc(
     *    description="Création d'une annonce ",
     *    input={"class"=AnnonceType::class, "name"=""}
     * )
     *
     * @Rest\View
     * @Rest\Post("/api/newAnnonce")
     */
    public function postAnnonceAction(Request $request)
    {
        $annonce= new Annonce();
        $form = $this->createForm(AnnonceType::class, $annonce);
        $form->submit($request->request->all());

        if ($form->isValid()) {

            $em = $this->get('doctrine.orm.entity_manager');
            $em->persist($annonce);
            $em->flush();
            return $annonce->getImageFile()->getFilename();
        } else {
            return $form;
        }
    }

}

Voici la configuration du bundle dans le fichier config.yml:

vich_uploader:
    db_driver: orm
    mappings:
        annonce_images:
            uri_prefix: '/uploads/images/annonce'
            upload_destination: '%kernel.root_dir%/../web/uploads/images/exemple'
            namer: vich_uploader.namer_origname

Ce que j'obtiens

Quand je fait un test avec postman, j'ai des erreurs. On dirait que le probleme c'est l'envoie de la photo.
voici les erreurs quand je effectue un test avec Postman:

{
    "error": {
        "code": 500,
        "message": "Internal Server Error",
        "exception": [
            {
                "message": "Call to a member function getFilename() on null",
                "class": "Symfony\\Component\\Debug\\Exception\\FatalThrowableError",
                "trace": [
                    {
                        "namespace": "",
                        "short_class": "",
                        "class": "",
                        "type": "",
                        "function": "",
                        "file": "/root/Bureau/mon_projet/SenAnnonce/src/AppBundle/Controller/AnnonceController.php",
                        "line": 36,
                        "args": []
                    },
                    {
                        "namespace": "AppBundle\\Controller",
                        "short_class": "AnnonceController",
                        "class": "AppBundle\\Controller\\AnnonceController",
                        "type": "->",
                        "function": "postAnnonceAction",
                        "file": null,
                        "line": null,
                        "args": [
                            [
                                "object",
                                "Symfony\\Component\\HttpFoundation\\Request"
                            ]
                        ]
                    },
                    {
                        "namespace": "",
                        "short_class": "",
                        "class": "",
                        "type": "",
                        "function": "call_user_func_array",
                        "file": "/root/Bureau/mon_projet/SenAnnonce/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php",
                        "line": 153,
                        "args": [
                            [
                                "array",
                                [
                                    [
                                        "object",
                                        "AppBundle\\Controller\\AnnonceController"
                                    ],
                                    [
                                        "string",
                                        "postAnnonceAction"
                                    ]
                                ]
                            ],
                            [
                                "array",
                                [
                                    [
                                        "object",
                                        "Symfony\\Component\\HttpFoundation\\Request"
                                    ]
                                ]
                            ]
                        ]
                    },
                    {
                        "namespace": "Symfony\\Component\\HttpKernel",
                        "short_class": "HttpKernel",
                        "class": "Symfony\\Component\\HttpKernel\\HttpKernel",
                        "type": "->",
                        "function": "handleRaw",
                        "file": "/root/Bureau/mon_projet/SenAnnonce/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php",
                        "line": 68,
                        "args": [
                            [
                                "object",
                                "Symfony\\Component\\HttpFoundation\\Request"
                            ],
                            [
                                "integer",
                                1
                            ]
                        ]
                    },
                    {
                        "namespace": "Symfony\\Component\\HttpKernel",
                        "short_class": "HttpKernel",
                        "class": "Symfony\\Component\\HttpKernel\\HttpKernel",
                        "type": "->",
                        "function": "handle",
                        "file": "/root/Bureau/mon_projet/SenAnnonce/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php",
                        "line": 171,
                        "args": [
                            [
                                "object",
                                "Symfony\\Component\\HttpFoundation\\Request"
                            ],
                            [
                                "integer",
                                1
                            ],
                            [
                                "boolean",
                                true
                            ]
                        ]
                    },
                    {
                        "namespace": "Symfony\\Component\\HttpKernel",
                        "short_class": "Kernel",
                        "class": "Symfony\\Component\\HttpKernel\\Kernel",
                        "type": "->",
                        "function": "handle",
                        "file": "/root/Bureau/mon_projet/SenAnnonce/web/app_dev.php",
                        "line": 30,
                        "args": [
                            [
                                "object",
                                "Symfony\\Component\\HttpFoundation\\Request"
                            ]
                        ]
                    },
                    {
                        "namespace": "",
                        "short_class": "",
                        "class": "",
                        "type": "",
                        "function": "require",
                        "file": "/root/Bureau/mon_projet/SenAnnonce/vendor/symfony/symfony/src/Symfony/Bundle/WebServerBundle/Resources/router.php",
                        "line": 42,
                        "args": [
                            [
                                "string",
                                "/root/Bureau/mon_projet/SenAnnonce/web/app_dev.php"
                            ]
                        ]
                    }
                ]
            }
        ]
    }
}

Quelqu'un peut m'aider svp?

4 réponses


Bonjour Devmapassion. J'ai un peu le même problème que toi : il presiste, mets les donnés dans la base, mais ne remonte pas l'image. As tu trouvé une solution ? Il y a quelque chose dans ce bundle qui m'echappe...

Bonjour, je rencontre la même problème ! avey vous des solution @ sylvigouroux ou @ devmapassion

Bonjour,

J'ai eu le même soucis dans mon projet, après avoir chercher sur Google je n'avais pas trouvé de solution.
J'ai tout de même réussi avec deux solution :

Base64

Le but est de tout envoyer en JSON. J'ai utilisé le bundle Ivory\Base64FileBundle, il permet de récupérer l'image encodé en Base64 et ainsi la convertir en fichier.
Voici un exemple de mon formulaire :

### File ArticleType.php
$builder
        ->add('titre', TextType::class)
        ->add('slug', TextType::class)
        ->add('content', TextType::class)
        ->add('brouillon', CheckboxType::class)
        ->add('publishFacebook', CheckboxType::class)
         ->add('image', ImageType::class, [
             'mapped' => true
         ]);

### File ImageType.php
$builder
      ->add('file', FileType::class, [
              'base64' => true,
              'mapped' => true
      ]);

Avec cet exemple il faut lui envoyer un payload du type :

{
    "titre": "titre",
    "slug": "titre",
    "content": "content",
    "brouillon": true,
    "publishFacebook": false,
    "image": {
        "file": {
            "name": "filename.png",
            "value": "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABh0lEQVQ4T23TO8iPYR..."
        }
    } 
}

Au niveau du controller, ça reste standart :

* @Rest\View(statusCode=Response::HTTP_CREATED, serializerGroups={"article"})
     * @Rest\Post("/article")
     */
    public function postArticleAction(Request $request)
    {
        $article = new Article();
        $form = $this->createForm(ArticleType::class, $article);
        $form->submit($request->request->all(), false);

        if($form->isValid()){
            $em = $this->get('doctrine.orm.entity_manager');
            $em->persist($article);
            $em->flush();
            return $article;
        } else {
            return new JsonResponse($this->getFormErrors($form), Response::HTTP_BAD_REQUEST);
        }
WebKitFormBoundary

Envoyer l'image avec un header POST multipart/form-data.

Au niveau du controller j'ai manuellement ajouté l'image a mon entité :

    /**
     * @Rest\View(statusCode=Response::HTTP_CREATED, serializerGroups={"image"})
     * @Rest\Post("/galerie/{id}/image")
     */
    public function postImageGalerieAction(Request $request, Galerie $galerie)
    {
        $image = new Image();
        $image->setFile($request->files->all()['image']);
        $galerie->addImage($image);

        $form = $this->createForm(GalerieType::class, $galerie);

Au niveau du formulaire :


### File GalerieType.php
        $builder
            ->add('titre', TextType::class)
            ->add('createdAt', DateTimeType::class, [
                'required' => false
            ])
            ->add('updatedAt', DateTimeType::class, [
                'required' => false
            ])
            ->add('categorieGalerie',EntityType::class,[
                'class' => CategorieArticle::class
            ])
            ->add('images', CollectionType::class, [
                'entry_type' =>ImageGalerieType::class,
                'allow_add' => true,
                'mapped' => true,
                'required' => true
            ]);

### File ImageGalerieType.php

        $builder
            ->add('file', FileType::class, [
                'multiple' => true,
                'base64' => false,
                'mapped' => true,
            ]);

Voilà j'espère t'avoir aidé avec une de ces solutions.

Bonjour a tous. Désolé pour ma reponse tardive : je ne suis pas returné sur grafikart depuis longtemps.
Merci Matteo. Moi aussi, je n'ai pas trouvé d'où venais l'erreur. J'ai donc suivi le tuto de Maryam : https://maryamdev.blogspot.fr/2017/10/tuto-symfony-3-upload-dune-image-en.html

Ca a l'air un peu plus lourd comme code... mais ca fonctionne parfaitement.

Nous avons donc a present trois methodes.