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.
<?phprequire_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
b2pweb / bdf-form example snippets
namespaceApp\Form;
useBdf\Form\Aggregate\FormBuilderInterface;
useBdf\Form\Custom\CustomForm;
classLoginFormextendsCustomForm{
protectedfunctionconfigure(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 validif (!$form->submit($_POST)->valid()) {
// The form has an error : use `ElementInterface::error()` to get the error and render itecho'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 formclassPerson{
/** @var string */public $firstName;
/** @var string */public $lastName;
/** @var DateTimeInterface|null */public $birthDate;
/** @var Country|null */public $country;
}
classPersonFormextends \Bdf\Form\Custom\CustomForm{
protectedfunctionconfigure(\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')->
classPersonControllerextendsController{
private $repository;
// Get a form view with entity valuespublicfunctioneditForm($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 setreturn$this->render('person/form', ['form' => $view]);
}
// Use the form to create the entitypublicfunctioncreate($request){
// Get the form instance
$form = new PersonForm();
// Submit form dataif (!$form->submit($request->post())->valid()) {
thrownew FormError($form->error());
}
// $form->value() will return the filled entity$this->repository->insert($form->value());
}
// Update an existent entity: simply attach the entity to fillpublicfunctionupdate($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 dataif (!$form->submit($request->post())->valid()) {
thrownew 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()'edpublicfunctionpatch($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 dataif (!$form->patch($request->post())->valid()) {
thrownew FormError($form->error());
}
// $form->value() will return the filled entity$this->repository->insert($form->value());
}
}
classMyFormextendsCustomForm{
protectedfunctionconfigure(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()
;
}
}
classUserFormextends \Bdf\Form\Custom\CustomForm{
protectedfunctionconfigure(\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')->
classCredentialsFormextends \Bdf\Form\Custom\CustomForm{
protectedfunctionconfigure(\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')->
classUserFormextends \Bdf\Form\Custom\CustomForm{
protectedfunctionconfigure(\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 helperclassCredentialsFormextends \Bdf\Form\Custom\CustomForm{
protectedfunctionconfigure(\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 fieldif ($value !== \Bdf\Form\Util\FieldPath::parse('password')->value($input)) {
return'Confirm must be same as password';
}
})
;
}
}
// Using FieldFinderTrait on custom formclassCredentialsFormextends \Bdf\Form\Custom\CustomForm{
use \Bdf\Form\Util\FieldFinderTrait;
protectedfunctionconfigure(\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 formif ($value !== $this->findFieldValue('password')) {
return'Confirm must be same as password';
}
})
;
}
}
useBdf\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 elementswitch ($btn = $form->root()->submitButton() ? $btn->name() : null) {
case MyForm::SAVE_BTN:
doSave($form->value());
break;
case MyForm::DELETE_BTN:
doDelete($form->value());
break;
default:
thrownewException();
}
$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){ ... })
;
useBdf\Form\Aggregate\FormBuilderInterface;
useBdf\Form\Custom\CustomForm;
useBdf\Form\ElementInterface;
useBdf\Form\Transformer\TransformerInterface;
// Declare the element classclassUriElementextendsCustomForm{
const INNER_ELEMENT = 'uri';
/**
* @var UriFactoryInterface
*/private $uriFactory;
// Set the UriFactoryInterface at the constructorpublicfunction__construct(?FormBuilderInterface $builder = null, ?UriFactoryInterface $uriFactory = null){
parent::__construct($builder);
$this->uriFactory = $uriFactory ?? new DefaultUriFactory(); // Default instance for the factory
}
protectedfunctionconfigure(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(newclassimplementsTransformerInterface{
publicfunctiontransformToHttp($value, ElementInterface $input){
// $value is an array of children value// Return only the inner element valuereturn $value[UriElement::INNER_ELEMENT];
}
publicfunctiontransformFromHttp($value, ElementInterface $input){
// $value is the URI string// Map it as array to the inner elementreturn [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());
});
}
}
useBdf\Form\Choice\ChoiceInterface;
useBdf\Form\Leaf\LeafElement;
useBdf\Form\Transformer\TransformerInterface;
useBdf\Form\Validator\ValueValidatorInterface;
// Declare the element using LeafElement classclassUriElementextendsLeafElement{
/**
* @var UriFactory
*/private $uriFactory;
// Overrides constructor to add the factorypublicfunction__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 factoryprotectedfunctiontoPhp($httpValue): ?UriInterface{
return $httpValue ? $this->uriFactory->createUri($httpValue) : null;
}
// Stringify the UriInterface instanceprotectedfunctiontoHttp($phpValue): ?string{
return $phpValue === null ? null : (string) $phpValue;
}
}
useBdf\Form\AbstractElementBuilder;
useBdf\Form\Choice\ChoiceBuilderTrait;
useBdf\Form\ElementInterface;
// Declare the builderclassUriElementBuilderextendsAbstractElementBuilder{
useChoiceBuilderTrait; // Enable choices building/**
* @var UriFactory
*/private $uriFactory;
publicfunction__construct(?RegistryInterface $registry = null, ?UriFactory $uriFactory = null){
parent::__construct($registry);
$this->uriFactory = $uriFactory ?? new DefaultUriFactory();
}
// You can define custom builder methods for constrains or publicfunctionhost(string $hostName): self{
return$this->satisfy(function(?UriInterface $uri)use($hostName){
if ($uri->getHost() !== $hostName) {
return'Invalid host name';
}
});
}
// Create the elementprotectedfunctioncreateElement(ValueValidatorInterface $validator, TransformerInterface $transformer) : ElementInterface{
returnnew 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){
returnnew UriElementBuilder($registry, $container->get(UriFactoryInterface::class));
});
$builder->add('uri', UriElement::class);
classMyCustomChildBuilderextendsChildBuilder{
publicfunction__construct(string $name, ElementBuilderInterface $elementBuilder, RegistryInterface $registry = null){
parent::__construct($name, $elementBuilder, $registry);
// Add a filter provider$this->addFilterProvider([$this, 'provideFilter']);
}
// Model transformer helper methodpublicfunctionsaveAsCustom(){
return$this->modelTransformer(new MyCustomTransformer());
}
// Provide default filterprotectedfunctionprovideFilter(){
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 stringreturnnew Response('<div class="alert alert-danger">'.$form->error().'</div>');
// For simple API error system, use toArray()returnnew JsonReponse(['error' => $form->error()->toArray()]);
// Or use accessorsforeach ($form->error()->children() as $name => $error) {
echo $error->field().' : '.$error->global().' ('.$error->code().')'.PHP_EOL;
}
}
/**
* Print errors in format [httpField => ['code' => errorCode, 'message' => errorMessage]]
*/classApiPrinterimplements \Bdf\Form\Error\FormErrorPrinterInterface{
private $errors = [];
private $current;
// Define the error message for the current elementpublicfunctionglobal(string $error) : void{
$this->errors[$this->current]['message'] = $error;
}
// Define the error code for the current elementpublicfunctioncode(string $code) : void{
$this->errors[$this->current]['code'] = $code;
}
// Define the error element http fieldpublicfunctionfield(\Bdf\Form\Child\Http\HttpFieldPath $field) : void{
$this->current = $field->get();
}
// Iterate on element's childrenpublicfunctionchild(string $name,\Bdf\Form\Error\FormError $error) : void{
$error->print($this);
}
// Get all errorspublicfunctionprint(){
return$this->errors;
}
}
// Usagereturnnew 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.