simplesquid / nova-enum-field

An enum field and filters for Laravel Nova.
MIT License
52 stars 26 forks source link

nullable() not respected by Enum::make #37

Closed nathan-io closed 2 years ago

nathan-io commented 2 years ago

Example:

Enum::make('Reason', 'reason')->attach(ReasonType::class)->sortable()->nullable(),

On forms, this field is still required, and the form won't submit without a value.

mdpoulter commented 2 years ago

Looks like a duplicate of #17.

nathan-io commented 2 years ago

Thanks Matthew. I took a look at #17 and #172. I'm not understanding the relevance of #172 to this issue.

The purpose of #172 seems to be to support instantiation of enums from null, which is not what I'm talking about here.

We're (myself and the author of #17) just talking about supporting the option to pass no value for the enum field when submitting the form, so we can keep a nullable/optional enum attribute null if the user didn't wish to provide a value.

mdpoulter commented 2 years ago

What would you want to store in the database in that instance?

nathan-io commented 2 years ago

If the enum attribute's column is nullable in the table (the only scenario I'm aware of where we'd use nullable() in the Nova resource) I'm not understanding why we have to pass a value to store at all.

It would be just like any other nullable field. They didn't provide a value, so we don't set it on store or update. It just stays null

mdpoulter commented 2 years ago

The problem with that approach is that when it's then read from the database (as null), it would throw an error (BenSampo/laravel-enum#172).

I think the better approach would be to create an explicit "null" or "none" value:

final class ReasonType extends Enum
{
    const None = 0;
    const ReasonOne = 1;
    const ReasonTwo = 2;
}
nathan-io commented 2 years ago

I think the better approach would be to create an explicit "null" or "none" value:

I respectfully disagree. If this is the solution, then why not just get rid of null in every RDMS and use this "0 is the new null" pattern on every column and data type we previously had as nullable?

Of course not. Clearly null is incredibly useful, and I'd certainly like to be able to leverage it here.

The problem with that approach is that when it's then read from the database (as null), it would throw an error (BenSampo/laravel-enum#172).

In Tinker at least, I'm not getting an exception when accessing a null enum attribute or saving a model with a null enum attribute - and that's without any safety checks!

Psy Shell v0.10.8 (PHP 8.0.9 — cli) by Justin Hileman
>>> $ps = App\Models\PassStatus::first();
=> App\Models\PassStatus {#4674
     id: 1,
     pass_id: 1,
     status: 1,
     reason: null,
     setter_type: "App\Models\User",
     setter_id: 1,
     created_at: "2021-09-25 21:06:00",
     updated_at: "2021-09-25 21:06:00",
   }
>>> $ps->reason
=> null
>>> $ps->reason->value
<warning>PHP Warning:  Attempt to read property "value" on null in Psy Shell code on line 1</warning>
=> null
>>> $ps->save()
=> true

So the worst I got a warning. If this were production code, I'd have implemented safety checks. in whatever view, controller, etc.

Now I go into TablePlus and change the reason value for that record to 1. Then continue in the same Tinker session:

>>> $ps->refresh()
=> App\Models\PassStatus {#4674
     id: 1,
     pass_id: 1,
     status: 1,
     reason: 1,
     setter_type: "App\Models\User",
     setter_id: 1,
     note: null,
     created_at: "2021-09-25 21:06:00",
     updated_at: "2021-09-25 21:06:00",
   }
>>> $ps->reason
=> App\Enums\PassStatus\ReasonType {#4741
     +value: 1,
     +key: "Chargeback",
     +description: "Chargeback",
   }
>>>

And to confirm, I do have the cast in the model:

use App\Enums\PassStatus\StatusType;
use App\Enums\PassStatus\ReasonType;

// ...

    protected $casts = [
        'status' => StatusType::class,
        'reason' => ReasonType::class,
    ];

IMO, if any attribute's corresponding table column - regardless of type - is nullable() in the migration, the responsibility lies on the developer to handle safety checks and validation elsewhere throughout the application.

And at least looking at the CLI example above, there's nothing preventing saving a null value to an enum attribute, or accessing a null enum-casted value.


In one of my current projects, we're using enums in probably a dozen models. Some are nullable, and we've had no issues. Then again, we have safety checks on any nullable model attribute, which I think is just to be expected as a best practice.

By the way, I do want to say I'm very grateful for this package, it's been incredibly useful for me lately, and saved time in multiple projects. I appreciate it!

mdpoulter commented 2 years ago

That's interesting actually. It looks like support was added for casting null values (BenSampo/laravel-enum#152) - I missed that!

Feel free to PR a nullable() method with tests, otherwise I should have time to do it myself in the coming days.

nathan-io commented 2 years ago

I totally missed #152! It's been a busy week, but I could try to give it a shot if you're not able to carve out time in the next few days.