Задача:
Стандартная форма с загрузкой файла. Нужно избавиться от стандартного хеширования имени файла при загрузке и сохранять оригинальное имя файла. Кроме того, во избежание граблей с кириллицей, надо транслитерировать имя файла и заменить пробелы на подчерки (данный сниппет актуален для 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.
Все, загружаем файл, наслаждаемся результатом ))
6 Comments
Спасибо, помогло. =)
Рад был помочь )
А если надо файл сохранять в другой директорий, который находится уровнем выше чем /web/ папка для этого домена, где копирование файла надо встроить? Т.е. сайт на домене mydomain.com тут симфони работает, а картинки, что загружаюстя с админки должны ложиться сюда: imgs.mydomain.com – и тут тоже грамотно расскадываться по папкам…
Мы для этого делали отдельный подпроектик filestorage ) А в symfony можно глянуть sfValidatedFile::save() – там вся эта логика реализуется (как раз от него и отнаследуетесь). По поводу “выше” docroot – имхо не важно, лишь бы у вебсервера был бы туда доступ. Мы в тот же filestorage делали 2 клиента – local (который как раз действовал как вы описали) и remote (API + PHP скрипт для этого).
Спасибо, за ответ. Получилось, изменить путь и имя на нужное. Но вот такой вопросик. Мне нужно сгенерить имя, в зависимоти от строки БД, для которой грузится эта картинка. Как из валидатора добраться до объекта (BaseFormDoctrine) или формы, для которой валидатор вызван? В нем я уже могу получить запись через $this->getObject() к которой относится картинка… Или как в валидатор передать нужные мне данные?
Посмотрите sfValidateFile::doClean() в самом конце:
Если отнаследоваться от этого валидатора то можно модифицировать его так чтобы передавать ему дополнительную опцию (например $form->getObject() или сразу ID сущности, если она там определена уже). В общем посмотрите в этом направлении.