b2in/packages/acme/contact-form/README.md
2026-04-10 17:18:17 +02:00

8.2 KiB
Raw Blame History

Acme Contact Form

Ein wiederverwendbares, spam-geschütztes Kontaktformular-Package für Laravel mit Livewire. Bietet Honeypot, zeitbasierte Prüfung, Inhaltsanalyse, Rate Limiting und mehr ohne externe Dienste, DSGVO-konform.

Features

  • Spam-Schutz: Honeypot, Zeitprüfung, Inhaltsanalyse, Wegwerf-E-Mails, Rate Limiting, IP-Blacklist, Bot-User-Agent-Erkennung
  • Konfigurierbare Felder: Presets (simple, full) oder eigene Felddefinitionen
  • Livewire: Reaktives Formular ohne Page-Reload
  • Flexibel: Einfache Formulare bis komplexe Multi-Feld-Formulare
  • DSGVO-konform: Keine externen Services, alles in-house

Voraussetzungen

  • PHP 8.2+
  • Laravel 10+ / 11+ / 12+
  • Livewire 3+

Installation

1. Package installieren

composer require acme/contact-form

Für lokale Entwicklung (Path-Repository):

{
    "repositories": [
        {
            "type": "path",
            "url": "package/acme/contact-form"
        }
    ],
    "require": {
        "acme/contact-form": "@dev"
    }
}
composer update acme/contact-form

2. Konfiguration

php artisan vendor:publish --tag=contact-form-config

In .env:

CONTACT_FORM_RECIPIENT=kontakt@ihre-domain.de
CONTACT_FORM_MAX_ATTEMPTS=3
CONTACT_FORM_DECAY_MINUTES=15
CONTACT_FORM_MIN_FILL_TIME=3

3. Übersetzungen (optional)

php artisan vendor:publish --tag=contact-form-lang

Verwendung

Einfaches Formular (Preset: simple)

Name, E-Mail, Nachricht, Datenschutz-Checkbox:

<livewire:contact-form />

Oder mit explizitem Preset:

<livewire:contact-form preset="simple" subject="Kontaktanfrage" />

Vollständiges Formular (Preset: full)

Vorname, Nachname, E-Mail, Telefon, Unternehmen, Nachricht, Datenschutz:

<livewire:contact-form preset="full" subject="Neue Kontaktanfrage" />

Eigenes Formular mit benutzerdefinierten Feldern

Sie können beliebige Felder definieren. Jedes Feld benötigt:

  • type: text, email, tel, textarea, select, checkbox, honeypot
  • rules: Laravel-Validierungsregeln (Array)
  • label: Anzeigename (optional)
  • placeholder: Platzhalter (optional)
  • required: true/false (optional, wird aus rules abgeleitet)
  • name: Formularfeld-Name (optional, Standard: Array-Key)
  • options: Für select Array [value => label] (optional)

Beispiel: Minimales Formular (nur E-Mail + Nachricht)

<livewire:contact-form
    :fields="[
        'email' => [
            'type' => 'email',
            'rules' => ['required', 'email:rfc,dns', 'max:150'],
            'label' => 'Ihre E-Mail',
            'placeholder' => 'name@beispiel.de',
        ],
        'message' => [
            'type' => 'textarea',
            'rules' => ['required', 'string', 'max:2000'],
            'label' => 'Nachricht',
        ],
        'privacy' => [
            'type' => 'checkbox',
            'rules' => ['accepted'],
            'label' => 'Ich stimme der Datenschutzerklärung zu.',
        ],
        'honeypot' => [
            'type' => 'honeypot',
            'name' => 'website',
            'rules' => ['nullable', 'string', 'max:0'],
        ],
    ]"
    subject="Feedback"
/>

Beispiel: Formular mit Select-Feld

<livewire:contact-form
    :fields="[
        'name' => [
            'type' => 'text',
            'rules' => ['required', 'string', 'max:120'],
            'label' => 'Name',
        ],
        'email' => [
            'type' => 'email',
            'rules' => ['required', 'email:rfc,dns'],
            'label' => 'E-Mail',
        ],
        'topic' => [
            'type' => 'select',
            'rules' => ['required', 'string', 'in:general,support,sales'],
            'label' => 'Betreff',
            'placeholder' => 'Bitte wählen...',
            'options' => [
                'general' => 'Allgemeine Anfrage',
                'support' => 'Technischer Support',
                'sales' => 'Verkauf',
            ],
        ],
        'message' => [
            'type' => 'textarea',
            'rules' => ['required', 'string', 'max:2000'],
            'label' => 'Nachricht',
        ],
        'privacy' => [
            'type' => 'checkbox',
            'rules' => ['accepted'],
            'label' => 'Datenschutz akzeptiert',
        ],
        'honeypot' => [
            'type' => 'honeypot',
            'name' => 'company_check',
            'rules' => ['nullable', 'string', 'max:0'],
        ],
    ]"
    subject="Anfrage über Kontaktformular"
/>

Wichtig: Jedes Formular sollte ein Honeypot-Feld enthalten. Das Feld wird für Nutzer unsichtbar dargestellt; Bots füllen es oft aus und werden so erkannt.

Eigene Presets in der Config

In config/contact-form.php können Sie eigene Presets definieren:

'presets' => [
    'mein-formular' => [
        'name' => [
            'type' => 'text',
            'rules' => ['required', 'string', 'max:120'],
            'label' => 'Name',
        ],
        'email' => [
            'type' => 'email',
            'rules' => ['required', 'email:rfc,dns'],
            'label' => 'E-Mail',
        ],
        'message' => [
            'type' => 'textarea',
            'rules' => ['required', 'string', 'max:2000'],
            'label' => 'Nachricht',
        ],
        'privacy' => [
            'type' => 'checkbox',
            'rules' => ['accepted'],
            'label' => 'Datenschutz',
        ],
        'honeypot' => [
            'type' => 'honeypot',
            'name' => 'website',
            'rules' => ['nullable', 'string', 'max:0'],
        ],
    ],
],

Verwendung:

<livewire:contact-form preset="mein-formular" />

Spam-Schutz im Detail

Maßnahme Beschreibung
Honeypot Verstecktes Feld wenn ausgefüllt → Spam
Zeitprüfung Formular < 3 Sek. ausgefüllt → Spam
Inhaltsanalyse Spam-Keywords, URLs, XSS-Muster → Spam
Wegwerf-E-Mails tempmail.com, mailinator.com etc. → Spam
Rate Limiting Max. 3 Anfragen/IP in 15 Min. (konfigurierbar)
IP-Blacklist Blockierte IPs in Config
Bot-User-Agent curl, wget, python-requests etc. → Spam

Bei Spam wird immer eine Erfolgsmeldung angezeigt (Täuschung von Bots), aber keine E-Mail versendet. Alle Anfragen werden geloggt.

Service und SpamDetector direkt nutzen

Falls Sie einen eigenen Controller oder eine eigene Livewire-Komponente verwenden möchten:

use Acme\ContactForm\ContactFormService;
use Acme\ContactForm\SpamDetector;

// Spam prüfen
$spamDetector = SpamDetector::fromConfig();
$isSpam = $spamDetector->detect($validatedData, $formLoadedAt);

// Anfrage verarbeiten
$service = app(ContactFormService::class);
$service->handle([
    'name' => $request->input('name'),
    'email' => $request->input('email'),
    'message' => $request->input('message'),
    'ip' => $request->ip(),
    'user_agent' => $request->userAgent(),
    'is_spam' => $isSpam,
], 'Betreff der E-Mail', 'Log-Kontext');

Konfiguration

Option Beschreibung Standard
recipient E-Mail-Empfänger aus .env
rate_limit.max_attempts Max. Anfragen pro IP 3
rate_limit.decay_minutes Zeitfenster in Minuten 15
blacklisted_ips IP-Adressen blockieren []
honeypot_fields Namen der Honeypot-Felder company_check, website, url
spam.min_fill_time_seconds Min. Zeit zum Ausfüllen 3
spam.suspicious_patterns Regex für Spam-Erkennung siehe Config
spam.disposable_email_domains Wegwerf-Domains siehe Config

Styling anpassen

Die Standard-Views nutzen generische Klassen (border-gray-300, focus:ring-primary). Passen Sie sie an Ihr Design an:

php artisan vendor:publish --tag=contact-form-views

Die Views liegen dann unter resources/views/vendor/contact-form/.

Event nach Absenden

Nach erfolgreichem Absenden wird das Event contact-form-submitted dispatched. Sie können darauf reagieren:

<div x-data="{ submitted: false }"
     x-on:contact-form-submitted.window="submitted = true">
    <livewire:contact-form />

    <template x-if="submitted">
        <p>Vielen Dank für Ihre Nachricht!</p>
    </template>
</div>

Lizenz

MIT License