Form Component
Table of contents
Overview
The default bundle configuration is to enable this component as seen below.
silverback_api_components:
enabled_components:
form: true #default
The Form resource allows you to define a Symfony Form type class and it will serialze an object formView
representing the form. You can also send POST
and PATCH
requests to special endpoints created for the resource to validate fields of the form and submit the form.
The endpoint will have the following format
/component/forms/{id}/submit
Usage
Create the component.
Endpoint /component/forms
. Example POST
{
"formType": "App\\Form\\FormType"
}
Output
Here is an example of the data you will receive:
{
"@context": "/contexts/Form",
"@id": "/component/forms/48b72a08-8fc5-11ea-9d59-003ee1c35971",
"@type": "Form",
"formType": "App\\Form\\FormType",
"formView": {
"vars": {
"errors": [],
"action": "",
"api_disabled": false,
"attr": {
"novalidate": "novalidate"
},
"block_prefixes": [
"form",
"test",
"_test"
],
"disabled": false,
"full_name": "test",
"id": "test",
"label_attr": [],
"name": "test",
"post_app_proxy": "/proxy",
"realtime_validate_disabled": false,
"required": true,
"submitted": true,
"unique_block_prefix": "_test",
"valid": true,
"value": {
"name": "John Smith"
}
},
"children": [
{
"vars": {
"errors": [],
"action": "",
"attr": [],
"block_prefixes": [
"form",
"text",
"_test_name"
],
"disabled": false,
"full_name": "test[name]",
"id": "test_name",
"label_attr": [],
"name": "name",
"required": true,
"submitted": true,
"unique_block_prefix": "_test_name",
"valid": true,
"value": "John Smith"
},
"children": [],
"rendered": false,
"methodRendered": false,
"form": {
"name": [],
"company": []
}
},
{
"vars": {
"errors": [],
"action": "",
"attr": [],
"block_prefixes": [
"form",
"text",
"_test_company"
],
"disabled": false,
"full_name": "test[company]",
"id": "test_company",
"label_attr": [],
"name": "company",
"required": true,
"submitted": false,
"unique_block_prefix": "_test_company",
"valid": true,
"value": ""
},
"children": [],
"rendered": false,
"methodRendered": false,
"form": {
"name": [],
"company": []
}
}
],
"rendered": false,
"methodRendered": false,
"form": {
"name": [],
"company": []
}
},
"componentLocations": [],
"componentGroups": [],
"modifiedAt": "2020-05-06T18:13:27+00:00",
"createdAt": "2020-05-06T18:13:27+00:00",
"_metadata": {
"persisted": true
}
}
Submitting form data
PATCH / Validate fields
Endpoint: /component/forms/{id}/submit
Instead of duplicating validation in the front-end application and having to keep it sycnhronised in your API, you can easily validate a single field (or group of fields). Here is an example of what you could submit:
{
"name": "",
"company": "company"
}
If the validation is successful, you will receive a 200
HTTP status code. Otherwise you will receive a 400
status code. In both instances you will receive the exact same structure of a serialized form as when you get a form resource. There will be keys on each item that is submitted and validation errors where applicable.
Example:
{
"@context": "/contexts/Form",
"@id": "/component/forms/eb48bf02-8fc5-11ea-95f7-003ee1c35971",
"@type": "Form",
"formType": "App\\Form\\FormType",
"formView": {
"vars": {
"errors": [],
"action": "",
"api_disabled": false,
"attr": {
"novalidate": "novalidate"
},
"block_prefixes": [
"form",
"test",
"_test"
],
"disabled": false,
"full_name": "test",
"id": "test",
"label_attr": [],
"name": "test",
"post_app_proxy": "/proxy",
"realtime_validate_disabled": false,
"required": true,
"submitted": true,
"unique_block_prefix": "_test",
"valid": false,
"value": {
"name": null,
"company": "company"
}
},
"children": [
{
"vars": {
"errors": [
"Please provide your name"
],
"action": "",
"attr": [],
"block_prefixes": [
"form",
"text",
"_test_name"
],
"disabled": false,
"full_name": "test[name]",
"id": "test_name",
"label_attr": [],
"name": "name",
"required": true,
"submitted": true,
"unique_block_prefix": "_test_name",
"valid": false,
"value": ""
},
"children": [],
"rendered": false,
"methodRendered": false,
"form": {
"name": [],
"company": []
}
},
{
"vars": {
"errors": [],
"action": "",
"attr": [],
"block_prefixes": [
"form",
"text",
"_test_company"
],
"disabled": false,
"full_name": "test[company]",
"id": "test_company",
"label_attr": [],
"name": "company",
"required": true,
"submitted": true,
"unique_block_prefix": "_test_company",
"valid": true,
"value": "company"
},
"children": [],
"rendered": false,
"methodRendered": false,
"form": {
"name": [],
"company": []
}
}
],
"rendered": false,
"methodRendered": false,
"form": {
"name": [],
"company": []
}
},
"componentLocations": [],
"componentGroups": [],
"modifiedAt": "2020-05-06T18:18:00+00:00",
"createdAt": "2020-05-06T18:18:00+00:00",
"_metadata": {
"persisted": true
}
}
POST (submitting the form)
Endpoint: /component/forms/{id}/submit
This is very similar to a validation request where you receive the form back and a HTTP status code of 400
for an invalid submission. For a successful submission the status code is 201
and by default you will still receive the form back.
Form Success Listeners
On a successful submission, an event is fired Silverback\ApiComponentsBundle\Event\FormSuccessEvent
. You can hook into this event just as you can with any other event in Symfony.
You have 2 useful methods to use the form data: FormSuccessEvent::getForm()
which returns the form resource and FormSuccessEvent::getFormData()
which is a shortcut to FormSuccessEvent::getForm()->formView->getForm()->getData()
and will return the submitted form data.
If you set FormSuccessEvent->result
, then whatever you set will be serialized and returned to your API user.
Reusable EntityPersistFormListener
You can re-use a listener if you simply want to persist the data in your submitted form to the database.
Create your class, for example:
Using this listener will result in your object being serialised and returned to the API User upon successful submission by default. Set the 3rd parameter on the parent constructor to
false
to disable this.
<?php
declare(strict_types=1);
namespace App\EventListener\Form\User;
use Silverback\ApiComponentsBundle\Entity\User\AbstractUser;
use Silverback\ApiComponentsBundle\EventListener\Form\EntityPersistFormListener;
use Silverback\ApiComponentsBundle\Form\Type\User\NewEmailAddressType;
class NewEmailAddressListener extends EntityPersistFormListener
{
public function __construct()
{
parent::__construct($supportedFormType = NewEmailAddressType::class, $supportedDataClass = AbstractUser::class, $returnFormDataOnSuccess = true);
}
}
Register the service like this:
use App\EventListener\Form\User\NewEmailAddressListener;
use Silverback\ApiComponentsBundle\EventListener\Form\EntityPersistFormListener;
use Silverback\ApiComponentsBundle\Event\FormSuccessEvent;
$services
->set(NewEmailAddressListener::class)
->parent(EntityPersistFormListener::class)
->tag('kernel.event_listener', ['event' => FormSuccessEvent::class]);
or
App\EventListener\Form\User\NewEmailAddressListener:
parent: Silverback\ApiComponentsBundle\EventListener\Form\EntityPersistFormListener
tags:
- { name: 'kernel.event_listener', event: 'Silverback\ApiComponentsBundle\Event\FormSuccessEvent' }