понедельник, 23 ноября 2020 г.

Загрузка файла при обновлении записи

Иногда на стеке встречается вопрос от новичков - как заменить файл, принадлежащий некоторой сущности. Допустим, в программе есть сущность Книга, и у нее есть файл обложки. Когда книга редактируется - файл обложки может быть изменен.

Многие начинающие разработчики будут полагать, что путь к текущему файлу обложки надо выводить на форму редактирования, затем, аналогично полям <input type="text" />, добавлять его значение в запрос\метод\функцию обновления. По их мнению получается, что если файл не был обновлен, то в БД запишется то же самое значение пути к файлу, что есть в БД сейчас. Выглядит это в их голове как-то так:

if (isset($_POST['update-btn'])) {
    $name = $_POST['name'];

    // вроде как тут должен быть путь к файлу,
    // но <input type="file" /> работает не так
    $file = $_POST['file'];

    $query = "UPDATE tbl_book SET name = '$name', filepath = '$file' WHERE id = 42";
    $dbh->execute($query);
}

Как следствие, у них возникает вопрос - как вывести текущий путь к файлу в <input type="file" />? Ответ прост - никак. Нужно понять две вещи: первое - никакое значение от сервера в <input type="file" /> предустановить нельзя. Второе - работать с заменой файла следует иначе, чем с заменой текстовых значений.

Поэтому правильный алгоритм обновления файла выглядит так:

1. На форме обновления рядом с полем загрузки файла (<input type="file" />) выводим текущий файл, например: Текущая обложка <img src="path/to/file.jpg" />. Так как в общем случае никому не интересно, как файл назван в системе, то такого вывода достаточно: пользователь видит содержимое файла (собственно картинку) и может решить - следует ли загрузить новый файл. Если же действительно надо показать текущий путь к файлу, то выводим, например, Текущий файл расположен по пути "path/to/file.jpg". В обоих случаях path/to/file.jpg - это путь к файлу, хранящийся в БД.

В поле загрузки файла пользователь может загрузить новый файл, а может не загрузить. Поэтому дальше идут два варианта развития событий.

2.1. Пользователь не загрузил файл. Значит, в запросе\методе\функции обновления записи не требуется указывать поле с файлом.

2.2. Пользователь загрузил новый файл. Значит, надо этот файл сохранить в каком-то каталоге и сформировать путь к нему. Далее, в запросе\методе\функции обновления записи указать путь к новому файлу, и после успешного обновления записи удалить с диска старый файл. Старый файл можно и не удалять, если на диске достаточно места.

Схематично код выглядит так, его аналоги можно реализовать в любом фреймворке:

if (isset($_POST['update-btn'])) {
    $name = $_POST['name'];

    if ($_FILES['file']) {
        move_uploaded_file();
        $file = 'path/to/file.jpg';

        // опционально, за этим значением нужно сходить в БД
        $oldFile = 'path/to/old_file.jpg';
    }

    if (isset($file)) {
        $query = "UPDATE tbl_book SET name = '$name', filepath = '$file' WHERE id = 42";
    } else {
        $query = "UPDATE tbl_book SET name = '$name' WHERE id = 42";
    }
    
    $dbh->execute($query);
    
    // опционально
    unlink($oldFile);
}

Это всё, что требуется сделать в случае замены файла в сущности. Никаких костылей изобретать не требуется.

Комментариев нет:

Отправка комментария