Search code examples
formssymfony1validationsymfony-1.4

Change dynamically validated_file_class in symfony 1.4


I have this model:

Banner:
 columns:
  filename: string(255)
  url: string(255)
  position:
   type: enum
   values: [top, right]
   default: right

and this form:

class BannerForm extends BaseBannerForm
{
  public function configure()
  {
    $this->widgetSchema['filename'] = new sfWidgetFormInputFileEditable(array(
      'file_src' => $this->getObject()->getThumbURL(),
      'is_image' => true,
      'edit_mode' => $this->getObject()->exists()
    ));
    $validated_file_class = $this->getObject()->position === 'right' ? 'bannerRightValidatedFile' : 'bannerTopValidatedFile';
    $this->validatorSchema['filename'] = new sfValidatorFile(array(
      'path' => sfConfig::get('sf_upload_dir'),
      'mime_types' => 'web_images',
      'validated_file_class' => $validated_file_class',
      'required' => $this->getObject()->isNew()
    ));
  }
}

I use different validate classes because inside it i incapsulate thumbnail operations, and the sizes of banners depends on it position field. The problem is that $validated_file_class is always bannerRightValidatedFile class. How i can achieve this thing ?


Solution

  • I can suggest 4 solutions which you can choose from:

    Option 1:

    You should add a update$fieldNameColumn method to the form class. In your case it should look like this:

    // change validated file instance before calling save
    protected function updateFilenameColumn($value)
    {
      if ($value instanceof sfValidatedFile)
      {
        $class = 'right' == $this->getValue('position') ? 'bannerRightValidatedFile' : 'bannerTopValidatedFile';
        // this will not work as I thought at first time
        // $this->getValidator('filename')->setOption('validated_file_class', $class);
    
        $this->values['filename'] = new $class(
          $value->getOriginalName(),
          $value->getType(),
          $value->getTempName(),
          $value->getSize(),
          $value->getPath()
        );
    
        return $this->processUploadedFile('filename');
      }
    
      return $value;
    }
    

    I think it's kind of hacky.

    Option 2:

    You should add a doctrine hook method to the model:

    /**
     * @param Doctrine_Event $event
     */
    public function postSave($event)
    {
      $record = $event->getInvoker();
    
      if (array_key_exists('filename', $record->getLastModified()))
      {
        // get the full path to the file
        $file = sfConfig::get('sf_upload_dir') . '/' . $record->getFilename();
    
        if (file_exists($file))
        {
          // resize the file e.g. with sfImageTransformPlugin
          $img = new sfImage($file);
          $img
            ->resize(100, 100)
            ->save();
        }
      }
    }
    

    This will work when creating records whitout a form e.g. when using fixtures.

    Option 3:

    Use the admin.save_object event.

    public static function listenToAdminSaveObject(sfEvent $event)
    {
      $record = $event['object'];
    
      if ($event['object'] instanceof Banner)
      {
        // use the same code as in the `postSave` example
      }
    }
    

    Option 4:

    Use the sfImageTransformExtraPlugin

    It's kind of hard to setup and configure (and it's code is a mess :), but it makes possible to modify the size of the image whithout regenerating all the already resized ones.