Bonjour,

Alors c'est très très bête, mais je dois avouer que je n'arrive pas vraiment à trouver la solution depuis des heures...

<?php

namespace App\Controller;

class MonImageController implements NamerInterface, ConfigurableInterface
{

    public function SetId()
    {
        $test = uniqid();
        return $test;
    }

    public function name($object, PropertyMapping $mapping): string
    {
        $file = $mapping->getFile($object);
        $name = $file->getClientOriginalName();
        if ($this->transliterate) {
            $name = $this->transliterator->transliterate($name);
        }
        return $this->SetId().'-'.$name;  //Retourner l'identifiant à ce niveau
    }

    public function __invoke(Request $request)
    {
        $livre = new Livre();
        $file = $request->files->get('file');
        $livre->setFile($file);
        $livre->setCreatedAt(new \DateTime('Europe/Paris'));
        if (!$file) {
            throw new BadRequestHttpException('Aucune photo importée');
        }
        $livre->setNom($request->get('Nom'));
        $livre->setIdentifiant($this->setId()); //Retourner le même identifiant à ce niveau
        return $livre;
    }
}

Ce que je veux

Je cherche à déclarer une variable, dans ma classe PHP, pour pouvoir l'utiliser dans 2 fonctions différentes.
La variable que je souhaite déclarer est un identifiant unique (via uniqid() ou Uuid de Symfony) pour pouvoir renommer mon fichier par cet identifiant unique et, par la même occasion, déclarer le champ identifiant dans une autre fonction mais toujours dans la même classe.
Alors j'ai tenté de créer une fonction setId(), au sein de ma classe PHP, et de l'utiliser dans les 2 autres fonctions sauf que je n'obtiens pas le même identifiant (logique...).
Comment faire pour générer un identifiant unique et le stocker dans une variable pour pouvoir l'utiliser dans mes 2 fonctions ?
Je précise que le code fonctionne très bien (peut-être pas optimisé ?), je cherche juste à obtenir un identifiant unique pour pouvoir l'utiliser dans mes 2 fonctions

Ce que j'obtiens

Si je déclare la variable avec, par exemple :

    public function SetId()
    {
        $test = uniqid();
        return $test;
    }

Je vais obtenir un identifiant différent pour chaque appel de mes 2 fonctions.
Et il est impossible de déclarer une variable dans ma classe, comme ci-dessous, et de l'utiliser sinon j'obtiens l'erreur :
Fatal error: Constant expression contains invalid operations in .../Controller/MonImageController.php on line XX

class MonImageController implements NamerInterface, ConfigurableInterface
{
    private $uniqId = uniqid();

}

12 réponses


dev.skoro
Auteur
Réponse acceptée

Re,

@gillesr j'utilise Symfony pour mon projet et j'ai un code similitaire au tiens. Pour information, pour obtenir l'extension du fichier avec ...\File\UpladedFile, tu peux utiliser :

$fileName = $safeFilename.'-'.uniqid().'.'.$file->getClientOriginalExtension();

au lieu de :

$fileName = $safeFilename . '-' . uniqid() . '.' . $file->guessExtension();

Bon, j'ai enfin réussi ce que je souhaitais faire !

Je récupère le nom du fichier via mon service et je le déclare à ma variable $name :

$name = fileUploader->upload($uploadedFile);    //return : NJZVte3i6RzkgMLEnc3Nwd.jpg
$identifiant = substr($name, 0, strpos($name, ".")); //return : NJZVte3i6RzkgMLEnc3Nwd (enlève le point + extension du fichier)

Bon c'était pas le problème du siècle, ni la chose indispensable à mon projet mais c'était un bon défi.

Merci @all ! :)

Bonjour,
tu peux stocker la donnée dans un attribrut de ta classe

Re,

J'ai tenté de le stocker dans un attribut de ma classe (voir la fin de mon post) mais j'obtiens l'erreur
Constant expression contains invalid operations

C'est là que je bloque, je sais que nous pouvons définir une variable avec une chaîne de caractères ou alors des opérations, mais avec uniqid() j'obtiens cette erreur.
Donc d'un côté impossible de le stocker dans un attribut et de l'autre, si j'utilise une fonction il me donne 2 identifiants différents car 2 appels différents

alors passe par le constructeur pour renseigner ton attribut
$this->uniquid = uniqid();

Déjà tenté, mais ça me donne aussi 2 identifiants différents...
Est-ce aussi difficile de faire cette simple chose ? :'(

En fait c'est difficile, car tu n'a pas la main sur quand sont call les 2 methodes de ton contrôleur.

D'un côté tu as ton call lors de l'upload via __invoke() et de l'autre un call avec la lib d'upload qui passe par la méthode name().

Si tu veux avoir le même identifiant il faut soit le génerer et le transmettre pour tous les services qui en n'on besoin. Soit il faut le stoquer quelque par et le retrouver plus tard.

A voir si la lib ne permet pas d'avoir un accès au nom générer mais en soit de ce que je comprends, tu ne devrait pas avoir à gérer la sauvegarde du nom du fichier vu que c'est déjà effectué par la lib. Perso je ne connais pas la lib utilisé, juste vu vite fait un article et la doc sur github.

Quand je regarde la lib je me demande vraiment pourquoi tu as besoin de faire tout ça ? Cela semble gérer de base.

Re,

Alors en effet, le bundle VichUploader peut gérer le nom du fichier. Dans le cas du OrignameNamer (une des classes qui gére le nom du fichier), le bundle va récupérer le nom du fichier original et ajouter un uniqid() avant le fichier pour rendre le nom du fichier vraiment unique (uniqid() + nom du fichier original. Exemple : 6319dcaad82d6_nom_original.png).

Dans mon cas, je souhaite stocker l'identifiant unique généré pour pouvoir l'ajouter à un champ de ma base de donnée.
Si je remplace l'identifiant par un nom, j'aurais pu obtenir utilisateur1_nom_original.png et pour identifiant utilisateur1 mais je préfère avoir un identifiant unique généré automatiquement.

Ainsi, je pourrais avoir 6319dcaad82d6_nom_original.png pour mon nom de fichier et stocker l'identifiant 6319dcaad82d6 dans un champ de ma base de donnée.
Bien sûr, le but n'est pas d'avoir ce résultat à tout prix, car ce n'est pas indispensable à mon projet, mais je suis curieux à l'idée de savoir comment résoudre ce "petit" problème :)

Bonjour,

Quand je veux uploader des fichiers, je crée un service en charge de l'upload.
Ce service est une simple classe qui contient un méthode upload à laquelle je passe en paramètre (à minima) le fichier et le dossier de destination.
Cette méthode fait le travail necessaire sur le fichier et le met au bon endroit puis me renvoie soit le nom généré du fichier uploadé, soit une erreur.

Dans mon controller, je n'ai plus qu'à tester le retour de la méthode upload et utiliser le nom renvoyé pour mettre à jour mon entité avec le nom unique.

tu renseignes ton attribut dans le constructeur et ensuite dans tes méthodes, tu vas chercher l'attribut et non la fonction uniqid comme ça tu est sur d'avoir le même.

Re,

@gillesr tu arrives à faire l'upload de fichier, récupérer le nom généré pour l'inclure dans un champs de ton entité en même temps ? J'ai aussi pensé à cette méthode, mais pour la difficulté est là : faire ces opérations en même temps.

@Carouge10 tu as un exemple ? Lorsque j'essaye différentes méthodes, j'obtiens toujours un uniqid() différent

Oui, car j'ai décomposé en plusieurs étapes.
J'ai un service FileUploadService (que j'utilise dans symfony, mais ça peut marcher sur le même principe sans symfony) dont voici une version très simplifiée :


<?php

namespace App\Service;

use Symfony\Component\String\Slugger\SluggerInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\File\Exception\FileException;

class FileUploadService
{
    public function __construct(private SluggerInterface $slugger)
    {
    }

    public function upload(UploadedFile $file, string $folder)
    {
        $originalFilename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
        $safeFilename = $this->slugger->slug($originalFilename);
        $fileName = $safeFilename . '-' . uniqid() . '.' . $file->guessExtension();

        // vérification existence du dossier de destination
        // etc.

        try {
            $file->move($folder, $fileName);
        } catch (FileException $e) {
            // simplification pour l'exemple
            return null;
        }

        return $fileName;
    }
}

la méthode upload se charge de renommer et de placer le fichier envoyé au bon endroit.
Ensuite je l'appelle dans mon controleur, en adaptant le tien, ça donnerait qqchose comme ça :


public function __invoke(Request $request, FileUploadService $uploader)
    {
        $uploadedFile = $uploader->upload($request->files->get('file'), 'monDossier');
        if(!$uploadedFile) {
                throw new BadRequestHttpException('Aucune photo importée');
        }

        $livre = new Livre();
        $livre->setFile($uploadedFile);

        // suite du code
        ...

    }
<?php

namespace App\Service;

use Symfony\Component\String\Slugger\SluggerInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\File\Exception\FileException;

class FileUploadService
{

    private $uniqid;

    public function __construct(private SluggerInterface $slugger)
    {
        $this->uniqid = uniqid();
    }

    public function upload(UploadedFile $file, string $folder)
    {
        $originalFilename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
        $safeFilename = $this->slugger->slug($originalFilename);
        $fileName = $safeFilename . '-' . $this->uniquid . '.' . $file->guessExtension();

        // vérification existence du dossier de destination
        // etc.

        try {
            $file->move($folder, $fileName);
        } catch (FileException $e) {
            // simplification pour l'exemple
            return null;
        }

        return $fileName;
    }
}