IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Symfony PHP Discussion :

Images et événements doctrine [2.x]


Sujet :

Symfony PHP

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Janvier 2013
    Messages
    97
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2013
    Messages : 97
    Points : 40
    Points
    40
    Par défaut Images et événements doctrine
    Bonjour à tous,

    J'ai dans mes entités une entité "Shop" et une entité "ImageShop", la première est faite pour gérer des boutiques appartenant à des commerçants et la deuxième est faite pour associer une image à cette boutique.

    J'ai suivi la partie un tutoriel (la partie qui traite l'exemple d'association d'une image en utilisant les événement Doctrine pour ne plus avoir à faire appel à la méthode upload() dans le contrôleur).

    Le tout fonctionne presque bien, j'arrive à rajouter une boutique avec une image, à la modifier sans image (dans ce cas il garde l'ancienne) et à la modifier avec image (dans ce cas il supprime l'ancienne et met la nouvelle).

    Le seul problème que j'ai actuellement c'est lorsque je veux créer une boutique sans image (et donc laisser le champ à NULL), il arrive bien à me mettre le champ image à null mais je ne sais pour quelle raison il veut absolument me persister un objet ImageShop...

    Voici l'erreur que j'obtiens :


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    An exception occurred while executing 'INSERT INTO ImageShop (url, alt) VALUES (?, ?)' with params [null, null]:
     
    SQLSTATE[23000]: Integrity constraint violation: 1048 Le champ 'url' ne peut être vide (null)
    Voici mon entité Shop :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    class Shop
    {
     
    ...
     
        /**
         * @ORM\OneToOne(targetEntity="InApps\PassWayBundle\Entity\ImageShop", inversedBy="shop", cascade={"persist"}, orphanRemoval=true)
         *
         * @Assert\Valid()
         *
         * @Expose
         */
        private $image;
     
     
    ...
     
     
        /**
         * Set image
         *
         * @param \InApps\PassWayBundle\Entity\ImageShop $image
         * @return Shop
         */
        public function setImage(\InApps\PassWayBundle\Entity\ImageShop $image = null)
        {
            $this->image = $image;
            if($image !== null){
               $image->setShop($this);
            }
     
            return $this;
        }
     
    ...
     
    }
    Voici mon entité ImageShop :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    class ImageShop
    {
        /**
         * @var integer
         *
         * @ORM\Column(name="id", type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        private $id;
     
        /**
         * @var string
         *
         * @ORM\Column(name="url", type="string", length=255)
         *
         * @Expose
         */
        private $url;
     
        /**
         * @var string
         *
         * @ORM\Column(name="alt", type="string", length=255)
         *
         * @Expose
         */
        private $alt;
     
        /**
         * @ORM\OnetoOne(targetEntity="InApps\PassWayBundle\Entity\Shop", mappedBy="image")
         */
        private $shop;
     
     
        /**
         * @Assert\Image(
         *      maxSize = "1M",
         *      mimeTypes={"image/png", "image/jpeg", "image/gif"}
         * )
         */
        private $file;
     
        /**
         * Utilisé uniquement pour sauvegarder le nom du fichier pour toujours l'avoir lors du postRemove
         */
        private $tempFilename;
     
     
     
     
     
     
        /**
         * Get id
         *
         * @return integer
         */
        public function getId()
        {
            return $this->id;
        }
     
        /**
         * Set alt
         *
         * @param string $alt
         * @return ImageShop
         */
        public function setAlt($alt)
        {
            $this->alt = $alt;
     
            return $this;
        }
     
        /**
         * Get alt
         *
         * @return string
         */
        public function getAlt()
        {
            return $this->alt;
        }
     
        /**
         * Set file
         *
         * @param file $file
         * @return ImageShop
         */
        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;
            }
        }
     
        /**
         * Get file
         *
         * @return file
         */
        public function getFile()
        {
            return $this->file;
        }
     
        /**
         * Set name
         *
         * @param string $name
         * @return ImageShop
         */
        public function setUrl($url)
        {
            $this->url = $url;
     
            return $this;
        }
     
        /**
         * Get name
         *
         * @return string
         */
        public function getUrl()
        {
            return $this->url;
        }
     
        /**
         * @ORM\PrePersist()
         * @ORM\PreUpdate()
         */
        public function preUpload()
        {
            // Si jamais il n'y a pas de fichier (champ facultatif)
            if (null === $this->file) {
                $this->getShop()->setImage(null);
                return;
            }
     
            //Vérification de l'extension du fichier
            $fileExtension   = $this->file->guessExtension();
     
            //on vérifie l'extension du fichier
            if(!in_array($fileExtension,array("jpg","jpeg","gif","png"))){
                throw new FileException(sprintf("le fichier doit être de type image (jpg, jpeg, gif ou png)."));
            }
     
            // On prend un nom de fichier unique
            $this->url = md5(uniqid()) . '.' . $fileExtension;
     
            // 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->getShop()->getName();
        }
     
        /**
         * @ORM\PostPersist()
         * @ORM\PostUpdate()
         */
        public function upload()
        {
            // Si jamais il n'y a pas de fichier (champ facultatif)
            if (null === $this->file) {
                return;
            }
     
     
     
            // Si on avait un ancien fichier, on le supprime
            if (null !== $this->tempFilename) {
                $oldFile = $this->getUploadRootDir().'/'.$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(),
                $this->url
            );
     
            // On sauvegarde le nom de fichier dans notre attribut $urlImage
            //$this->setUrl($url);
     
            // On crée également le futur attribut alt de notre balise <img> avec le nom de la boutique
            //$this->setAlt($this->getShop()->getName());
        }
     
        /**
         * @ORM\PreRemove()
         */
        public function preRemoveUpload()
        {
            // On sauvegarde temporairement le nom du fichier, car il dépend de l'id
            $this->tempFilename = $this->url;
        }
     
        /**
         * @ORM\PostRemove()
         */
        public function removeUpload()
        {
            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
            return 'uploads/img/Shop/'. $this->getShop()->getDirectory();
        }
     
        protected function getUploadRootDir()
        {
            // On retourne le chemin relatif vers l'image pour notre code PHP
            return __DIR__.'/../../../../web/'.$this->getUploadDir();
        }
     
        /**
         * Set shop
         *
         * @param \InApps\PassWayBundle\Entity\Shop $shop
         * @return ImageShop
         */
        public function setShop(\InApps\PassWayBundle\Entity\Shop $shop = null)
        {
            $this->shop = $shop;
     
            return $this;
        }
     
        /**
         * Get shop
         *
         * @return \InApps\PassWayBundle\Entity\Shop
         */
        public function getShop()
        {
            return $this->shop;
        }
    }
    Dans mon contrôleur je ne fais rien de spécial qui pourrait faire tout foirer si vous voulez je pourrais le rajouter.

    Est-ce que quelqu'un verrait pourquoi il veut absolument me créer une entité ImageShop alors que je met à null l'attribut image de mon entité Shop ? Ca fait au moins une heure que je tourne en rond j'en peux plus ^^

    Merci d'avance,

    Jonathan

  2. #2
    Membre habitué
    Homme Profil pro
    Inscrit en
    Août 2012
    Messages
    98
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Août 2012
    Messages : 98
    Points : 163
    Points
    163
    Par défaut
    Si tu sais qu'en php 'null' et vide sont identiques message d'erreur est assez explicite, le champ url est vide (alt aussi est vide), la solution hyper crado qui ne résoud rien c'est de passer tes champs url et alt à "nullable = true", tu n'auras plus d'erreur, mais ton image ne seras pas persistée.

    La solution un peu plus propre c'est de spécifier à doctrine qu'il doit mapper ImageShop :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    /**
     * algo
     *
     * @ORM\Table(name="")
     * @ORM\Entity
     * @ORM\HasLifecycleCallbacks
     */
    class ImageShop
    N'oublie surtout pas "@ORM\HasLifecycleCallbacks"

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Janvier 2013
    Messages
    97
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2013
    Messages : 97
    Points : 40
    Points
    40
    Par défaut
    Bonjour Garniture et merci pour ton aide,

    Cependant, j'avais déjà essayé de mettre les champs de mon entité ImageShop à nullable=true et comme je m'y attendais ça ne résout rien du tout, le tout fonctionne mais il me crée une ligne orpheline dans la table image_shop de ma base de donnée qui ne sert à rien, moi ce que je voudrais c’est tout simplement qu'il ne persiste pas cette entité et qu'il ne crée pas cette ligne. J’ai donc enlevé le nullable=true sur ces champs c’est pas ce que je voulais.

    Pour l'annotation "@ORM\HasLifecycleCallbacks", elle y est c’est juste que j'ai copié/collé uniquement la classe, si cette annotation n'était pas là rien ne fonctionnerait ^^

    Voici les annotations que j'ai pour information :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
     * ImageShop
     *
     * @ORM\Table()
     * @ORM\Entity(repositoryClass="InApps\PassWayBundle\Entity\ImageShopRepository")
     *
     * @ORM\HasLifecycleCallbacks
     *
     * @ExclusionPolicy("all")
     */
    class ImageShop

    J'ai très bien compris le message d'erreur il est très clair pour moi, il veut me persister mon entité ImageShop et faire un Insert avec des champs à NULL alors que je ne lui en donne pas l'autorisation. Mon problème c'est pas la compréhension de l'erreur mais le fait que je voudrais tout simplement qu'il ne persiste pas mon entité ImageShop quand il n'y a pas d'image...

    Quelqu'un aurait une idée ? ^^

  4. #4
    Membre habitué
    Homme Profil pro
    Inscrit en
    Août 2012
    Messages
    98
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Août 2012
    Messages : 98
    Points : 163
    Points
    163
    Par défaut
    ah zut, je pensais que c'etait un probleme assez simple, tu peux montrer ton fichier BoutiqueType.php ?
    normalement le champ file tu dois ressembler à sa:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     ->add('image', new ImageShopType(), array('required'=>false)
    ton entity Shop elle fois avoir un nullable=true sur l'attribut image

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Janvier 2013
    Messages
    97
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2013
    Messages : 97
    Points : 40
    Points
    40
    Par défaut
    Biensur, le voici :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    /** @DI\Service("form.type.shop") */
    class ShopType extends AbstractType
    {
     
     
        /**
         * @var \Symfony\Component\Security\Core\SecurityContext
         */
        protected $securityContext;
     
        /**
         * @DI\InjectParams({"securityContext" = @DI\Inject("security.context")})
         */
        public function __construct(SecurityContext $securityContext)
        {
            $this->securityContext = $securityContext;
        }
     
         /**
         * @param FormBuilderInterface $builder
         * @param array $options
         */
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $user = $this->securityContext->getToken()->getUser();
     
            $builder
                ->add('name', 'text', array('attr'=> array('class' => 'form-control')))
                ->add('address', 'text', array('attr'=> array('class' => 'form-control')))
                ->add('city', 'text', array('attr'=> array('class' => 'form-control')))
                ->add('postalCode', 'text', array('attr'=> array('class' => 'form-control')))
                ->add('country', 'text', array('attr'=> array('class' => 'form-control')))
                ->add('description', 'tinymce')
                ->add('image', new ImageShopType())
                ->add('lists','entity', array(
                    'class'        => 'InAppsPassWayBundle:ListUser',
                    'property'     => 'name',
                    'multiple'     => 'true',
                    'expanded'     => 'true',
                    'query_builder' => function(\InApps\PassWayBundle\Entity\ListUserRepository $r) use($user) {
                        return $r->getListsOfUser($user);
                    }
                ))
            ;
        }
     
        /**
         * @param OptionsResolverInterface $resolver
         */
        public function setDefaultOptions(OptionsResolverInterface $resolver)
        {
            $resolver->setDefaults(array(
                'data_class' => 'InApps\PassWayBundle\Entity\Shop'
            ));
        }
     
        /**
         * @return string
         */
        public function getName()
        {
            return 'inapps_passwaybundle_shop';
        }
    }


    Je te met aussi la fonction de mon contrôleur même s'il n'y a rien de bien intéressant à l'intérieur, on ne sait jamais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    public function createShopAction()
        {
            if(!$this->get('security.context')->isGranted('IS_AUTHENTICATED_FULLY')){
                throw new AccessDeniedException("création d'une boutique");
            }
     
            $security_context = $this->get('security.context');
            $shop = new Shop();
            $formShop = $this->createForm(new ShopType($security_context), $shop);
     
            $request = $this->get('request');
            if ($request->getMethod() == 'POST') {
                $formShop->handleRequest($request);
     
                if($formShop->isValid()){
     
                    $em = $this->getDoctrine()->getManager();
     
                    $user = $this->getUser();
     
                    //$shop -> getImage() -> upload();//plus besoin on le gère avec les événement doctrine !
     
                    $em->persist($shop);
                    $this->getUser()->addShop($shop);
                    $em->flush();
     
     
     
                    $this->get('session')->getFlashBag()->add(
                        'info',
                        'Votre boutique a bien été créée.'
                    );
     
                    return $this->redirect($this->generateUrl('in_apps_pass_way_manage_shops'));
                }
                else{
                    $this->get('session')->getFlashBag()->add(
                        'danger',
                        'Le formulaire est incomplet, veuillez résoudre les problèmes ci-desous :'
                    );
                }
            }
     
     
            return $this->render('InAppsPassWayBundle:Shop:createShop.html.twig', array(
                'formShop' => $formShop->createView()
            ));
        }

  6. #6
    Membre éprouvé
    Homme Profil pro
    Inscrit en
    Juin 2011
    Messages
    725
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Juin 2011
    Messages : 725
    Points : 1 050
    Points
    1 050
    Par défaut
    Bonjour,

    Effectivement dans tous les cas ImageShopType va renvoyer un objet ImageShop, qu'il y ait eut upload ou pas.

    Peut-être pourrait tu regarder du coté des DataTransformer afin que ton sous-formulaire "Image" renvoit null si image n'est pas encore persisté et que l'utilisateur n'a pas uploadé de fichier.

    lien vers la doc:
    http://symfony.com/fr/doc/current/co...nsformers.html

    Il me semble qu'il a une petite subtilité, c'est qu'un formulaire peut avoir +sieurs datatransformers et que l'on traite le résultat du précédent DataTransformer.
    Ce qui veut dire que tu prend en entrée un objet Image et que tu renverra soit null soit cet objet Image (on aurait pu penser que l'on prenait simplement en entrée le tableau php des données soumises par l'utilisateur pour le transformer en objet).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
        /**
         * @return Image | null
         */
        public function reverseTransform($image)
        {
            if(!$image->getId()){
               if(!$image->getFile()){
                   return null;
               }
            }
            return $image;
        }
    par curiosité à quoi correspond l'annotation @Expose ?

  7. #7
    Membre du Club
    Profil pro
    Inscrit en
    Janvier 2013
    Messages
    97
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2013
    Messages : 97
    Points : 40
    Points
    40
    Par défaut
    Bonjour Arnooo999 et merci pour ton aide,

    Je vais un peu regarder tout ce dont tu m'as parlé et effectuer quelques tests, je repasserai très probablement par là plus tard ou demain si j'ai un soucis, pour le moment ce que tu as link est assez flou pour moi mais je n'ai pas encore lu ton lien vers la doc, je verrais bien. Je reposterai probablement ici ^^

    En ce qui concerne l'annotation @Expose elle est utilisée par le bundle JMSSerializeBundle pour indiquer lors de la sérialisation de mon objet s'il doit afficher ("exposer") ou non cette attribut. Le résultat est ensuite renvoyé à nos web services REST.

  8. #8
    Membre du Club
    Profil pro
    Inscrit en
    Janvier 2013
    Messages
    97
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2013
    Messages : 97
    Points : 40
    Points
    40
    Par défaut
    Citation Envoyé par garniture Voir le message
    ah zut, je pensais que c'etait un probleme assez simple, tu peux montrer ton fichier BoutiqueType.php ?
    normalement le champ file tu dois ressembler à sa:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     ->add('image', new ImageShopType(), array('required'=>false)
    ton entity Shop elle fois avoir un nullable=true sur l'attribut image
    Je n'avais pas vu que tu avais edit ton post Garniture. Le nullable=true n'est pas nécessaire ça l'est de base.

    Par contre j'ai rajouté le array('required'=>false) comme tu le suggérais (j'avais complètement oublié de le mettre) et ça a l'air de parfaitement fonctionner, merci beaucoup, je passe en résolu ! ^^

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [AC-2007] Problème d'évènement sur image
    Par warback dans le forum VBA Access
    Réponses: 4
    Dernier message: 25/11/2009, 21h33
  2. [DOM] Gérer l'événement onMouseOver sur une image créée avec DOM
    Par sab_etudianteBTS dans le forum Général JavaScript
    Réponses: 10
    Dernier message: 20/02/2008, 12h06
  3. Utiliser une image pour un événement avec swing/awt
    Par Tavarez59 dans le forum AWT/Swing
    Réponses: 1
    Dernier message: 23/03/2007, 09h39
  4. gérer l'évènement Click du contrôle Image
    Par naruto01 dans le forum VB 6 et antérieur
    Réponses: 7
    Dernier message: 30/01/2007, 18h43
  5. Gestion des évènements lors d'un clique sur une image.
    Par yoghisan dans le forum Débuter
    Réponses: 7
    Dernier message: 23/06/2005, 19h04

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo