User Tools

Site Tools


backend:symfonyvich

Symfony : vich/uploader-bundle

logo


Il permet d'ajouter la gestion d'upload de fichiers : Ease file uploads attached to entities Il suffit de suivre la documentation pour pouvoir l'utiliser.

Installation

composer require vich/uploader-bundle

Configuration

Namers

Le bundle utilise des noms pour nommer les fichiers et répertoires qu'il enregistre dans le système de fichiers. Un Namer implémente l'interface Vich\UploaderBundle\Naming\NamerInterface.
Si aucun nom n'est configuré pour un mappage, le bundle utilisera simplement le nom du fichier qui a été téléchargé (ceci est obsolète).
vous pouvez utiliser l'un des noms fournis ou en implémenter un personnalisé.
Il y a plusieurs noms disponibles :

  • Vich\UploaderBundle\Naming\UniqidNamer
  • Vich\UploaderBundle\Naming\OrignameNamer
  • Vich\UploaderBundle\Naming\PropertyNamer
  • Vich\UploaderBundle\Naming\HashNamer
  • Vich\UploaderBundle\Naming\Base64Namer
  • Vich\UploaderBundle\Naming\SmartUniqueNamer (intéressant pour le référencement)
  • Vich\UploaderBundle\Naming\SlugNamer

Voir la documentation officielle : Namers

vich_uploader.yaml

Dans le fichier config\packages\vich_uploader.yaml, il faut configurer :

vich_uploader:
  db_driver: orm 

  mappings:
    pin_image:
      uri_prefix: /upload/pins
      upload_destination: '%kernel.project_dir%/public/upload/pins'
      namer: Vich\UploaderBundle\Naming\SmartUniqueNamer

Entité

Il faut importer les classes suivantes dans l'entité :

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

Et mettre l'annotation :

* @Vich\Uploadable

On ajoute ensuite la propriété correspondant au fichier image, ce champs ne sera pas ajouté à la base de données.
Il faut indiquer :

  • le mapping comme défini dans le fichier config\packages\vich_uploader.yaml (ici : “pin_image”)
  • le nom de la propriété correspondant au nom de l'image (ici : “imageSize”)
  • le nom de la propriété correspondant à la taille de l'image (ici : “imageSize”)
/**
     * NOTE: This is not a mapped field of entity metadata, just a simple property.
     * 
     * @Vich\UploadableField(mapping="pin_image", fileNameProperty="imageName", size="imageSize")
     * 
     * @var File|null
     */
    private $imageFile;

On ajoute le geter et le seter pour cette propriété :

/**
  * If manually uploading a file (i.e. not using Symfony Form) ensure an instance
  * of 'UploadedFile' is injected into this setter to trigger the update. If this
  * bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
  * must be able to accept an instance of 'File' as the bundle will inject one here
  * during Doctrine hydration.
  *
  * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile|null $imageFile
  */
public function setImageFile(?File $imageFile = null): void
{
    $this->imageFile = $imageFile;
 
    if (null !== $imageFile) {
        // It is required that at least one field changes if you are using doctrine
        // otherwise the event listeners won't be called and the file is lost
        $this->updatedAt = new \DateTimeImmutable();
    }
}
 
public function getImageFile(): ?File
{
    return $this->imageFile;
}

Pour le seter, il faut mettre à jour une propriété pour que ça fonctionne (ici c'est : updatedAtpour mettre à jour la propriété imageFile.))
Il est nécessaire qu'au moins un champ change si on utilise doctrine, sinon les écouteurs d'événements ne seront pas appelés et le fichier est perdu

On peut utiliser le seter de updatedAt :

$this->setUpdatedAt(new \DateTimeImmutable);

Il faut vider le cache une fois l'entité configurée.

Evénements du cycle de vie

On peut optionnellement personnaliser le comportement à adopter lorsque les entités sont hydratées, mises à jour ou supprimées.
Trois options de configuration simples existent :

  • delete_on_remove : defaut true, le fichier doit-il être supprimé lorsque l'entité est supprimée ?
  • delete_on_update : defaut true, le fichier doit-il être supprimé lorsqu'un nouveau fichier est téléchargé ?
  • inject_on_load : defaut false, si le fichier doit être injecté dans l'entité téléchargeable lorsqu'il est chargé à partir du magasin de données ?. L'objet sera uen instance de Symfony\Component\HttpFoundation\File\File.

Ceci doit être configuré dans le fichier config\packages\vich_uploader.yaml :

vich_uploader:
  db_driver: orm 
 
  mappings:
    pin_image:
      uri_prefix: /upload/pins
      upload_destination: '%kernel.project_dir%/public/upload/pins'
      namer: Vich\UploaderBundle\Naming\SmartUniqueNamer
 
      inject_on_load: false
      delete_on_update: true
      delete_on_remove: true

Utilisation

VichImageType Field

Il faut ajouter le champs imageFile au formulaire (ici src\Form\PinType.php) pour pouvoir choisir un fichier image :

->add('imageFile', VichImageType::class, [
  'label' => 'Image (JPG or PNG file)',
  'required' => false,
  'allow_delete' => true,
  'download_label' => '...',
  'download_uri' => true,
  'image_uri' => true,
  'imagine_pattern' => '...',
  'asset_helper' => true,
])

Les paramètre sont :

  • 'label' : label du champs dans le formulaire
  • 'required' : champs obligatoire ou non (vérification HTML uniquement),
  • 'allow_delete' : permet d'afficher le bouton 'Delete?'' dans le formulaire
  • 'download_label' : label du lien permettant de télécharger l'image
  • 'download_uri' : si false, il n'affiche pas le lien permettant de télécharger l'image, mais on peut quand même télécharger l'image en cliquant dessus.
  • 'image_uri'
  • 'imagine_pattern' : permet de créer plusieurs versions d'une image
  • 'asset_helper' : indique si l'uri de téléchargement est généré avec la methode asset() du composant symfony/asset (c'est la fonction Twig)

Pour créer plusieurs versions d'une image (plusieurs dimensions, noir et blanc…), on utilise 'imagine_pattern' et il faut installer le bundle LiipImagineBundle .

il faut aussi importer la classe :

use Vich\UploaderBundle\Form\Type\VichImageType;

Il faut que l'extension fileinfo soit activée dans le fichier php.ini pour que ça fonctionne : extension=fileinfo

Affichage du fichier dans le formulaire en JS

On va modifier le fichier public\build\app.js.
Pour être sur que les changements soient pris en compte, il vaut mieux faire :

npm run build

au lieu de npm run watch

Le champs du formulaire a la classe custom-file-input :

<input type="file" id="pin_imageFile_file" name="pin[imageFile][file]" class="custom-file-input">

En JS, on va :

  • Ecouter lorsqu'il y a un changement dans ce champs de la classe .custom-file-input
  • Récupérer l'évenement e dans la variable inputFile
  • Rechercher dans le parent de .custom-file-input, le champs .custom-file-label
  • lui affecter ka valeur inputFile.files[0].name qui est le nom du fichier sélectionné
$('.custom-file-input').on('change', function (e) {
  var inputFile = e.currentTarget;
  $(inputFile).parent().find('.custom-file-label').html(inputFile.files[0].name);
})

Limitation de la taille d'une image

On utilise la contrainte maxSize de file (car image hérite de file), dans la propriété concernée, pour pouvoir limiter la taille d'une image sélectionnée :

  /**
   * NOTE: This is not a mapped field of entity metadata, just a simple property.
   * 
   * @Vich\UploadableField(mapping="pin_image", fileNameProperty="imageName")
   * 
   * @Assert\Image(maxSize="8M", maxSizeMessage="Le fichier est trop volumineux ({{ size }} {{ suffix }}), La taille maximale autorisée est de {{ limit }} {{ suffix }}.")
   * 
   * @var File|null
   */
  private $imageFile;

Il faut mettre à jour la variable upload_max_filesize = 8M dans le fichier php.ini

On peut aussi ajouter des contraintes directement dans le formulaire src\Form\PinType.php en important les classes nécessaires (ici Image) :

use Symfony\Component\Validator\Constraints\Image;
 
class PinType extends AbstractType
{
  public function buildForm(FormBuilderInterface $builder, array $options)
  {
    $builder
      ->add('imageFile', VichImageType::class, [
        'label' => 'Image (JPG or PNG file)',
        'required' => false,
        'allow_delete' => true,
        'download_uri' => false,
        'constraints' => [
          new Image([
            'maxSize' => '1M',
            'maxSizeMessage' => 'Le fichier est trop volumineux ({{ size }} {{ suffix }}), La taille maximale autorisée est de {{ limit }} {{ suffix }}.'
          ])
        ]
      ])
      ->add('title')
      ->add('description');
  }

Avec ce système, on peut éffectuer des vérifications pour voir des contraintes différentes :

class PinType extends AbstractType
{
  public function buildForm(FormBuilderInterface $builder, array $options)
  {
    $isEdit = $options['method'] === 'PUT';
    $imageFileConstraints = [
      new Image([
        'maxSize' => '1M',
        'maxSizeMessage' => 'Le fichier est trop volumineux ({{ size }} {{ suffix }}), La taille maximale autorisée est de {{ limit }} {{ suffix }}.'
      ])
    ];
 
    if ($isEdit) {
      $imageFileConstraints = [...]; // nouvelles contraintes
    }
 
    $builder
      ->add('imageFile', VichImageType::class, [
        'label' => 'Image (JPG or PNG file)',
        'required' => false,
        'allow_delete' => true,
        'download_uri' => false,
        'constraints' => $imageFileConstraints
      ])
      ->add('title')
      ->add('description');
  }

Affichage des images : Generating URLs

Generating a URL in a Twig Template : on utilise la fonction vich_uploader_asset :

<img src="{{ vich_uploader_asset(pin) | imagine_filter('squared_thumbnail_medium') }}" alt="{{ pin.title }}" />

On peut aussi faire un test pour savoir sur le pin existe :

<img src="{{ (pin.imageName ? vich_uploader_asset(pin) : asset('img/404.jpg')) | imagine_filter('squared_thumbnail_medium') }}" alt="{{ pin.title }}" />
backend/symfonyvich.txt · Last modified: 2020/09/16 10:35 (external edit)