Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Changelog

## [0.1.0] - 2026-07-23

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The year in the release date seems to be a future date (2026). Unless this is intentional, it is likely a typo and should be updated to reflect the actual release date.

Suggested change
## [0.1.0] - 2026-07-23
+## [0.1.0] - 2025-02-24

- 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
Comment on lines +4 to +10

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To follow the 'Keep a Changelog' standard, items should be grouped under category headings such as ### Added. This improves readability and organization as the project evolves.

Suggested change
- 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
### Added
- 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

2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions src/Destinations/MailDestination.php
Original file line number Diff line number Diff line change
Expand Up @@ -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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the project uses PHP 8.0+ features (like str_starts_with), you can replace the Laravel optional() helper with the native null-safe operator (?->). This is more idiomatic in modern PHP and avoids the overhead of a helper function call.

Suggested change
$recipient = optional($submission->form)->recipient_email
$recipient = $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));
}
}
2 changes: 1 addition & 1 deletion src/Destinations/SmartpingsDestination.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +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.'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updated error message implies that setting the environment variables is sufficient to enable the destination. However, the method remains unimplemented and will still throw a RuntimeException. It is clearer to state that the implementation is pending integration.

Suggested change
'SmartPings destination is not implemented yet. Set SMARTPINGS_CLIENT_ID and SMARTPINGS_SECRET_ID before enabling it.'
'SmartPings destination is not implemented yet. It requires integration with the smartpings/php-sdk.'

);
}
}
3 changes: 2 additions & 1 deletion src/Destinations/WebhookDestination.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
14 changes: 8 additions & 6 deletions src/Models/FormSubmission.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Loading