Angelov
Angelov

Reputation: 168

Symfony3 Form component trying to pass null to a type hinted method in PHP 7

In my entity class I have defined all the expected argument types for the setters and return types of the getters. Later, when I have a form which uses the said class, I get an error if some of the fields in the form is empty because the form component tries to pass null to the setter instead of string.

I get the following exception when I submit the form:

Expected argument of type "string", "NULL" given

500 Internal Server Error - InvalidArgumentException

The exception is thrown from vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php at line 254

Is there a way to convert the "null" value to empty string before passing it to the object, and let the validator argue about it?

Upvotes: 13

Views: 3596

Answers (2)

gp_sflover
gp_sflover

Reputation: 3500

If an Entity Property cannot be null (and you use PHP 7.1+), then also the application of the nullable return type declaration sounds more like a dirty and fast workaround to maintain a direct data binding between Entities and Forms (using the Symfony Form Component).

A better global approach (in my opinion) is to decouple the Form data binding from your Entities using a DTO (Data Transfer Object), that is a simple POPO (Plain Old PHP Object) to contain your form data.

Using a DTO will allow you to maintain a strict type hinting in your Entities (no loss of data consistency) and will decouple Form data binding (but also data validation) from your Entities.

DTO's allows reusability and have many other advantages.

Some useful references about the use of DTO's with Symfony Forms:

Upvotes: 11

HPierce
HPierce

Reputation: 7409

I see two options here:

Quick and Dirty - make the argument passed to the setter optional:

public function setTitle(String $title = null)
{
    $this->title = $title;
    return $this;
}

Probably better - use a data transformer in the FormType:

Data transformers allow you to modify the data before it gets used.

   $builder
    // ...
        ->add('title', 'text')
    // ...
    ;

    $builder->get('title')->addModelTransformer(new CallbackTransformer(
        function($originalInput){
            return $string;
        },
        function($submittedValue){ 
            // When null is cast to a string, it will be empty.
            return (string) $submittedValue;
        }
    ));

I've posted another answer before using this to retrieve method to retrieve an Entity object before. See that if it helps to see a more complicated example.

Upvotes: 5

Related Questions