views:

1788

answers:

3

I am using Symfony 1.4.

Users from the backend application can upload files and publish them to the frontend. Using sfWidgetFormInputFile and sfValidatorFile, I would like to keep the original filename instead of the default functionality of a random string (i.e. Meaningful_filename.docx instead of a4b25e9f48cfb6268f34b691fc18cd76fefe96b5.docx - numbers can be appended onto duplicate names). This can be useful in scenarios where the user downloads several files and would not be able to tell them apart from the file name.

$this->widgetSchema['file_name'] = new sfWidgetFormInputFile(array('label' => 'File'));

$this->validatorSchema['file_name'] = new sfValidatorFile(array(
  'required'   => true,
  'path'       =>     sfConfig::get('sf_upload_dir').DIRECTORY_SEPARATOR.sfConfig::get('app_dir_file_sharing').DIRECTORY_SEPARATOR,
'mime_types' => array('application/msword',
                    'application/vnd.ms-word',
                    'application/msword',
                    'application/msword; charset=binary')
), array(
    'invalid'    => 'Invalid file.',
    'required'   => 'Select a file to upload.',
    'mime_types' => 'The file must be a supported type.'
));

Is there native functionality in the sfWidgetFormInputFile widget or is there another solution to this?

+6  A: 

You get the file by calling $form["file_name"]->getValue(). This gives you an object of class sfValidatedFile where you can call the method getOriginalName().


To define how the file should be save you can do this: The sfValidatorFile class accepts an option which sfValidatedFile class to use:

validated_file_class: Name of the class that manages the cleaned uploaded file (optional)

The sfValidatedFile class has a method save that calls a method generateFileName. Subclass this class and overwrite this method:

class CustomValidatedFile {
    /**
      * Generates a random filename for the current file.
      *
      * @return string A random name to represent the current file
      */
    public function generateFilename()
    {
        return 'foo bar'// your custom generated file name;
    }
}

Here is the function from the original class:

public function generateFilename()
{
    return sha1($this->getOriginalName().rand(11111, 99999)).$this->getExtension($this->getOriginalExtension());
}

Then you set up the validator this way:

$this->validatorSchema['file_name'] = new sfValidatorFile(array(
      'required'   => true,
      'path' =>   'yourpath',
      'validated_file_class' => 'CustomValidatedFile',
      'mime_types' => array('application/msword',
                            'application/vnd.ms-word',
                            'application/msword',
                            'application/msword; charset=binary')
     ), 
     array('invalid'    => 'Invalid file.',
           'required'   => 'Select a file to upload.',
           'mime_types' => 'The file must be a supported type.')
);

Hope that helps!

Felix Kling
A: 

According to the Symfony documentation "The sfValidatorFile validator validates an uploaded file. The validator converts the uploaded file to an instance of the sfValidatedFile class, or of the validated_file_class option if it is set." (Source: http://www.symfony-project.org/forms/1_4/en/B-Validators#chapter_b_sub_sfvalidatorfile)

Although the sfValidatedFile class renames files right out of the box, you can override this function by setting the validated_file_class to a custom class, and extending sfValidatedFile.

In your custom validated file class, pass your custom filename to the save() method. "If you don't pass a file name, it will be generated by the generateFilename method." (Source: http://www.symfony-project.org/api/1_4/sfValidatedFile#method_save)

Here's one way you could do it (Source: http://forum.symfony-project.org/index.php/m/90887/#msg_90887)...

A custom validated file class:

// lib/validator/myValidatedFile.php
class myValidatedFile extends sfValidatedFile {
  private $savedFilename = null;

  // Override sfValidatedFile's save method
  public function save($file = null, $fileMode = 0666, $create = true, $dirMode = 0777) {
    // This makes sure we use only one savedFilename (it will be the first)
    if ($this->savedFilename === null) $this->savedFilename = $file;

    // Let the original save method do its magic :)
    return parent::save($this->savedFilename, $fileMode, $create, $dirMode);
  }
}

Make sure to set 'validated_file_class' => 'myValidatedFile' for the sfWidgetFormInputFile. And to set the logic for what the file name is going to be in Form's save method.

Adam V.
+1  A: 

After some research:

While you can extend sfValidatedFile and override generateFilename I found out that sfFormPropel checks for the existence of a method based on the column name for the model to name the file.

From symfony/plugins/sfPropelPlugin/lib/form line 292:

$method = sprintf('generate%sFilename', $column);
if (null !== $filename)
{
  return $file->save($filename);
}
else if (method_exists($this, $method))
{
  return $file->save($this->$method($file));
}

Therefore, if your column is called file_name, the method looks for the existence of generateFileNameFilename in the form class. This way you only have to add one method to your form class, rather than extending the sfValidatedFile widget. For instance, my function uses the original name if it is not taken, otherwise appends a sequential number (one method is to recursively check the generated filename):

public function generateFileNameFilename($file = null)
{
  if (null === $file) {
    // use a random filename instead
    return null;
  }

  if (file_exists($file->getpath().$file->getOriginalName())) {
     return $this->appendToName($file);
  }

  return $file->getOriginalName();
}

public function appendToName($file, $index = 0)
{
    $newname = pathinfo($file->getOriginalName(), PATHINFO_FILENAME).$index.$file->getExtension();

    if (file_exists($file->getpath().$newname)) {
       return $this->appendToName($file, ++$index);
    } else {
       return $newname;
    }
 }

I can't find this documented in the symfony API anywhere which is why it took some searching the code base to find. If you are using this method in many places, extending sfValidatedFile might be a good option too.

markb