b2pweb/bdf-form-attribute

Declaring forms using PHP 8 attributes and typed properties, over bdf-form

dev-master / 1.0.x-dev 2024-12-03 14:39 UTC

This package is auto-updated.

Last update: 2024-12-03 14:39:28 UTC


README

build Scrutinizer Code Quality Code Coverage Packagist Version Total Downloads Type Coverage

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 :

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());

Available attributes

On form class

On button property

On element property