Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Laravel Nova isn't using the array key as the option value in filters

I created this filter for Nova v4.32.11:

class AccountFilter extends Filter
{
    public function apply(Request $request, $query, $value)
    {
        return $query->where('account_id', $value);
    }

    public function options(Request $request)
    {
        return Account::all()
            ->sortBy('name')
            ->mapWithKeys(function ($account) {
                return [$account->id => $account->name];
            })
            ->toArray();
    }
}

If I dd() the array being returned in options() it looks like this:

[
    1 => 'Foo',
    2 => 'Bar',
    3 => 'Baz',
]

However, if I inspect the element in Nova I see this for the select options:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

<select dusk="Account Filter-select-filter" class="w-full block form-control form-select form-control-sm form-select-bordered">
<option value="">—</option>
<option value="Foo">Foo</option>
<option value="Bar">Bar</option>
<option value="Baz">Baz</option>
</select>

If I dd() the $value in apply() I see that value is indeed the account name instead of the ID as I’d expect from the array returned for the options list.

What is wrong here?

>Solution :

It seems like you need to use the account ID for filtering, but Laravel Nova is using the account name instead. One workaround is to use a custom component for the filter. This way, you can control the select options’ value and display text separately.

First, you need to create a new Vue component. This component will be used for the filter. In this component, you can specify the value and the display text of the select options separately.

Here is an example of how you can create this component:

<template>
    <select class="form-control form-select" v-model="value">
        <option v-for="(name, id) in options" :value="id">{{ name }}</option>
    </select>
</template>

<script>
export default {
    props: ['options', 'value'],
}
</script>

Next, you need to register this component in your Nova service provider:

public function boot()
{
    parent::boot();

    Nova::script('custom-filter', __DIR__.'/../dist/js/filter.js');
    Nova::style('custom-filter', __DIR__.'/../dist/css/filter.css');

    Nova::provideToScript([
        'custom-filter' => new CustomFilter,
    ]);
}

Finally, you can use this component in your AccountFilter class:

class AccountFilter extends Filter
{
    public function apply(Request $request, $query, $value)
    {
        return $query->where('account_id', $value);
    }

    public function options(Request $request)
    {
        return Account::all()
            ->sortBy('name')
            ->mapWithKeys(function ($account) {
                return [$account->id => $account->name];
            })
            ->toArray();
    }

    public function components()
    {
        return [
            'custom-filter' => new CustomFilter,
        ];
    }
}

In this modified version, the apply method filters by the account ID, and the options method returns an array where the keys are the IDs and the values are the names. The components method specifies that the custom-filter component should be used for this filter. This way, the value selected in the Nova interface will match the value used in the apply method.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading