Search code examples
symfonydoctrine-ormvichuploaderbundleimage-file

image_name in database is always null vichuploaderbundle symfony2


I'm trying to add a new user with an image file. I'm using VichUpladerBundle and FOSUserBundle. So the new user is created but image_name is always null.

This is my Image Entity :

namespace SocialNetworkBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Gedmo\Mapping\Annotation as Gedmo;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\HttpFoundation\File\File;

/**
 * Image
 *
 * @ORM\Table(name="image")
 * @ORM\Entity(repositoryClass="SocialNetworkBundle\Repository\ImageRepository")
 * @Vich\Uploadable 
 */
class Image {

    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

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

    /**
     * NOTE: This is not a mapped field of entity metadata, just a simple property.
     * 
     * @Vich\UploadableField(mapping="image", fileNameProperty="imageName")
     * @Assert\File(
     *     maxSize = "1024k",
     *     mimeTypes = {"image/png", "image/jpeg", "image/jpg"},
     *     mimeTypesMessage = "Please upload a valid PDF or valid IMAGE"
     * )
     * 
     * @var File
     */
    private $imageFile;

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

    /**
     * @ORM\Column(type="datetime")
     *
     * @var \DateTime
     */
    private $updatedAt;

    /**
     * Set imageName
     *
     * @param string $imageName
     *
     * @return Image
     */
    public function setImageName($imageName) {
        $this->imageName = $imageName;

        return $this;
    }

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

    /**
     * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
     *
     * @return Image
     */
    public function setImageFile(File $image = null) {
        $this->imageFile = $image;

        if ($image) {
            $this->updatedAt = new \DateTime('now');
        }

        return $this;
    }

    /**
     * @return File|null
     */
    public function getImageFile() {
        return $this->imageFile;
    }

}

User entity :

namespace UserBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\HttpFoundation\File\File;    
use SocialNetworkBundle\Entity\Image ;

/**
 * User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="UserBundle\Repository\UserRepository") 
 * @Vich\Uploadable
 */
class User extends BaseUser 
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255, unique=false)
     * @Assert\Length(min=2, max=100)
     */
    private $name;

    /**
     * @ORM\OneToOne(targetEntity="SocialNetworkBundle\Entity\Image", cascade={"persist", "merge", "remove"})
     * @ORM\JoinColumn(name="image_id", referencedColumnName="id")
     * @Assert\Valid()
     */
    private $image;

    /**
     * Set name
     *
     * @param string $name
     *
     * @return User
     */
    public function setName($name) 
    {
        $this->name = $name;

        return $this;
    }

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

    /**
     * Set image
     *
     * @param \SocialNetworkBundle\Entity\Image $image
     *
     * @return User
     */
    public function setImage(\SocialNetworkBundle\Entity\Image $image = null) 
    {
        $this->image = $image;

        return $this;
    }

    /**
     * Get image
     *
     * @return \SocialNetworkBundle\Entity\Image
     */
    public function getImage() 
    {
        return $this->image;
    }
}

Image Type :

namespace SocialNetworkBundle\Form;

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

class ImageType extends AbstractType
{
  public function buildForm(FormBuilderInterface $builder, array $options)
  {
    $builder
     ->add('imageFile', VichFileType::class, array(
                    'required'      => false,
                    'allow_delete'  => true, // not mandatory, default is true
                    'download_link' => true, // not mandatory, default is true
                    ))

    ;
  }

  public function setDefaultOptions(OptionsResolverInterface $resolver)
  {
    $resolver->setDefaults(array(
        'data_class' => 'SocialNetworkBundle\Entity\Image',
    ));
  }

  public function getName()
  {
    return 'socialnetworkbundle_image';
  }
}

RegistrationForm :

namespace UserBundle\Form\Type;

use Symfony\Component\Form\FormBuilderInterface;
use FOS\UserBundle\Form\Type\RegistrationFormType as BaseType;
use SocialNetworkBundle\Form\ImageType;

class RegistrationFormType extends BaseType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        parent::buildForm($builder, $options);

        // add your custom field
        $builder->add('name')
            ->add('roles', 'collection', array(
                'type'    => 'choice',
                'options' => array(
                    'choices' => array(
                        'ROLE_ADMIN' => 'Admin',
                    ),
                ),
             ))
             ->add('image', new ImageType())
         ;
    }

    public function getName()
    {
        return 'user_registration';
    }
}

And register.html.twig

{% extends "UserBundle::layout.html.twig" %}


{% block body %}
    <center> <h1> Inscription </h1> </center>
    <aside class="col-sm-3">
        <div class="panel panel-default">
            <div class="panel-heading">Inscription</div>
            <div class="panel-body">
                Veuillez remplir les champs 
            </div>
        </div>
    </aside>

    <!--timeline-->
    <section class="timeline col-sm-9">
        <!--post Timeline-->
        <div class="thumbnail thumbnail-post">
            <!--caption-->
            <div class="caption">
                <form action="{{ path('fos_user_registration_register') }}" {{ form_enctype(form) }} method="POST" class="form-horizontal">
                    <div class="form-group">    
                        {{ form_errors(form.name) }}    
                        <div class="col-sm-9">
                            Nom   {{ form_widget(form.name,  { 'attr': {'class': 'form-control', 'placeholder': 'form.name'|trans } })}}
                        </div>
                    </div>

                    <div class="form-group">    
                        {{ form_errors(form.email) }}
                        <div class="col-sm-9">
                            Email   {{ form_widget(form.email, { 'attr': {'class': 'form-control', 'placeholder': 'form.email'|trans } }) }}
                        </div>
                    </div>
                    <div class="form-group">
                        {{ form_errors(form.username) }}
                        <div class="col-sm-9">
                            Pseudo   {{ form_widget(form.username, { 'attr': {'class': 'form-control', 'placeholder': 'form.username'|trans } }) }}
                        </div>
                    </div>    
                    <div class="form-group">
                        {{ form_errors(form.plainPassword.first) }} 
                        <div class="col-sm-9">    
                            Mot de passe    {{ form_widget(form.plainPassword.first, { 'attr': {'class': 'form-control', 'placeholder': 'form.password'|trans } }) }}
                        </div>
                    </div>

                    <div class="form-group">
                        {{ form_errors(form.plainPassword.second) }}
                        <div class="col-sm-9">  
                            Confirmer le mot de passe    {{ form_widget(form.plainPassword.second, { 'attr': {'class': 'form-control', 'placeholder': 'form.password_confirmation'|trans } }) }}
                        </div>
                    </div>

                    <div class="form-group">
                        {# Génération du label. #}
                        {{ form_label(form.image.imageFile, "Image", {'label_attr': {'class': 'col-sm-3 control-label'}}) }}

                        {# Affichage des erreurs pour ce champ précis. #}
                        {{ form_errors(form.image.imageFile) }}

                        <div class="col-sm-4">
                            {# Génération de l'input. #}
                            {{ form_widget(form.image.imageFile, {'attr': {'class': 'form-control'}}) }} 
                        </div>
                    </div>
                    <br />

                    <div id="roles">
                        <div class="form-group">
                            <div class="col-sm-4">   {{ form_widget(form.roles, { 'attr': {'class': 'form-control', 'placeholder': 'form.role'|trans } }) }}
                            </div>
                            {{ form_errors(form.roles) }}
                        </div>
                    </div>
                    {{ form_rest(form) }}

                    <div class="form-group">
                        <div class="col-md-4 col-sm-4 col-xs-12 col-md-offset-3">
                            <input class="btn btn-default submit" type="submit" value="{{ 'registration.submit'|trans }}">
                        </div>
                    </div>
                </form>
            </div> <!--#caption-->

            <!--#post timeline-->
        </div>
        <!--#timeline-->
    </section>
{% endblock %}

{% block js %}
    <script>
        $(document).ready(function () {
            $('#roles').hide();
        });
    </script>
{% endblock %} 

config.yml

imports:
    - { resource: parameters.yml }
    - { resource: security.yml }
    - { resource: services.yml }
    - { resource: "@SocialNetworkBundle/Resources/config/services.yml" }
    - { resource: "@UserBundle/Resources/config/services.yml" }

# Put parameters here that don't need to change on each machine where the app is deployed
# http://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
    locale: en
    fos_user.template.engine: 'twig'

framework:
    #esi:             ~
    #translator:      { fallbacks: ["%locale%"] }
    secret:          "%secret%"
    router:
        resource: "%kernel.root_dir%/config/routing.yml"
        strict_requirements: ~
    form:            ~
    csrf_protection: ~
    validation:      { enable_annotations: true }
    #serializer:      { enable_annotations: true }
    templating:
        engines: ['twig']
    default_locale:  "%locale%"
    trusted_hosts:   ~
    trusted_proxies: ~
    session:
        # handler_id set to null will use default session handler from php.ini
        handler_id:  ~
    fragments:       ~
    http_method_override: true

# Twig Configuration
twig:
    debug:            "%kernel.debug%"
    strict_variables: "%kernel.debug%"
    form_themes:
        # other form themes
        - 'VichUploaderBundle:Form:fields.html.twig'

# Doctrine Configuration
doctrine:
    dbal:
        driver:   pdo_mysql
        host:     "%database_host%"
        port:     "%database_port%"
        dbname:   "%database_name%"
        user:     "%database_user%"
        password: "%database_password%"
        charset:  UTF8
        # if using pdo_sqlite as your database driver:
        #   1. add the path in parameters.yml
        #     e.g. database_path: "%kernel.root_dir%/data/data.db3"
        #   2. Uncomment database_path in parameters.yml.dist
        #   3. Uncomment next line:
        #     path:     "%database_path%"

    orm:
        auto_generate_proxy_classes: "%kernel.debug%"
        naming_strategy: doctrine.orm.naming_strategy.underscore
        auto_mapping: true

# Swiftmailer Configuration
swiftmailer:
    transport: "%mailer_transport%"
    host:      "%mailer_host%"
    username:  "%mailer_user%"
    password:  "%mailer_password%"
    spool:     { type: memory }


# FOSUser Configuration
fos_user:
    db_driver:     orm                       
    # Le type de BDD à utiliser, nous utilisons l'ORM Doctrine depuis le début
    firewall_name: main                      
    # Le nom du firewall derrière lequel on utilisera ces utilisateurs
    user_class:    UserBundle\Entity\User 
    # La classe de l'entité User que nous utilisons
    #autoriser l'envoie de mail si reset password
    service:
        mailer: fos_user.mailer.twig_swift
    registration:
        form:
            type: user_registration

# Assetic            
assetic:
    debug:          '%kernel.debug%'
    use_controller: false
    bundles:
        - "SocialNetworkBundle"
    filters:
        cssrewrite: ~
        jpegoptim:
            bin: /usr/local/bin/jpegoptim
            max: 20
            apply_to: "\.jpg$"
    twig:
        functions:
            jpegoptim: ~

#VichUploader
vich_uploader:
    db_driver: orm

    mappings:
        user_image:
            uri_prefix:         /SocialNetowrk/web/images/user
            upload_destination: '%kernel.root_dir%/../web/images/user'
            inject_on_load:     false
            delete_on_update:   true
            delete_on_remove:   true
            namer:              vich_uploader.namer_uniqid

in my database i had a null value at column image_name. How can i resolve that?


Solution

  • You should add one mapped field (for example, updatedAt) into your Image entity. When you set your image using method setImageFile you also should update the mapped field to trigger doctrine event listeners and process an uploaded file.

    namespace SocialNetworkBundle\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    use Symfony\Component\Validator\Constraints as Assert;
    use Gedmo\Mapping\Annotation as Gedmo;
    use Vich\UploaderBundle\Mapping\Annotation as Vich;
    use Symfony\Component\HttpFoundation\File\File;
    
    /**
     * Image
     *
     * @ORM\Table(name="image")
     * @ORM\Entity(repositoryClass="SocialNetworkBundle\Repository\ImageRepository")
     * @Vich\Uploadable 
     */
    class Image 
    {    
        /**
         * @var int
         *
         * @ORM\Column(name="id", type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        private $id;
    
        /**
         * Get id
         *
         * @return int
         */
        public function getId() 
        {
            return $this->id;
        }
    
        /**
         * NOTE: This is not a mapped field of entity metadata, just a simple property.
         * 
         * @Vich\UploadableField(mapping="image", fileNameProperty="imageName")
         * @Assert\File(
         *     maxSize = "1024k",
         *     mimeTypes = {"image/png", "image/jpeg", "image/jpg"},
         *     mimeTypesMessage = "Please upload a valid PDF or valid IMAGE"
         * )
         * 
         * @var File
         */
        private $imageFile;
    
        /**
         * @ORM\Column(type="string", length=255, nullable=true)
         *
         * @var string
         */
        private $imageName;
    
        /**
         * @ORM\Column(type="datetime")
         *
         * @var \DateTime
         */
        private $updatedAt;
    
        /**
         * Set imageName
         *
         * @param string $imageName
         *
         * @return Image
         */
        public function setImageName($imageName)
        {
            $this->imageName = $imageName;
    
            return $this;
        }
    
        /**
         * Get imageName
         *
         * @return string
         */
        public function getImageName()
        {
            return $this->imageName;
        }
    
        /**
         * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
         *
         * @return Image
         */
        public function setImageFile(File $image = null) 
        {
            $this->imageFile = $image;
    
            if ($image) {
                $this->updatedAt = new \DateTime('now');
            }
    
            return $this;
        }
    
        /**
         * @return File|null
         */
        public function getImageFile() 
        {
            return $this->imageFile;
        }
    }