[6.x] Forms 2: Connections - #15063
Open
duncanmcclean wants to merge 91 commits into
Open
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…need to use antlers Using Antlers in email address fields is still supported, but this is a slightly easier approach for end-users.
covers the situation where the "email" field the config is pointing to doesn't have a value. without us skipping the email, an error would be thrown.
- give email configs an `id` in the `email()` setter, and backfill them when hydrating `connections.email`, so `Email::render()` always has one to key on - strip null values per-config in `fileData()` rather than per-connection-type - run `CreateAssetsFromFileUploads` synchronously again, so assets exist before anything reads the submission. connection jobs stay chained behind it - restore `parseConfig()` to protected - stop scalar type hinting closure params in `addresses()` and `finalized()`, so malformed config is skipped instead of throwing - add the missing optional chaining to `FormFieldsFieldtype` - prune removed rows from `collapsed`, and route `enabled` through the `update:modelValue` setter in `ConnectionList` Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- `Connection` now uses the `HasTitle` trait, so custom titles get translated like other extension types - `ConnectionRepository::all()` matches `DictionaryRepository` and the unused `classes()` method has been removed - dropped the redundant `ConnectionRepository` singleton binding, since facades auto-resolve repositories - documented `connections()` on the form contract for the next breaking release and deprecated `email()` Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
whether a config should run is one question, so disabled rows now fail `passes()` rather than every caller pairing it with its own enabled check. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- renamed `ConnectionList`/`ConnectionListItem`/`ConnectionLogic` to `ConnectionRows`/`ConnectionRow`/`ConnectionRules`, matching the row terminology used by replicator and grid - `ConnectionRows` now owns adding, duplicating, removing and dirty tracking, since every connection uses the same row shape - the initial row mapping is handled by the exported `connectionRows` helper Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
the empty states were reusing the connect index card descriptions, which just restate the page title. they now explain what you'd add and why, and the "webhooks" label is hidden when there's nothing under it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
duncanmcclean
marked this pull request as ready for review
August 17, 2026 09:58
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request implements the concept of "Connections" for forms.
Connections let a form talk to the outside world when submissions come in — starting with Emails and Webhooks, and paving the way for third-party integrations.
Forms now have a "Connect" area in the Control Panel, listing the available connections along with how many of each are configured.
Emails
Emails mostly work like they did before, they've just moved to the "Connect" area. We have added a few niceities though:
CleanShot.2026-08-17.at.10.59.30.mp4
Existing
emailconfigs in form YAML are automatically converted to email connections, saved under the newconnectionskey.Form::email()has been deprecated in favour ofForm::connections().Webhooks
Upon submission, forms can now send webhooks — a POST request containing the
formhandle andsubmissiondata, sent to a URL of your choice.SSL verification can be disabled per webhook, useful for local development or when sending requests to internal services.
Like emails, webhooks can be triggered based on conditions.
Registering custom connections
Apps and addons can register their own connections, which will show up alongside the built-in ones in the "Connect" area.
Registering a connection
A connection is a class that extends
Statamic\Forms\Connections\Connection. It provides a title, description and icon for the Connect index, an optionalcount()for the badge on the index table, and returns a Vue component fromrender():Connections in the
FormConnectionsdirectory of apps and addons are registered automatically. Addons can also register them explicitly via the$formConnectionsproperty in their service provider.Connections need to register their own route for saving. Any routes registered via the
routes()method are automatically wrapped in authorization.Frontend
The
render()method determines which Vue component gets rendered, along with its props.If your connection supports multiple "rows" (eg. multiple emails per form), you can use the
<ConnectionList>component to get a head start.Simply pass it your array of configs via
v-model, a header slot and a body slot for each row, and it takes care of the collapsible row UI, along with the add/duplicate/remove actions.Logic
If you want your connection to support conditional logic, the
<ConnectionLogic>component renders the logic builder. Simply bind your conditions withv-model:conditionsand put whatever the conditions control inside itsthenslot.On the PHP side, the
Statamic\Forms\Connections\ConnectionLogicclass handles the rest:ConnectionLogic::normalize($conditions)strips out any incomplete conditions, and returnsnullwhen there's nothing to save.ConnectionLogic::passes($config, $submission)evaluates the conditions against the submission, so you can decide whether to send anything or not.Sending notifications
When a submission is finalized, Statamic dispatches a single job chain: file uploads are converted to assets, then each of the connection jobs run and finally temporary file uploads are deleted.
To hook into this process, you should return a job (or array of jobs) from the
finalized()method:Because we're using Laravel's job chaining feature, if you need to dispatch additional jobs within one of your jobs, call
$this->prependToChain($job)(from Laravel'sQueueabletrait) so they stay part of the chain.Depends on: #15064
Related: https://github.com/statamic/forms-pro/pull/17
Closes statamic/ideas#1176
Closes statamic/ideas#1434