Форма связи с аттачем
Отправку почты рассмотрим на примере формы обратной связи с полями Имя
, Почта
, Сообщение
.
- Создаем контроллер, который будет показывать фому и обрабатывать POST-запрос от клиента
- Добавляем два роута
- Создаем форму
- Создаем шаблон письма
- Добавляем класс
FeedbackMailer
, расширяющийIlluminate\Mail\Mailable
- Настраиваем Laravel
Контроллер
Создаем контроллер FeedbackController
:
php artisan make:controller FeedbackController
app/Http/FeedbackController.php<?php
namespace App\Http\Controllers;
use stdClass;
use App\Mail\FeedbackMailer;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
class FeedbackController extends Controller {
// Выводим форму на странице
public function index() {
return view('feedback.index');
}
// Отправляем данные формы на почту
public function send(Request $request) {
// Валидация полей формы
$request->validate([
'name' => 'required|max:100',
'email' => 'required|email|max:100',
'message' => 'required|max:500',
'image' => 'mimes:jpeg,jpg,png|max:5000',
]);
// Переменная с файлом
$image = $request->file('image');
// Проверка загрузки файла
if ($image) {
// Название файла
$raw = $image->get();
// Получаем расширение файла
$ext = $image->extension();
}
// Создаем динамический объект
$data = new stdClass();
// Данные из поля формы name
$data->name = $request->name;
// Данные из поля формы email
$data->email = $request->email;
// Данные из поля формы message
$data->message = $request->message;
// Тернарная проверка вложения на null
$data->image = $raw ?? null;
$data->ext = $ext ?? null;
// В метод Mail::to первым аргументом передаем почту куда будет отправка письма, вторым методом передаем класс отправки почты в который передаем аргументом объект с данными формы $data который попадет в конструктор класса FeedbackMailer
Mail::to($data->email)->send(new FeedbackMailer($data));
// Редирект через имя роута на ту же страницу с формой
return redirect()->route('feedback.index')
// Вывод сообщения
->with('success', 'Ваше сообщение успешно отправлено');
}
}
Роуты
Добавляем два маршрута:
routes/web.phpRoute::get('/feedback', [FeedbackController::class, 'index'])->name('feedback.index');
Route::post('/feedback', [FeedbackController::class, 'send'])->name('feedback.send');
Форма связи
Создаем шаблон:
resources/views/feedback/index.blade.php@extends('layouts.app')
@section('content')
<h1>Обратная связь</h1>
@if (session('success'))
<div class="alert alert-success" role="alert">
{{ session('success') }}
</div>
@endif
@if ($errors->any())
<div class="alert alert-danger" role="alert">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form method="post" action="{{ route('feedback.send') }}" enctype="multipart/form-data">
@csrf
<div class="form-group">
<input type="text" class="form-control" name="name" placeholder="Имя, фамилия"
required maxlength="100" value="{{ old('name') ?? '' }}">
</div>
<div class="form-group">
<input type="email" class="form-control" name="email" placeholder="Адрес почты"
required maxlength="100" value="{{ old('email') ?? '' }}">
</div>
<div class="form-group">
<textarea class="form-control" name="message" placeholder="Ваше сообщение"
required maxlength="500" rows="3">{{ old('message') ?? '' }}</textarea>
</div>
<div class="form-group">
<input type="file" class="form-control-file" name="image" accept="image/png, image/jpeg">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Отправить</button>
</div>
</form>
@endsection
Шаблон почтового сообщения
Нам потребуется шаблон для письма:
resources/views/email/feedback.blade.php<h1>Форма обратной связи</h1>
<p><strong>Имя:</strong> {{ $data->name }}</p>
<p><strong>Почта:</strong> {{ $data->email }}</p>
<p><strong>Сообщение:</strong> {{ $data->message }}</p>
Класс отправки почты
В Laravel каждый тип почтового сообщения (обратная связь, заказ в магазине), отправляемых приложением, представлен классом Mailable
. Эти классы хранятся в директории app/Mail
, которая будет создана при создании первого такого класса.
Создаем класс:
php artisan make:mail FeedbackMailer
Заполняем класс:
app/Mail/FeedbackMailer.php<?php
namespace App\Mail;
use Illuminate\Support\Facades\Storage;
use stdClass;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class FeedbackMailer extends Mailable {
use Queueable, SerializesModels;
// Публичная переменная в которую запишем переданные данные формы из контроллера FeedbackController
public $data;
// Плучаем данные формы из контроллера FeedbackController
public function __construct(stdClass $feedback) {
$this->data = $feedback;
}
// Создаем сообщение
public function build() {
if ($this->data->image) {
$this->attachData($this->data->image, 'image.'.$this->data->ext);
}
// От кого письмо
$this->from('noreply@aurora.com', 'ООО ТД АВРОРА')
// Тема письма
->subject('Форма обратной связи')
// Вызываем представление
->view('email.feedback');
}
}
Мы получаем из объекта http-запроса сырую строку файла и передаем классу FeedbackMailer
. Но можно еще прикрепить файл из хранилища с помощью методов attachFromStorage()
(используется диск по умолчанию) или attachFromStorageDisk()
(диск надо указать самостоятельно).
app/Http/FeedbackController.phpclass FeedbackController extends Controller {
public function send(Request $request) {
$image = $request->file('image');
if ($image) { // был загружен файл изображения
$path = $image->store('feedback', 'local');
}
$data = new stdClass();
$data->image = $path ?? null;
Mail::to($data->email)->send(new FeedbackMailer($data));
return redirect()->route('feedback.index')
->with('success', 'Ваше сообщение успешно отправлено');
}
}
app/Mail/FeedbackMailer.phpclass FeedbackMailer extends Mailable {
public function build() {
if ($this->data->image) {
$this->attachFromStorageDisk('local', $this->data->image);
}
$this->subject('Форма обратной связи')
->view('email.feedback');
}
}
Мы сохраняем загруженное изображение в директорию storage/app/feedback
и потом оттуда прикрепляем к письму. Смысла хранить изображение дальше нет, так что его можно удалить. Но сразу удалять нельзя — возникает ошибка, потому что файл удаляется до того, как будет прикреплен к письму. Нужно добавить обработчик события MessageSent
(возникает после отправки) — и уже там удалить файл с диска.
Вместо заключения
Передать данные в шаблон письма resources/views/email/feedback.blade.php
можно через публичное свойство класса FeedbackMailer
. Все публичные поля этого класса будут доступны в шаблоне как переменные, в нашем случае $data
:
app/Mail/FeedbackMailer.phpclass FeedbackMailer extends Mailable {
use Queueable, SerializesModels;
public $data;
public function __construct(stdClass $data) {
$this->data = $data;
}
public function build() {
return $this->subject('Форма обратной связи')->view('email.feedback');
}
}
Чтобы изменить данные, прежде чем передать их шаблону, свойство должно быть объявлено как protected
или private
:
app/Mail/FeedbackMailer.phpclass FeedbackMailer extends Mailable {
use Queueable, SerializesModels;
private $data;
public function __construct(stdClass $data) {
$this->data = $data;
}
public function build() {
return $this->subject('Форма обратной связи')
->view('email.feedback')
->with([ // в шаблоне будут доступны $name, $email, $message
'name' => $this->data->name,
'email' => $this->data->email,
'message' => $this->data->message,
]);
}
}
Метод withSwiftMessage()
класса Mailable
позволяет зарегистрировать анонимную функцию, которая будет вызываться экземпляром класса SwiftMailer
перед отправкой сообщения. Это дает возможность кастомизировать сообщение перед тем как оно будет отправлено:
app/Mail/FeedbackMailer.phpclass FeedbackMailer extends Mailable {
public function build() {
$this->subject('Форма обратной связи')
->view('email.feedback');
$this->withSwiftMessage(function ($message) {
$message->getHeaders()->addTextHeader('Custom-Header', 'HeaderValue');
});
}
}