Заметка по VichUploaderBundle

Долго мы мудохались (по другому не скажу) с парой-тройкой багов, которые полезли в результате использования VichUploaderBundle. И главное мы к ним возвращались по 2-3 раза на разных проектах за последние полгода :).

Symfony’s form component cleanup

Наименее костыльное решение предложили здесь. Чтобы постоянно не прописывать data_class в формах я добавил его в DefaultOptions для FileType и отправил PR в Symfony2. Чуть позже доделаем тесты и думаю PR примут.

The file is not updated if there are not other changes in the entity

Это хитрый баг, который возникает когда в форме меняется только файл. Словили его когда решили обновить логотип для спонсора на http://fwdays.com. Сегодня я выделил время на дебаг и понял, что нормальным путем проблему не решить. Смотрите сами:

...
 
// биндим данные на форму
// - запускаем дата трансформеры
// - дергаем сеттеры сущности (setFile и др.)
$form->bind($request);
 
// валидируем форму
// в $file должен быть объект загружаемого файла (UploadableField), чтобы пройти валидацию
if ($form->isValid()) {
    $em = $this->getDoctrine()->getManager();
 
    // готовим сущность к сохранению в БД
    // вычисляется хеш объекта и определяется его состояние (новый, обновленный и т.д.)
    // здесь дергается хук preUpdate (обновление) или prePersist (новая сущность) и файл физически перемещается в место назначения, но 
    // !! если нужно просто обновить файл, то проблема т.к. имя файла (которое хранится в БД) ещё не изменилось, а preUpdate запускается только если в сущности есть изменения !!
    $em->persist($entity);
 
    // сохранение данных
    $em->flush();
 
...

Получается, что нужно зааплоадить новый файл и засетить его имя в сущность до того как отправить сущность в persist() менеджера сущностей. Иначе Doctrine не видит изменений в сущности, не дергает хук preUpdate и аплоад средствами VichUploaderBundle не происходит :(.

Спасает костыль от автора бандла, который предлагает делать в сущности поле updatetAt и обновлять его если в сеттер файла приходит что-то новенькое:

public function setFile($file)
{
    $this->file = $file;
 
    if ($file instanceof UploadedFile) {
        $this->setUpdatedAt(new \DateTime());
    }
}

Как-то так :)