Symfony forms. Тюнинг формы загрузки файла: заменяем хэширование имени файла на транслитерацию


Задача:

Стандартная форма с загрузкой файла. Нужно избавиться от стандартного хеширования имени файла при загрузке и сохранять оригинальное имя файла. Кроме того, во избежание граблей с кириллицей, надо транслитерировать имя файла и заменить пробелы на подчерки (данный сниппет актуален для ORM Doctrine).

Решение:

Положим у нас есть модель EntityWithFile, у которой есть поле filename, в котором ожидается имя файла.

Модифицируем класс формы

Поскольку есть модель, то ей соответствует форма: EntityWithFileForm. Модифицируем класс формы следующим образом:

// lib/form/doctrine/EntityWithFileForm.class.php
class EntityWithFileForm extends BaseEntityWithFileForm
{
  public function configure()
  {
    // ...
    $this->widgetSchema['filename'] = new sfWidgetFormInputFile( array(), array() );
    $this->validatorSchema['filename'] = new sfValidatorFile(
      array(
        'path' => sfConfig::get('sf_upload_dir') . sfConfig::get('app_files_dir'),
        'validated_file_class'  => 'myFileValidated',
      ),
      array(
        'required' => 'укажите файл для загрузки',
      )
    );
  }
}

Обратите внимание на настройки валидатора: path и validated_file_class.
Первая опция задает путь, куда будет загружен файл. В данном случае этот путь складывается из двух настроек: sf_upload_dir и app_files_dir. Первая – системная, вторую надо добавить в app.yml:

# apps/frontend/config/app.yml
all:
  files_dir: /files

Таким образом целевая директория от DOCUMENT ROOT будет следующая: /uploads/files.

Класс транслитерации

Не мудрствуя лукаво, класс транслитерации сделаем таким:

// apps/frontend/lib/myTranslit.class.php
class myTranslit
{
  /**
   * Таблица перекодировки
   * @var <array>
   */
  static $transliteration_table = array(
    'а'=>'a', 'б'=>'b', 'в'=>'v', 'г'=>'g', 'д'=>'d', 'е'=>'e', 'ж'=>'g', 'з'=>'z',
    'и'=>'i', 'й'=>'y', 'к'=>'k', 'л'=>'l', 'м'=>'m', 'н'=>'n', 'о'=>'o', 'п'=>'p',
    'р'=>'r', 'с'=>'s', 'т'=>'t', 'у'=>'u', 'ф'=>'f', 'ы'=>'i', 'э'=>'e', 'А'=>'A',
    'Б'=>'B', 'В'=>'V', 'Г'=>'G', 'Д'=>'D', 'Е'=>'E', 'Ж'=>'G', 'З'=>'Z', 'И'=>'I',
    'Й'=>'Y', 'К'=>'K', 'Л'=>'L', 'М'=>'M', 'Н'=>'N', 'О'=>'O', 'П'=>'P', 'Р'=>'R',
    'С'=>'S', 'Т'=>'T', 'У'=>'U', 'Ф'=>'F', 'Ы'=>'I', 'Э'=>'E', 'ё'=>"yo", 'х'=>"h",
    'ц'=>"ts", 'ч'=>"ch", 'ш'=>"sh", 'щ'=>"shch", 'ъ'=>"", 'ь'=>"", 'ю'=>"yu", 'я'=>"ya",
    'Ё'=>"YO", 'Х'=>"H", 'Ц'=>"TS", 'Ч'=>"CH", 'Ш'=>"SH", 'Щ'=>"SHCH", 'Ъ'=>"", 'Ь'=>"",
    'Ю'=>"YU", 'Я'=>"YA", " "=>"_",
  );

  /**
   * Транслитерация строки
   * @param <string> $string
   * @return <string>
   */
  public static function transliterate( $string )
  {
    return strtr( $string, self::$transliteration_table );
  }
}

С этим классом я думаю все и так предельно ясно.

Класс пост-валидации

Собственно последний штрих – класс для пост-валидации формы. Он создается файл-валидатором после успешной валидации. В обязанности этого класса входит перемещение загруженного файла и, соответственно, его именование:

// apps/frontend/lib/validator/myFileValidated.class.php
class myFileValidated extends sfValidatedFile
{
  /**
   * Генерация имени файла
   * @return <type>
   */
  public function generateFilename()
  {
    return myTranslit::transliterate( $this->getOriginalName() );
  }
}

Здесь фактически нужно отнаследоваться от sfValidatedFile и переопределить требуемым нам образом метод generateFilename.

Все, загружаем файл, наслаждаемся результатом ))

This entry was posted in Профессиональное and tagged , , , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

6 Comments

  1. Albatros
    Posted 2011/01/24 at 9:40 pm | Permalink

    Спасибо, помогло. =)

    • hudson
      Posted 2011/01/24 at 11:28 pm | Permalink

      Рад был помочь )

  2. Alex
    Posted 2011/04/12 at 1:45 pm | Permalink

    А если надо файл сохранять в другой директорий, который находится уровнем выше чем /web/ папка для этого домена, где копирование файла надо встроить? Т.е. сайт на домене mydomain.com тут симфони работает, а картинки, что загружаюстя с админки должны ложиться сюда: imgs.mydomain.com – и тут тоже грамотно расскадываться по папкам…

    • Posted 2011/04/12 at 10:19 pm | Permalink

      Мы для этого делали отдельный подпроектик filestorage ) А в symfony можно глянуть sfValidatedFile::save() – там вся эта логика реализуется (как раз от него и отнаследуетесь). По поводу “выше” docroot – имхо не важно, лишь бы у вебсервера был бы туда доступ. Мы в тот же filestorage делали 2 клиента – local (который как раз действовал как вы описали) и remote (API + PHP скрипт для этого).

      • Alex
        Posted 2011/04/13 at 11:01 am | Permalink

        Спасибо, за ответ. Получилось, изменить путь и имя на нужное. Но вот такой вопросик. Мне нужно сгенерить имя, в зависимоти от строки БД, для которой грузится эта картинка. Как из валидатора добраться до объекта (BaseFormDoctrine) или формы, для которой валидатор вызван? В нем я уже могу получить запись через $this->getObject() к которой относится картинка… Или как в валидатор передать нужные мне данные?

        • Posted 2011/04/13 at 1:32 pm | Permalink

          Посмотрите sfValidateFile::doClean() в самом конце:

          // lib/vendor/symfony/lib/validator/sfValidatorFile.class.php
          protected function doClean($value){
            // ...
            $class = $this->getOption('validated_file_class');
            return new $class($value['name'], $mimeType, $value['tmp_name'], $value['size'], $this->getOption('path'));
          }

          Если отнаследоваться от этого валидатора то можно модифицировать его так чтобы передавать ему дополнительную опцию (например $form->getObject() или сразу ID сущности, если она там определена уже). В общем посмотрите в этом направлении.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">