Что мы будем делать
У пользователя есть фотография профиля по умолчанию.
Пользователь нажимает на фотографию своего профиля, и у него появляется возможность выбрать новую фотографию ( при этом он может нажать кнопку отмены, и ничего не произойдет).
Если пользователь выбирает изображение, то изображение его профиля обновляется сразу же (нет необходимости перезагружать страницу, изменения сохраняются).
Как мы будем это делать
У нас есть много способов это реализовать.
В данном случае мы сделаем это следующим образом:
- Мы добавим форму (но с помощью стилей сделаем так, чтобы она оставалась скрытой). Эта форма будет содержать поле типа file
- С помощью Javascript мы привяжем событие клика к изображению профиля. Когда это событие будет идентифицировано, мы запустим click на вводе файла.
- Мы будем слушать событие изменения входных данных, так что когда файл будет выбран, мы сделаем AJAX-запрос для изменения изображения профиля.
- AJAX-запрос вернет сообщение об успехе или неудаче. Если ответ был успешным, то мы обновим изображение профиля с помощью Javascript (чтобы не обновлять страницу).
Погнали
Форма будет скрыта с помощью свойства CSS под названием display (со значением none).
В данном случае я размещаю ее непосредственно перед изображением.
<form action="{{ url('profile/foto') }}" method="post" style="display: none" id="avatarForm">
{{ csrf_field() }}
<input type="file" id="avatarInput" name="photo">
</form>
<img src="{{ auth()->user()->getAvatarUrl() }}" id="avatarImage">
Code language: HTML, XML (xml)
Обратите внимание:
- Форма, а также изображение и ввод, имеют свой id (это важно для доступа к этим элементам через Javascript).
- Входной тип файла имеет атрибут name со значением photo (это значение должно соответствовать тому, которое указано в нашем контроллере, чтобы правильно получить загруженный файл из бэкенда).
- Я упростил код, удалив атрибуты class, alt и title из моего изображения (вы можете использовать эти атрибуты по своему усмотрению в своих проектах).
Что на счет Javascript?
Как я уже говорил, нам нужно зарегистрировать 2 события. А также получить данные об элементах.
$(function () {
var $avatarImage, $avatarInput, $avatarForm;
$avatarImage = $('#avatarImage');
$avatarInput = $('#avatarInput');
$avatarForm = $('#avatarForm');
$avatarImage.on('click', function () {
$avatarInput.click();
});
$avatarInput.on('change', function () {
alert('change');
});
});
Code language: PHP (php)
Как вы могли заметить, в событии изменения input я поместил только одно оповещение.
На этом этапе вы должны убедиться, что получили это сообщение, после нажатия на изображение и выбора файла.
Если все в порядке, то вы можете заменить оповещение для выполнения Ajax-запроса.
Таким образом, мы получим следующее:
$avatarInput.on('change', function () {
var formData = new FormData();
formData.append('photo', $avatarInput[0].files[0]);
$.ajax({
url: $avatarForm.attr('action') + '?' + $avatarForm.serialize(),
method: $avatarForm.attr('method'),
data: formData,
processData: false,
contentType: false
}).done(function (data) {
if (data.success)
$avatarImage.attr('src', data.path);
}).fail(function () {
alert('Неправильный формат изображения.');
});
});
Code language: PHP (php)
Здесь мы должны помнить, что:
- Мы получаем url, к которому будет сделан запрос, и method из значений, определенных в форме.
- Мы используем объект FormData для загрузки изображения через Ajax (поскольку метод serialize в данном случае передает только токен csrf).
- Ответ, который мы получаем от Ajax-запроса, состоит из объекта, который мы называем data и который имеет 2 атрибута (success, чтобы указать, была ли операция успешной, и path с путем к изображению профиля).
Нам нужно зарегистрировать только путь profile/photo (который мы использовали в action формы). При желании вы можете использовать другой путь.
Этот маршрут должен быть объявлен в файле Laravel routes.
Route::post('/profile/foto', 'ProfileController@updatePhoto');
Code language: PHP (php)
В этом случае маршрут определяется через контроллер под названием ProfileController. В частности, через метод updatePhoto.
Таким образом, в этом контроллере у нас будет следующее:
public function updatePhoto(Request $request)
{
$this->validate($request, [
'photo' => 'required|image'
]);
$file = $request->file('photo');
$extension = $file->getClientOriginalExtension();
$fileName = auth()->id() . '.' . $extension;
$path = public_path('images/users/'.$fileName);
Image::make($file)->fit(144, 144)->save($path);
$user = auth()->user();
$user->photo_extension = $extension;
$saved = $user->save();
$data['success'] = $saved;
$data['path'] = $user->getAvatarUrl() . '?' . uniqid();
return $data;
}
Code language: PHP (php)
Данный метод:
- Проверяет, что поле photo, отправленное в запросе, является изображением (а также является обязательным полем).
- Получает название изображения. Таким образом идентификатор пользователя, после которого следует расширение, будет именем файла для сохранения.
- Он использует интерфейс Image facade, поэтому если изображение больше 144×144 пикселей, то его размер изменяется в соответствии с этим ограничением.
- Хранит в таблице пользователей в столбце photo_extension имя загруженного изображения.
- Возвращает ответ в формате JSON, указывающий, была ли операция успешной, а также отправляет обратно URL изображения.
Чтобы приведенный выше код работал правильно, необходимо, чтобы в таблице пользователей был столбец photo_extension.
$table->string('photo_extension')->nullable();
Code language: PHP (php)
Вам также необходимо определить метод getAvatarUrl в модели User.
public function getAvatarUrl()
{
if ($this->photo_extension)
return asset('images/users/'.$this->id.'.'.$this->photo_extension);
return asset('images/users/default.jpg');
}
Code language: PHP (php)
Этот метод возвращает абсолютный URL-адрес изображения профиля пользователя. А в случае, если его нет, абсолютный URL-адрес изображения по умолчанию.
Если вы еще не установили пакет Intervention/Image (необходимый для работы изменения размера изображения), вы можете установить его, просто выполнив команду:
composer require intervention/image
Code language: JavaScript (javascript)
Кстати. Если вы достаточно внимательны, то могли заметить использование uniqid() в методе updatePhoto. Этот метод используется для добавления уникального номера в конец имени файла, чтобы пользователь всегда видел свою новую фотографию профиля (поскольку ранее он мог загрузить изображение с тем же названием, которое сохранилось в кэше браузера).