From 4060d32a11690aa97393e346c17ab9f64dd497ed Mon Sep 17 00:00:00 2001 From: nfebe Date: Thu, 23 Jul 2026 22:21:56 +0100 Subject: [PATCH 1/4] chore: Set version to 0.1.0 and add changelog Use a standard composer 'version' field and start the package pre-1.0 ahead of its first tagged release. --- CHANGELOG.md | 10 ++++++++++ composer.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2965dff --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +## [0.1.0] - 2026-07-23 +- Polymorphic form collection: a Form definition and its submissions, each scoped to any owning model via an owner morph +- Public, throttled submission endpoint that resolves a form by key and records the submission +- Pluggable, delivery-agnostic destinations (mail, webhook, Mautic, Smartpings) behind a destination contract, selectable per form +- Spam protection: honeypot fields, a minimum submit time, and per-minute rate limiting +- Typed submission columns (name, email, phone, subject, message) alongside an arbitrary payload, plus a delivery log +- A FormSubmittedEvent the host can bridge to its own workflows +- Config-driven throughout: destinations, protection thresholds, and the response formatter diff --git a/composer.json b/composer.json index 2d8413e..49eaafa 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "Polymorphic form collection with pluggable, agnostic delivery destinations.", "type": "library", "license": "MIT", - "package-version": "1.0.0", + "version": "0.1.0", "authors": [ { "name": "WhileSmart", From 089b4c0066ceb6aef63813fb08908855e9daaf3c Mon Sep 17 00:00:00 2001 From: nfebe Date: Thu, 23 Jul 2026 22:37:44 +0100 Subject: [PATCH 2/4] fix: Wrap an over-long line to satisfy the code sniffer --- src/Destinations/SmartpingsDestination.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Destinations/SmartpingsDestination.php b/src/Destinations/SmartpingsDestination.php index 4a25e41..2c4be81 100644 --- a/src/Destinations/SmartpingsDestination.php +++ b/src/Destinations/SmartpingsDestination.php @@ -18,7 +18,8 @@ class SmartpingsDestination implements FormDestination public function deliver(FormSubmission $submission): void { throw new RuntimeException( - 'SmartPings destination is not implemented yet. Provide SMARTPINGS_CLIENT_ID and SMARTPINGS_SECRET_ID so it can be built on smartpings/php-sdk and tested.' + 'SmartPings destination is not implemented yet. Provide SMARTPINGS_CLIENT_ID and ' + .'SMARTPINGS_SECRET_ID so it can be built on smartpings/php-sdk and tested.' ); } } From 875fa0db323a28dd5c0fd09b127cc60d0f9c297a Mon Sep 17 00:00:00 2001 From: nfebe Date: Thu, 23 Jul 2026 22:39:39 +0100 Subject: [PATCH 3/4] fix: Shorten the SmartPings message under the line-length limit --- src/Destinations/SmartpingsDestination.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Destinations/SmartpingsDestination.php b/src/Destinations/SmartpingsDestination.php index 2c4be81..458ed63 100644 --- a/src/Destinations/SmartpingsDestination.php +++ b/src/Destinations/SmartpingsDestination.php @@ -18,8 +18,7 @@ class SmartpingsDestination implements FormDestination public function deliver(FormSubmission $submission): void { throw new RuntimeException( - 'SmartPings destination is not implemented yet. Provide SMARTPINGS_CLIENT_ID and ' - .'SMARTPINGS_SECRET_ID so it can be built on smartpings/php-sdk and tested.' + 'SmartPings destination is not implemented yet. Set SMARTPINGS_CLIENT_ID and SMARTPINGS_SECRET_ID before enabling it.' ); } } From 4e44af2f2b1272d63415b38bf0ea52d8f2143040 Mon Sep 17 00:00:00 2001 From: nfebe Date: Thu, 23 Jul 2026 22:44:34 +0100 Subject: [PATCH 4/4] fix: Clear pre-existing mess-detector findings Rename short variables, lift an assignment out of an if condition, and drop an unused closure parameter so the mess detector passes. --- src/Destinations/MailDestination.php | 6 +++--- src/Destinations/WebhookDestination.php | 3 ++- src/Models/FormSubmission.php | 14 ++++++++------ 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Destinations/MailDestination.php b/src/Destinations/MailDestination.php index 866e4c7..4dec4f7 100644 --- a/src/Destinations/MailDestination.php +++ b/src/Destinations/MailDestination.php @@ -12,13 +12,13 @@ class MailDestination implements FormDestination { public function deliver(FormSubmission $submission): void { - $to = optional($submission->form)->recipient_email + $recipient = optional($submission->form)->recipient_email ?? config('eloquent-forms.mail.to'); - if (empty($to)) { + if (empty($recipient)) { throw new RuntimeException('No recipient configured for the mail destination.'); } - Mail::to($to)->send(new FormSubmissionReceived($submission)); + Mail::to($recipient)->send(new FormSubmissionReceived($submission)); } } diff --git a/src/Destinations/WebhookDestination.php b/src/Destinations/WebhookDestination.php index 9ecb9d9..791b442 100644 --- a/src/Destinations/WebhookDestination.php +++ b/src/Destinations/WebhookDestination.php @@ -28,7 +28,8 @@ public function deliver(FormSubmission $submission): void $body = json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); $headers = ['Content-Type' => 'application/json']; - if ($secret = config('eloquent-forms.webhook.secret')) { + $secret = config('eloquent-forms.webhook.secret'); + if ($secret) { $headers['X-Forms-Signature'] = hash_hmac('sha256', $body, $secret); } diff --git a/src/Models/FormSubmission.php b/src/Models/FormSubmission.php index e56a1b0..a1bce66 100644 --- a/src/Models/FormSubmission.php +++ b/src/Models/FormSubmission.php @@ -79,18 +79,20 @@ public function submittable(): MorphTo */ public function fields(): array { - return collect($this->payload ?? []) - ->reject(fn ($value, $key) => str_starts_with((string) $key, '_')) - ->all(); + return array_filter( + $this->payload ?? [], + fn ($key) => ! str_starts_with((string) $key, '_'), + ARRAY_FILTER_USE_KEY + ); } - public function recordDelivery(string $destination, bool $ok, ?string $detail = null): void + public function recordDelivery(string $destination, bool $succeeded, ?string $detail = null): void { $log = $this->delivery_log ?? []; $log[$destination] = array_filter([ - 'ok' => $ok, + 'ok' => $succeeded, 'detail' => $detail, - ], fn ($v) => $v !== null); + ], fn ($value) => $value !== null); $this->delivery_log = $log; $this->save();