Empty values for POSTed null select option values

3 years ago

helvete
Member | 12
+
0
-

Hello!

I have a form, where users are about to select value from select box. The select box reflects database foreign key column, which allows nulls. Let's call it hobby_id.

Now when I generate the form, All hobbies are added as available option plus one extra option of value null and text 'N/A' is also added. Obviously the user can have no hobbies.

The post value for the empty option is empty and evaluated to '' when processing form values via $form->getValues(). And here comes the problem. The database column is of type INT while allowing null value, but the empty string ('') gets autoretyped to 0 which bothers me.

I know I can traverse the values array and retype the FK empty string values into nulls, but obviously I don't like this solution.

I have tried to add a condition to the select control to check if it is filled and added filter to return null in case of '', but the default validation comes in and evaluates the select as invalid even before the filter is applied.

Is there some sane solution how to configure the select element to behave like this?

Thanks for any insights in advance.

Edit:
I have read these, but nothing hints any nice solution:
https://forum.nette.org/…zdne-hodnoty
https://forum.nette.org/…na-condition
https://github.com/…/issues/1088
https://forum.nette.org/…ce-formularu
..and many more

Last edited by helvete (2015-11-06 11:35)

3 years ago

greeny
Member | 406
+
0
-

I assume you have validation similar to this:

$form->addSelect(...)
    ->addRule($form::INTEGER); // or $form::FLOAT

You can then change it to this:

$form->addSelect(...)
    ->addCondition($form::FILLED) // rules applies only if the field is filled, e.g. not empty string
        ->addRule($form::INTEGER);

Then validation vill accept empty string and you will have empty string in application's code.

And in your form's success handler just do something like this:

public function formSuccess($form, $values)
{
    if ($values->hobby_id === '') {
        $values->hobby_id = NULL;
    }

    $this->userRepo->persist($values); // replace with your saving code
}

3 years ago

helvete
Member | 12
+
0
-

Thanks for quick answer.

I knew it can be done like this but I presumed, there is some nicer solution available, since this situation is like super common.

Anyway, thanks for straightening the possibilities up for me;)

3 years ago

greeny
Member | 406
+
0
-

This situation is common, but in most big projects, you are not using Nette/Database, but Doctrine. In Doctrine, you add mapping to Entity, so foreign keys are not used, instead you access entities directly. So your code would look like this:

public function formSuccess($form, $values)
{
    $this->user->setHobby($this->hobbyRepo->find($values->hobby_id)); // obviously, there is no Hobby with id = '', so NULL is returned
    $this->em->persist($user);
}

Thats why not much users are actually solving issues like this.

3 years ago

helvete
Member | 12
+
0
-

Yeah, I am quite experienced with doctrine, but we switched of off it (Zend+Doctrin) in favour of Nette+NetteDatabase because of Doctrine's enormous resources usage inefficiency and because of currently increasing amount of smaller projects we work on.

I will implement similar solution as you suggest on the parent-most level model class.

Edit: In the end solved by adding lines like this into model insert and update methods.

// strip empty FK column field values
foreach ($values as $key => $value) {
    if (!preg_match('/_id$/', $key)) {
        continue;
    }
    if ($value === '') {
        unset($values[$key]);
    }
}

Thanks for insight.

Last edited by helvete (2015-11-06 13:08)