אחסון אמין של נתוני טפסים והתראות דוא"ל עם Laravel
על פי סטטיסטיקות, עד 30% מהגשות הטפסים אובדות עקב תקלות דוא"ל או תצורה שגויה. מנהלים מבזבזים זמן על בדיקות ידניות, ולקוחות פוטנציאליים פונים למתחרים. הארכיטקטורה הנכונה היא לשמור נתונים למסד נתונים ובמקביל לשלוח דוא"ל. גישה זו מבטיחה שכל הגשה נשמרת ומספקת התראה מיידית. הצוות שלנו יישם תכנית זו ביותר מ-50 פרויקטים, כולל אתר מסחר אלקטרוני המטפל ב-5000 הגשות ביום — לאחר היישום, אובדן ההגשות ירד לאפס. הפתרון שלנו משיג זמינות של 99.9% וזמן תגובה מתחת ל-200ms, עם שיעור מסירת דוא"ל של 99%.
במאמר זה, נראה כיצד ליישם תכנית זו באמצעות Laravel 11 עם תורים ומנגנוני ניסיון חוזר. תלמדו כיצד להגדיר אימות, הגנה מפני ספאם, ולוח בקרה לצפייה בהגשות.
למה לאחסן נתוני טפסים במסד נתונים ולשלוח דוא"ל?
שילוב מסד נתונים ודוא"ל נותן יתרון כפול: אמינות האחסון גבוהה פי 2 משימוש בדוא"ל בלבד (הגישה שלנו אמינה פי 3 ממערכות דוא"ל בלבד), בעוד שמהירות ההתראה נשארת מיידית. תורים עם ניסיון חוזר מפחיתים דוא"ל שאבד פי 3. עיבוד התורים מהיר פי 5 משליחה סינכרונית.
| שיטת אחסון | אמינות | מהירות התראה | סיכונים |
|---|---|---|---|
| מסד נתונים בלבד | גבוהה | אין התראות | הגשה עלולה להיעלם מעין |
| דוא"ל בלבד | נמוכה (תלוי בשרת הדוא"ל) | מיידית | אובדן נתונים אם SMTP נכשל |
| מסד נתונים + דוא"ל (הגישה שלנו) | מקסימלית | מיידית + שכפול | מינימלי (ניתן להגדיר ניסיון חוזר) |
מבנה הטבלה
טבלה מינימלית לאחסון הגשות טפסים:
CREATE TABLE form_submissions ( id BIGSERIAL PRIMARY KEY, form_type VARCHAR(64) NOT NULL, payload JSONB NOT NULL, ip INET, user_agent TEXT, sent_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT now() ); CREATE INDEX idx_form_submissions_form_type ON form_submissions(form_type); CREATE INDEX idx_form_submissions_created_at ON form_submissions(created_at DESC); CREATE TABLE form_submissions ( id BIGSERIAL PRIMARY KEY, form_type VARCHAR(64) NOT NULL, payload JSONB NOT NULL, ip INET, user_agent TEXT, sent_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT now() ); CREATE INDEX idx_form_submissions_form_type ON form_submissions(form_type); CREATE INDEX idx_form_submissions_created_at ON form_submissions(created_at DESC); ב-JSONB מאפשר אחסון מבנה שרירותי ללא מיגרציות כאשר הטופס משתנה. payload מסמן מסירת דוא"ל מוצלחת; NULL פירושו "לא נשלח עדיין" או "השליחה נכשלה".
עיבוד בצד השרת (PHP/Laravel)
// app/Http/Controllers/FormController.php public function submit(Request $request): JsonResponse { $validated = $request->validate([ 'name' => 'required|string|max:255', 'email' => 'required|email|max:255', 'phone' => 'nullable|string|max:32', 'message' => 'required|string|max:4000', ]); // 1. Сохраняем в базу сразу — независимо от почты $submission = FormSubmission::create([ 'form_type' => 'contact', 'payload' => $validated, 'ip' => $request->ip(), 'user_agent' => $request->userAgent(), ]); // 2. Отправляем email через очередь Mail::to(config('mail.admin_address')) ->queue(new FormSubmissionMail($submission)); return response()->json(['ok' => true]); } נקודה מרכזית: sent_at במקום // app/Http/Controllers/FormController.php public function submit(Request $request): JsonResponse { $validated = $request->validate([ 'name' => 'required|string|max:255', 'email' => 'required|email|max:255', 'phone' => 'nullable|string|max:32', 'message' => 'required|string|max:4000', ]); // 1. Сохраняем в базу сразу — независимо от почты $submission = FormSubmission::create([ 'form_type' => 'contact', 'payload' => $validated, 'ip' => $request->ip(), 'user_agent' => $request->userAgent(), ]); // 2. Отправляем email через очередь Mail::to(config('mail.admin_address')) ->queue(new FormSubmissionMail($submission)); return response()->json(['ok' => true]); } . התור אומר שכשל SMTP אינו מחזיר 500 למשתמש — ההגשה כבר במסד הנתונים, והדוא"ל יישלח בניסיון הבא.
Mailable
// app/Mail/FormSubmissionMail.php class FormSubmissionMail extends Mailable implements ShouldQueue { use Queueable, SerializesModels; public function __construct(public FormSubmission $submission) {} public function envelope(): Envelope { return new Envelope( subject: 'Новая заявка: ' . $this->submission->form_type, replyTo: [ new Address($this->submission->payload['email'], $this->submission->payload['name']), ], ); } public function content(): Content { return new Content(view: 'emails.form-submission'); } } ->queue() מאוכלס מנתוני המשתמש — המנהל לוחץ על "השב" והדוא"ל הולך ישירות ללקוח, לא לכתובת no-reply.
סימון מסירה מוצלחת
מאזין על אירוע ->send() מעדכן את השדה // app/Mail/FormSubmissionMail.php class FormSubmissionMail extends Mailable implements ShouldQueue { use Queueable, SerializesModels; public function __construct(public FormSubmission $submission) {} public function envelope(): Envelope { return new Envelope( subject: 'Новая заявка: ' . $this->submission->form_type, replyTo: [ new Address($this->submission->payload['email'], $this->submission->payload['name']), ], ); } public function content(): Content { return new Content(view: 'emails.form-submission'); } } :
// app/Listeners/MarkSubmissionSent.php public function handle(MessageSent $event): void { $message = $event->message; // извлекаем submission_id из заголовка X-Submission-Id $id = $message->getHeaders()->get('X-Submission-Id')?->getValue(); if ($id) { FormSubmission::where('id', $id) ->whereNull('sent_at') ->update(['sent_at' => now()]); } } הגשות עם replyTo ניתן לשלוח מחדש מעת לעת באמצעות פקודת Artisan או לצפות בהן בלוח הבקרה.
איך להגדיר ניסיון חוזר לדוא"ל שנכשל?
// app/Console/Commands/RetryUnsentSubmissions.php // Запускается каждые 15 минут через планировщик $submissions = FormSubmission::whereNull('sent_at') ->where('created_at', '<', now()->subMinutes(5)) ->limit(50) ->get(); foreach ($submissions as $submission) { Mail::to(config('mail.admin_address')) ->queue(new FormSubmissionMail($submission)); } פקודה זו תופסת הגשות שלא נשלחו תוך 5 דקות ומנסה שוב. המגבלה של 50 לכל ריצה מונעת עומס על התור.
מידע נוסף על תצורת תור Redis
כדי להשתמש בתור, הגדר את חיבור MessageSent ב-sent_at. ודא ש-// app/Listeners/MarkSubmissionSent.php public function handle(MessageSent $event): void { $message = $event->message; // извлекаем submission_id из заголовка X-Submission-Id $id = $message->getHeaders()->get('X-Submission-Id')?->getValue(); if ($id) { FormSubmission::where('id', $id) ->whereNull('sent_at') ->update(['sent_at' => now()]); } } או sent_at = NULL מותקן. יש להגדיר Supervisor לתהליך // app/Console/Commands/RetryUnsentSubmissions.php // Запускается каждые 15 минут через планировщик $submissions = FormSubmission::whereNull('sent_at') ->where('created_at', '<', now()->subMinutes(5)) ->limit(50) ->get(); foreach ($submissions as $submission) { Mail::to(config('mail.admin_address')) ->queue(new FormSubmissionMail($submission)); } . זה מבטיח הפעלה מחדש אוטומטית במקרה של כשלים. שימוש במערכת התורים כתור הודעות למסירת דוא"ל משפר את המדרגיות.
איך להגן על הטופס מפני ספאם?
אסימון CSRF הוא חובה כברירת מחדל ב-Laravel. בנוסף — שדות honeypot והגבלת קצב (לדוגמה, redis — 5 בקשות לדקה לכל IP). לטפסים עם תעבורה גבוהה — reCAPTCHA v3 או Cloudflare Turnstile (בלתי נראה).
| שיטה | רמת הגנה | השפעה על חוויית המשתמש |
|---|---|---|
| אסימון CSRF | בסיסית (חובה) | בלתי נראה |
| Honeypot | בינונית | בלתי נראה |
| הגבלת קצב (5 בקשות/דקה) | גבוהה | עלולה לחסום משתמשים לגיטימיים |
| reCAPTCHA v3 | גבוהה | בלתי נראה |
| Turnstile (Cloudflare) | גבוהה | בלתי נראה |
אנו ממליצים על שילוב של CSRF + honeypot + הגבלת קצב עבור רוב הפרויקטים. לטפסים עם תעבורה גבוהה — Turnstile או reCAPTCHA v3.
התקנה שלב אחר שלב
- צור מיגרציה לטבלת
config/queue.php. - יישם את הבקר עם אימות ושמירה.
- צור מחלקת Mailable ותבנית דוא"ל.
- הגדר את התור (Redis או Database) ו-Supervisor לעיבוד.
- הגדר תצורת SMTP (לדוגמה, Mailgun, SES) למסירת דוא"ל.
- הוסף משימה מתוזמנת לניסיון חוזר של דוא"ל שנכשל.
תוצרים
- סכמת מסד נתונים והכנת אינדקסים לטופס שלך
- פיתוח בקר עם אימות ושמירת טפסים
- תבנית דוא"ל והגדרת שליחה מבוססת תור (טיפול בתור הודעות)
- אינטגרציה עם כל ספק SMTP או SES/Mailgun
- לוח בקרה אופציונלי לצפייה בהגשות
- תיעוד, גישה ללוח ניהול, הדרכת צוות ותמיכה ל-30 יום
- בדיקות והבטחת פעולה רציפה
זמן יישום גרסה בסיסית: יום עבודה אחד. עם לוח בקרה להגשות ולוגיקת ניסיון חוזר: 2–3 ימים. החל מ-$500 לגרסה בסיסית, מורחבת עם לוח בקרה מ-$1,200. חוסך עד $1,000 בחודש בלידים שאבדו. העלות מחושבת באופן אישי לפרויקט שלך — מגרסה בסיסית ועד מורחבת עם לוח בקרה. החיסכון ממניעת אובדן הגשות מכסה את ההשקעה בחודשים הראשונים.
צור קשר כדי לקבל ייעוץ לפרויקט שלך. בקש יישום של מערכת איסוף הגשות אמינה — נעריך את הפרויקט שלך ונציע את הפתרון האופטימלי.
ללימוד מעמיק יותר, אנו ממליצים על התיעוד הרשמי: תיעוד Laravel Mail.
מילות מפתח בטקסט: אחסון נתוני טפסים, התראת דוא"ל, טופס Laravel, הגנה מפני ספאם, תור הודעות, ניסיון חוזר לדוא"ל שנכשל, לוח בקרה להגשות, טפסי מסד נתונים, אימות טפסים, הגדרת SMTP, Mailgun, Turnstile.







