Symfony: Personnaliser le nom du fichier lors d’un upload avec sfWidgetFormInputFileEditable
On emploie tous je pense, assez couramment maintenant, le widget sfWidgetFormInputFileEditable qui permet de rajouter quelques fonctionnalités à un widget d’upload classique, en l’occurrence visualisation et suppression. Le souci que j’ai rencontré récemment, c’est par contre la personnalisation du nom du fichier ainsi généré.
En effet, par défaut celui-ci est une empreinte sha1 généré aléatoirement, ce qui convient pour la majeure partie des cas, mais parfois ne suffit pas. Mais là encore Symfony étonne par le mécanisme mis en place pour contourner cette problématique, encore faut-il le savoir malheureusement.
Nous allons prendre un cas classique, une class Medias avec un champ path qui stockera le nom du fichier ainsi uploadé:
class MediasForm extends BaseMediasForm { public function configure() { $this->widgetSchema['path'] = new sfWidgetFormInputFileEditable(array( 'file_src' => '/uploads/medias/'.$this->getObject()->getPath(), 'is_image' => true, 'edit_mode' => !$this->isNew(), 'with_delete' => true, )); $this->validatorSchema['path'] = new sfValidatorFile(array( 'required' => false, 'path' => sfConfig::get('sf_upload_dir').'/medias', 'mime_types' => 'web_images' )); $this->validatorSchema['path_delete'] = new sfValidatorBoolean(); }
Maintenant, si on veut avoir la main sur le nom du fichier généré, il suffit tout simplement dans la class Medias de rajouter la méthode suivante:
// Ou Path est le nom du champ ciblé lors de l'upload public function generatePathFilename(sfValidatedFile $file) { // On a maintenant accès à notre fichier, on peut donc lui donner un nom basé sur son id ou son slug ou tout autre chose. return $this->getId().$file->getExtension($file->getOriginalExtension()); }
Tout simplement! C’est ce genre de détails qui me font apprécier la constante découverte de ce framework ;)
15 Réponses
Laisser un message

Il y a juste un problème c’est qu’au moment de la création, l’objet est instancié mais la méthode « generatePathFilename » est appelée avant les setters. Ce qui signifie que la méthode « getId() » (ou n’importe quel getter propre à l’objet) ne retourne rien.
Si quelqu’un connaît une solution à ce problème, je suis preneur.
Il suffit de récupérer directement l’id par exemple par sa propriété j’imagine, genre
Je testerai ça demain
Cela revient au même, les attributs de l’objet n’ont pas encore de valeur à cette étape de la création.
Après test, effectivement mon exemple était pas vraiment bien trouvé.
Comme je m’en sers dans un sous formulaire, mon objet Media à quand même accès à la relation et je récupère une propriété de cette relation pour la création de mon nom.
De ce que j’ai vu, comme le generatePathFilename est appelé au moment du processValues du form, je vois pas vraiment comment contourner ce problème.
Je ferais des corrections ce week end. Merci
Faut étendre la fonction doSave() dans le *Form.class.php pour avoir accès à l’objet.
public function doSave($con = null)
{
$variable = $this->getValue(‘variable’);
$this->autreVariable = $variable->generatePathFilename($this); // pas sûr du $this
$variable->save($pathToSave().$this->autreVariable);
// suite du traitement
parent::doSave($con);
// ou return parent::doSave();
}
Ne pas oublier aussi d’étendre la fonction updateObject() pour la repopulation de l’objet.
public function updateObject($values = null)
{
$object = parent::updateObject($values);
$object->setVariable($this->autreVariable);
return $object;
}
Bien sûr, on déclare la variable $autreVariable :
private $autreVariable;
J’utilise ce moyen pour avoir accès au fichier uploader et changer le nom.
Si cela peut aider. :) ++
Aussi, j’utilise la fonction generateFilename() et non generatePathFilename().
++
Le principe était justement de pas avoir à surcharger les méthode save et update
Mais merci de ta participation!
A quel endroit dans le code peut-on voir que ton « generatePathFilename(sfValidatedFile $file) » est appelé ?
Je pensais que ça venait de « sfValidatedFile » qui renvoyé « $this » si la fonction existe sur l’objet, mais je n’ai pas trouvé…
En fait la méthode est déterminé dans le saveFile de sfFormDoctrine (ou sfFormPropel). C’est d’ailleurs la raison pourquoi l’object n’est pas encore hydraté lors de l’appel.
Merci pour cette astuce qui marche impecc. Juste peut-être préciser pour les boulets dans mon genre (:D) que la méthode generate%Filename est a rajouter dans la class de base (Media ici) et non pas dans la class MediaForm. Également, le nom du champ est « camelizé ». Dans mon cas, pour le champ cpn_logo_url, ca donnait generateCpnLogoUrlFilename().
Pour la première remarque, je l’ai bien précisé :p Je vais le mettre en gras du coup ;)
Bon point pour le 2e!
Merci pour ces précisions
Est-ce qu’il y a un moyen pour que la fonction « generatePathFilename() » soit appelée à chaque modification du formulaire ?
Si par exemple on utilise ta méthode et qu’on avait en fait accès au label.
Déjà il faut que le label soit unique dans la base pour ne pas avoir de conflit avec les noms de fichier.
Je créé donc mon Media1 avec son fichier, j’aurais donc « upload/media/Media1.png ».
Maintenant, imaginons que j’edite Media1 en Media2, le nom du fichier ne changera pas..
Pour finir je créé un nouveau Media1 (disponible vu que l’ancien s’appele maintenant Media2), dans ce cas mon fichier « upload/media/Media1.png », lié à Media2, sera écrasé par un nouveau « Media1.png » qui lui fait référence au nouveau Media1.
Au final j’ai donc un gros problème car Media1 et Media2 renvoie au même fichier ce qui ne devrait pas être le cas.
J’aurais la solution de changer le nom du fichier dans le repertoire si le label est modifié mais c’est pas terrible je trouve…
Si quelqu’un a une idée…
En fait, la méthode n’est appelée que si tu upload un nouveau fichier.
Sinon, il faudra passer par qqch comme l’a suggéré drewprod plus haut en surchargant le doSave de ton formulaire.
En fait j’ai réussi à le faire sans surcharger doSave et updateObject, mais j’ai un problème qui persiste, le fait de changer le nom du fichier si le label (ou autre variable utilisé pour définir le nom du fichier) change…
Pour ça je créé une fonction static (un slugify améliorer en fait), je créé la fonction « generatePathFilename » dans l’objet et je surcharge le processForm en y ajoutant l’appel à ma fonction static.
Ca me donne un résultat plutôt propre, mais le problème du changement de label se pose…
De plus je me suis rendu compte que j’avais un problème avec une partie du slugify de Jobeet (iconv), si quelqu’un a une idée :
http://forum.symfony-project.org/index.php/m/83282/#msg_83282
Je vais continuer à chercher pour l’update… :)
Top! :) C’etait exactement ce que je cherchais, alors Merci!!!