Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proper Support For Laravel Forms #2

Open
fulopattila122 opened this issue Oct 7, 2018 · 0 comments
Open

Proper Support For Laravel Forms #2

fulopattila122 opened this issue Oct 7, 2018 · 0 comments
Assignees

Comments

@fulopattila122
Copy link
Collaborator

Problem

Scenario

  • Using an enum on a model class;
  • Using the Laravel Forms package;
  • Invoking the form with Form::model();
  • Rendering radio buttons within the form.

Blade Example:

{!! Form::model($issue, ['route' => ['stift.issue.update', $issue], 'method' => 'PUT']) !!}

@foreach($statuses as $status => $statusLabel)
    <label class="radio-inline" for="status_{{ $status }}">
        {{ Form::radio('status', $status, ($issue->status->value() == $status), ['id' => "status_$status"]) }}
        {{ $statusLabel }}
    </label>
@endforeach

{!! Form::close() !!}

Behavior

  • Since the form is using the model binding, the 3rd parameter ($checked) of Form::radio() actually gets ignored.
  • The checked state gets calculated by comparing the value of Form::radio()'s second parameter ($value) and the value of the field retrieved from the model.
  • The model will return an enum object as the field value.
  • When comparing the string with the object, PHP uses Enum::__toString() method that returns the label of the enum and not the value.
  • It results that the radio buttons are never checked.

Expected Behavior

The radio buttons are checked if the value of the model matches the value of the radio button.

NOTE: the problem definitely applies to checkboxes (although that's not a typical use case) and most probably to selects as well.

Possible Solutions

Variant 1

Laravel Forms library has an undocumented feature. If the model has a getFormValue() method, then the value of the form will be obtained from that method.

This can be used on any model like this:

class Issue
{
    public function getFormValue(string $key)
    {
        if ($this->isEnumAttribute($key)) {
            return $this->{$key}->value();
        }

        return $this->{$key};
    }
}

This works immediately, but it has to be added to every single model manually.
As a generic solution this library should have another trait eg. CastedEnumsSupportForms

The trait should define the getFormValue($key) method shown above.

Problems With Variant 1

This works but there are some problems when you want to define your own getFormValue method on the model:

  • the model classes own getFormValue() method will override the trait's method with the same name.
  • as a result the behavior will be the same as the one we have now.

Possible workarounds:
Import the method from the trait with a different name:

class Model
{
    use CastsEnums, CastedEnumsSupportForms {
        CastedEnumsSupportForms::getFormValue as private getEnumFormValue;
    }

    public function getFormValue($key)
    {
        if ('myvalue' == $key) {
            return 'whatever';
        }

        return $this->getEnumFormValue($key);
    }
}

The only problem with that is that it's fragile because the models need to be aware of this bevaior.

Variant 2

Using form model accessors: https://laravelcollective.com/docs/5.4/html#form-model-accessors
I haven't elaborated this, but it should be taken into consideration as well.

@fulopattila122 fulopattila122 self-assigned this Aug 26, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant