1. Go to this page and download the library: Download b2pweb/bdf-form library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
b2pweb / bdf-form example snippets
namespace App\Form;
use Bdf\Form\Aggregate\FormBuilderInterface;
use Bdf\Form\Custom\CustomForm;
class LoginForm extends CustomForm
{
protected function configure(FormBuilderInterface $builder) : void
{
// Register inputs using builder
// ltiple actions in one form)
$builder->submit('login');
}
}
// Instantiate the form (a container can be use for handle dependency injection)
$form = new LoginForm();
$view = $form->view(); // Get the form view
// Instantiate the form (a container can be use for handle dependency injection)
$form = new LoginForm();
// Submit and check if the form is valid
if (!$form->submit($_POST)->valid()) {
// The form has an error : use `ElementInterface::error()` to get the error and render it
echo 'Error : ', $form->error();
return;
}
// The form is valid : get the value
$credentials = $form->value();
// $credentials is an array with elements values
performLogin($credentials['username'], $credentials['password']);
// Declare the entity
// The properties should be public, or has public accessors to be handled by the form
class Person
{
/** @var string */
public $firstName;
/** @var string */
public $lastName;
/** @var DateTimeInterface|null */
public $birthDate;
/** @var Country|null */
public $country;
}
class PersonForm extends \Bdf\Form\Custom\CustomForm
{
protected function configure(\Bdf\Form\Aggregate\FormBuilderInterface $builder) : void
{
// Define that PersonForm::value() should return a Person instance
$builder->generates(Person::class);
// Declare fields with getter and setter
$builder->string('firstName')->
class PersonController extends Controller
{
private $repository;
// Get a form view with entity values
public function editForm($request)
{
// Get the entity
$person = $this->repository->find($request->query->get('id'));
// Create the form, import the entity data, and create the view object
$form = new PersonForm();
$view = $form->import($person)->view();
// The form view can be used: fields values are set
return $this->render('person/form', ['form' => $view]);
}
// Use the form to create the entity
public function create($request)
{
// Get the form instance
$form = new PersonForm();
// Submit form data
if (!$form->submit($request->post())->valid()) {
throw new FormError($form->error());
}
// $form->value() will return the filled entity
$this->repository->insert($form->value());
}
// Update an existent entity: simply attach the entity to fill
public function update($request)
{
// Get the entity
$person = $this->repository->find($request->query->get('id'));
// Get the form instance and attach the entity to update
$form = new PersonForm();
$form->attach($person);
// Submit form data
if (!$form->submit($request->post())->valid()) {
throw new FormError($form->error());
}
// $form->value() will return the filled entity
$this->repository->insert($form->value());
}
// Works like update, but apply only provided fields (HTTP PATCH method)
// The entity must be import()'ed instead of attach()'ed
public function patch($request)
{
// Get the entity
$person = $this->repository->find($request->query->get('id'));
// Get the form instance and import the entity to patch
$form = new PersonForm();
$form->import($person);
// Submit form data
if (!$form->patch($request->post())->valid()) {
throw new FormError($form->error());
}
// $form->value() will return the filled entity
$this->repository->insert($form->value());
}
}
// Declare a transformer
class JsonTransformer implements \Bdf\Form\Transformer\TransformerInterface
{
public function transformToHttp($value,\Bdf\Form\ElementInterface $input)
{
return json_encode($value);
}
public function transformFromHttp($value,\Bdf\Form\ElementInterface $input)
{
return json_decode($value, true);
}
}
class MyForm extends CustomForm
{
protected function configure(FormBuilderInterface $builder): void
{
// Transform JSON input to associative array
$builder->transformer(new JsonTransformer());
}
}
// Filter for keep only alpha numeric characters
class AlphaNumFilter implements \Bdf\Form\Filter\FilterInterface
{
public function filter($value,\Bdf\Form\Child\ChildInterface $input,$default)
{
if (!is_string($value)) {
return null;
}
return preg_replace('/[^a-z0-9]/i', '', $value);
}
}
class MyForm extends CustomForm
{
protected function configure(FormBuilderInterface $builder): void
{
$builder->string('foo')->filter(new AlphaNumFilter())->setter();
}
}
$form = new MyForm();
$form->submit(['foo' => '$$$abc123___']);
$form->value()['foo'] === 'abc123'; // The string is filtered
// Declare a transformer
class Base64Transformer implements \Bdf\Form\Transformer\TransformerInterface
{
public function transformToHttp($value,\Bdf\Form\ElementInterface $input)
{
return base64_encode($value);
}
public function transformFromHttp($value,\Bdf\Form\ElementInterface $input)
{
$value = base64_decode($value);
// Throw exception on invalid value
if ($value === false) {
throw new InvalidArgumentException('Invalid base64 data');
}
return $value;
}
}
class MyForm extends CustomForm
{
protected function configure(FormBuilderInterface $builder): void
{
// "foo" is a base64 string input
$builder
->string('foo')
->transformer(new Base64Transformer())
->transformerErrorMessage('Expecting base 64 data') // Define custom transformer error code and message
->transformerErrorCode('INVALID_BASE64_ERROR')
;
}
}
class MyForm extends CustomForm
{
protected function configure(FormBuilderInterface $builder): void
{
// The date should be saved as timestamp on the entity
$builder
->dateTime('date')
->saveAsTimestamp()
->setter()
;
// Save data as mongodb Binary
$builder
->string('data')
->modelTransformer(function ($value, $input, $toModel) {
return $toModel ? new Binary($value, Binary::TYPE_GENERIC) : $value->getData();
})
->setter()
;
}
}
class MyForm extends CustomForm
{
protected function configure(FormBuilderInterface $builder): void
{
$builder->generates(MyEntity::class);
$builder->string('foo')->setter();
$builder->string('bar')->setter();
$builder->satisfy(function (MyEntity $entity) {
// Validate the hydrated entity
if (!$entity->isValid()) {
return 'Invalid entity';
}
});
}
}
class UserForm extends \Bdf\Form\Custom\CustomForm
{
protected function configure(\Bdf\Form\Aggregate\FormBuilderInterface $builder): void
{
// Define a sub-form "credentials", which generates a Credentials object
$builder->embedded('credentials', function (\Bdf\Form\Child\ChildBuilderInterface $builder) {
// $builder is type of ChildBuilderInterface, but forward call to FormBuilderInterface
// So it can be used like a simple form builder
$builder->generates(Credentials::class);
$builder->string('username')-> // embedded and leaf fields can be mixed on the same form
$builder->string('email')->
class CredentialsForm extends \Bdf\Form\Custom\CustomForm
{
protected function configure(\Bdf\Form\Aggregate\FormBuilderInterface $builder) : void
{
$builder->generates(Credentials::class);
$builder->string('username')-> {
$builder->generates(Address::class);
$builder->string('address')->ace $builder) : void
{
// Simply define element with the embedded form class name
$builder->add('credentials', CredentialsForm::class);
$builder->array('addresses', AddressForm::class);
$builder->string('email')->
class UserForm extends \Bdf\Form\Custom\CustomForm
{
protected function configure(\Bdf\Form\Aggregate\FormBuilderInterface $builder) : void
{
// Simply define element with the embedded form class name
$builder->add('credentials', CredentialsForm::class)->prefix();
$builder->array('addresses', AddressForm::class);
$builder->string('email')->
// Using "low level" FieldPath helper
class CredentialsForm extends \Bdf\Form\Custom\CustomForm
{
protected function configure(\Bdf\Form\Aggregate\FormBuilderInterface $builder) : void
{
$builder->string('username');
$builder->string('password');
$builder->string('confirm')
->depends('password') // Password must be submitted before confirm
->satisfy(function ($value, \Bdf\Form\ElementInterface $input) {
// Get sibling field value using FieldPath
// Note: with FieldPath, the path is relative to the parent of the current field
if ($value !== \Bdf\Form\Util\FieldPath::parse('password')->value($input)) {
return 'Confirm must be same as password';
}
})
;
}
}
// Using FieldFinderTrait on custom form
class CredentialsForm extends \Bdf\Form\Custom\CustomForm
{
use \Bdf\Form\Util\FieldFinderTrait;
protected function configure(\Bdf\Form\Aggregate\FormBuilderInterface $builder) : void
{
$builder->string('username');
$builder->string('password');
$builder->string('confirm')
->depends('password') // Password must be submitted before confirm
->satisfy(function ($value, \Bdf\Form\ElementInterface $input) {
// Get sibling field value using findFieldValue
// Note: with FieldFinderTrait, the path is relative to the custom form
if ($value !== $this->findFieldValue('password')) {
return 'Confirm must be same as password';
}
})
;
}
}
use Bdf\Form\Util\FieldPath;
$form = new UserForm();
// Find the username field, starting from the root
// Start the expression with "." to not start the path from the parent of UserForm (which do not exists)
$username = FieldPath::parse('./embedded/username')->resolve($form);
// Also works from a "leaf" field
$password = FieldPath::parse('password')->resolve($username);
// Same as above
$password = FieldPath::parse('../password')->resolve($username);
// Absolute path : get "email" field of the root form
$email = FieldPath::parse('/email')->resolve($username);
$form = new MyForm();
// Submit the form
$form->submit($_POST);
// Get the submitted button name
// The submit button is handled by the root element
switch ($btn = $form->root()->submitButton() ? $btn->name() : null) {
case MyForm::SAVE_BTN:
doSave($form->value());
break;
case MyForm::DELETE_BTN:
doDelete($form->value());
break;
default:
throw new Exception();
}
$builder->integer('number')
->posivite() // Same as ->min(0)
->min(5) // The number must be >= 5
->max(9999) // The element must be <= 9999
->grouping(true) // The HTTP value will group values by thousands (i.e. 145 000 instead of 145000)
->raw(false) // If set to true, the input will be simply caster to int, and not transformed following the locale
;
$builder->float('number')
->posivite() // Same as ->min(0)
->min(1.1) // The number must be >= 1.1
->max(99.99) // The element must be <= 99.99
->grouping(true) // The HTTP value will group values by thousands (i.e. 145 000 instead of 145000)
->scale(2) // Only consider 2 digit on the decimal part
->raw(false) // If set to true, the input will be simply caster to int, and not transformed following the locale
;
$builder->boolean('enabled')
->httpValue('enabled') // Define the
$builder->dateTime('eventDate')
->className(Carbon::class) // Define a custom date class
->immutable() // Same as ->className(DateTimeImmutable::class)
->format('d/m/Y H:i') // Define the parsed date format
->timezone('Europe/Paris') // Define the parse timezone. The element PHP value will be on this timezone
->before(new DateTime('+1 year')) // eventDate must be before next year
->beforeField('eventDateEnd') // Compare the field value to a sibling field (eventDateEnd)
->after(new DateTime()) // eventDate must be after now
->afterField('accessDate') // Compare the field value to a sibling field (accessDate)
$builder->phone('contact')
->regionResolver(function () {
return $this->user()->country(); // Resolve the phone region using a custom resolver
})
->region('FR') // Force the region value for parse the phone number
->regionInput('address/country') // Use a sibling input for parse the number (do not forget to call `depends()`)
->allowInvalidNumber(true) // Do not check the phone number
->validateNumber('My error message') // Enable validation, and define the validator options (here the error message)
->setter()->saveAsString() // Save the phone number as string on the entity
;
$builder->csrf() // No need to set a name. by default "_token"
->tokenId('my_token') // Define the token id. By default is use CsrfElement::class, but define a custom tokenId permit to not share CSRF token between forms
->message('The token is invalid') // Define a custom error message
->invalidate(true) // The token is always invalidated after check, and should be regenerated
;
$builder->add('foo', AnyElement::class) // No helper method are present
->satisfy(function ($value) { ... }) // Configure the element
->transform(function ($value) { ... })
;
use Bdf\Form\Aggregate\FormBuilderInterface;
use Bdf\Form\Custom\CustomForm;
use Bdf\Form\ElementInterface;
use Bdf\Form\Transformer\TransformerInterface;
// Declare the element class
class UriElement extends CustomForm
{
const INNER_ELEMENT = 'uri';
/**
* @var UriFactoryInterface
*/
private $uriFactory;
// Set the UriFactoryInterface at the constructor
public function __construct(?FormBuilderInterface $builder = null, ?UriFactoryInterface $uriFactory = null)
{
parent::__construct($builder);
$this->uriFactory = $uriFactory ?? new DefaultUriFactory(); // Default instance for the factory
}
protected function configure(FormBuilderInterface $builder) : void
{
// Define inner element to store value
// The URI is basically a string
$builder->string(self::INNER_ELEMENT);
// The UriElement is a form, and supports only array values
// Define a transformer to remap value to the inner element
$builder->transformer(new class implements TransformerInterface {
public function transformToHttp($value, ElementInterface $input)
{
// $value is an array of children value
// Return only the inner element value
return $value[UriElement::INNER_ELEMENT];
}
public function transformFromHttp($value, ElementInterface $input)
{
// $value is the URI string
// Map it as array to the inner element
return [UriElement::INNER_ELEMENT => $value];
}
});
// Define the value generator : parse the inner element value to UriInterface using the factory
$builder->generates(function () {
return $this->uriFactory->createUri($this[self::INNER_ELEMENT]->element()->value());
});
}
}
use Bdf\Form\Choice\ChoiceInterface;
use Bdf\Form\Leaf\LeafElement;
use Bdf\Form\Transformer\TransformerInterface;
use Bdf\Form\Validator\ValueValidatorInterface;
// Declare the element using LeafElement class
class UriElement extends LeafElement
{
/**
* @var UriFactory
*/
private $uriFactory;
// Overrides constructor to add the factory
public function __construct(?ValueValidatorInterface $validator = null, ?TransformerInterface $transformer = null, ?ChoiceInterface $choices = null, ?UriFactory $uriFactory = null)
{
parent::__construct($validator, $transformer, $choices);
$this->uriFactory = $uriFactory ?? new DefaultUriFactory(); // Use default instance
}
// Parse the HTTP string value using the factory
protected function toPhp($httpValue): ?UriInterface
{
return $httpValue ? $this->uriFactory->createUri($httpValue) : null;
}
// Stringify the UriInterface instance
protected function toHttp($phpValue): ?string
{
return $phpValue === null ? null : (string) $phpValue;
}
}
use Bdf\Form\AbstractElementBuilder;
use Bdf\Form\Choice\ChoiceBuilderTrait;
use Bdf\Form\ElementInterface;
// Declare the builder
class UriElementBuilder extends AbstractElementBuilder
{
use ChoiceBuilderTrait; // Enable choices building
/**
* @var UriFactory
*/
private $uriFactory;
public function __construct(?RegistryInterface $registry = null, ?UriFactory $uriFactory = null)
{
parent::__construct($registry);
$this->uriFactory = $uriFactory ?? new DefaultUriFactory();
}
// You can define custom builder methods for constrains or
public function host(string $hostName): self
{
return $this->satisfy(function (?UriInterface $uri) use($hostName) {
if ($uri->getHost() !== $hostName) {
return 'Invalid host name';
}
});
}
// Create the element
protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer) : ElementInterface
{
return new UriElement($validator, $transformer, $this->getChoices(), $this->uriFactory);
}
}
// Register the element builder on the registry
// Use container to inject dependencies (here the UriFactoryInterface)
$registry->register(UriElement::class, function(Registry $registry) use($container) {
return new UriElementBuilder($registry, $container->get(UriFactoryInterface::class));
});
$builder->add('uri', UriElement::class);
class MyCustomChildBuilder extends ChildBuilder
{
public function __construct(string $name, ElementBuilderInterface $elementBuilder, RegistryInterface $registry = null)
{
parent::__construct($name, $elementBuilder, $registry);
// Add a filter provider
$this->addFilterProvider([$this, 'provideFilter']);
}
// Model transformer helper method
public function saveAsCustom()
{
return $this->modelTransformer(new MyCustomTransformer());
}
// Provide default filter
protected function provideFilter()
{
return [new MyFilter()];
}
}
// Now you can register the child builder with the element builder
$registry->register(CustomElement::class, CustomElementBuilder::class, MyCustomChildBuilder::class);
if (!$form->error()->empty()) {
// Simple render errors as string
return new Response('<div class="alert alert-danger">'.$form->error().'</div>');
// For simple API error system, use toArray()
return new JsonReponse(['error' => $form->error()->toArray()]);
// Or use accessors
foreach ($form->error()->children() as $name => $error) {
echo $error->field().' : '.$error->global().' ('.$error->code().')'.PHP_EOL;
}
}
/**
* Print errors in format [httpField => ['code' => errorCode, 'message' => errorMessage]]
*/
class ApiPrinter implements \Bdf\Form\Error\FormErrorPrinterInterface
{
private $errors = [];
private $current;
// Define the error message for the current element
public function global(string $error) : void
{
$this->errors[$this->current]['message'] = $error;
}
// Define the error code for the current element
public function code(string $code) : void
{
$this->errors[$this->current]['code'] = $code;
}
// Define the error element http field
public function field(\Bdf\Form\Child\Http\HttpFieldPath $field) : void
{
$this->current = $field->get();
}
// Iterate on element's children
public function child(string $name,\Bdf\Form\Error\FormError $error) : void
{
$error->print($this);
}
// Get all errors
public function print()
{
return $this->errors;
}
}
// Usage
return new JsonReponse(['errors' => $form->error()->print(new ApiPrinter())]);
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.