feat: answer received form submissions by mail

This commit is contained in:
fromme committed 2026-09-24 16:35:31 +02:00
1 parent 19364df2db
commit aa728ddc99
22 files changed
+974 -9

No files matched your search

@@ -66,6 +66,12 @@ class ApplicationFormSubmissionDto
public readonly ?string $zipCode = null,
#[Assert\Length(max: 255)]
public readonly ?string $city = null,
// Whether the applicant holds an EU (incl. Switzerland) nationality - the one payload
// field a rule depends on, hence mapped rather than left in $details. bool|string|int
// for the same reason as pageUid: a form posts "1" or "" as readily as a JSON boolean,
// and losing a whole application over the spelling would be absurd. Read it back
// through isEu().
public readonly bool|string|int|null $eu = null,
public readonly array $details = [],
) {
}
@@ -113,6 +119,28 @@ class ApplicationFormSubmissionDto
* Only the shape is checked, because that is all the json column and the display can carry:
* a label with content, and a scalar or a flat list as the answer.
*/
/**
* A value that is neither a boolean nor one of the spellings a form posts is a 400 rather
* than a silent null: null means "this form does not ask", and a misconfigured mapping that
* quietly stopped every receipt mail would be the worst of both worlds. The same reasoning
* as validatePageUid().
*/
#[Assert\Callback]
public function validateEu(ExecutionContextInterface $executionContext): void
{
if (null === $this->eu) {
return;
}
if (null === $this->isEu()) {
$executionContext
->buildViolation('eu must be a boolean.')
->atPath('eu')
->addViolation()
;
}
}
#[Assert\Callback]
public function validateDetails(ExecutionContextInterface $executionContext): void
{
@@ -154,6 +182,25 @@ class ApplicationFormSubmissionDto
}
}
/**
* null for "the form does not ask" as well as for a value that made no sense - the caller
* treats both as "no automatic mail", and validateEu() has already turned the second into a
* 400 by the time anything reads this.
*/
public function isEu(): ?bool
{
if (is_bool($this->eu)) {
return $this->eu;
}
return match (is_string($this->eu) ? mb_strtolower(trim($this->eu)) : $this->eu) {
null => null,
1, '1', 'true' => true,
0, '0', 'false', '' => false,
default => null,
};
}
public function getPageUid(): ?int
{
if (null === $this->pageUid) {