b2pweb / bdf-form-attribute
Declaring forms using PHP 8 attributes and typed properties, over bdf-form
Requires
- php: ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0
- b2pweb/bdf-form: ~1.1
- nette/php-generator: ~3.6|~4.0
Requires (Dev)
- phpunit/phpunit: ~9.5
- squizlabs/php_codesniffer: ~3.6.1
- symfony/security-csrf: ~5.4|~6.1
- vimeo/psalm: ~5.22 | ~6.0
This package is auto-updated.
Last update: 2024-12-03 14:39:28 UTC
README
Declaring forms using PHP 8 attributes and typed properties, over BDF form
Usage
Install using composer
composer require b2pweb/bdf-form-attribute
Declare a form class
To create a form using PHP 8 attributes, first you have to extend AttributeForm.
Then declare all input elements and buttons as property :
- For element :
public|protected|private MyElementType $myElementName;
- For button :
public|protected|private ButtonInterface $myButton;
Finally, use attributes on properties (or form class) for configure elements, add constraints, transformers...
#[Positive, UnitTransformer, GetSet] public IntegerElement $weight;
Adaptation of example from BDF Form : Handle entities
use Bdf\Form\Attribute\Form\Generates; use Bdf\Form\Leaf\StringElement; use Symfony\Component\Validator\Constraints\NotBlank; use Bdf\Form\Attribute\Child\GetSet; use Bdf\Form\Attribute\AttributeForm; use Bdf\Form\Leaf\Date\DateTimeElement; use Bdf\Form\Attribute\Element\Date\ImmutableDateTime; use Bdf\Form\Attribute\Child\CallbackModelTransformer; use Bdf\Form\ElementInterface; // Declare the entity class Person { public string $firstName; public string $lastName; public ?DateTimeInterface $birthDate; public ?Country $country; } #[Generates(Person::class)] // Define that PersonForm::value() should return a Person instance class PersonForm extends AttributeForm // The form must extend AttributeForm to use PHP 8 attributes syntax { // Declare a property for declare an input on the form // The property type is used as element type // use NotBlank for mark the input as required // GetSet will define entity accessor #[NotBlank, GetSet] private StringElement $firstName; #[NotBlank, GetSet] private StringElement $lastName; // Use ImmutableDateTime to change the value of birthDate to DateTimeImmutable #[ImmutableDateTime, GetSet] private DateTimeElement $birthDate; // Custom transformer can be declared with a method name as first parameter of ModelTransformer // Transformers methods must be declared as public on the form class #[ImmutableDateTime, CallbackModelTransformer(toEntity: 'findCountry', toInput: 'extractCountryCode'), GetSet] private StringElement $country; // Transformer used when extracting input value from entity public function findCountry(Country $value, ElementInterface $element): string { return $value->code; } // Transformer used when filling entity with input value public function extractCountryCode(string $value, ElementInterface $element): ?Country { return Country::findByCode($value); } }
Supported attributes
This library supports various attributes types for configure form elements :
- Symfony validator's
Constraint
, translated as...->satisfy(new Constraint(...))
ExtractorInterface
, translated as...->extractor(new Extractor(...))
HydratorInterface
, translated as...->hydrator(new Hydrator(...))
FilterInterface
, translated as...->filter(new Filter(...))
TransformerInterface
, translated as...->transformer(new Transformer(...))
Generate the configurator code from attributes
To improve performance, and to do without the use of reflection, attributes can be used to generate the PHP code of the configurator, instead of dynamically configure the form.
To do that, use CompileAttributesProcessor
as argument of form constructor.
const GENERATED_NAMESPACE = 'Generated\\'; const GENERATED_DIRECTORY = __DIR__ . '/var/generated/form/'; // Configure the processor by setting class and file resolvers $processor = new CompileAttributesProcessor( fn (AttributeForm $form) => GENERATED_NAMESPACE . get_class($form) . 'Configurator', // Retrieve the configurator class name from the form object fn (string $className) => GENERATED_DIRECTORY . str_replace('\\', DIRECTORY_SEPARATOR, $className) . '.php', // Get the filename of the configurator class from the configurator class name ); $form = new MyForm(processor: $processor); // Set the processor on the constructor $form->submit(['firstName' => 'John']); // Directly use the form : the configurator will be automatically generated // You can also pre-generate the form configurator using CompileAttributesProcessor::generate() $processor->generate(new MyOtherForm());