Nova Customisable Resource Fields

This week I'm back working on Laravel Nova, which includes working my way through the nova-issues repo and seeing what's what.

One issue that caught my eye was a feature request from @Grayda, https://github.com/laravel/nova-issues/issues/2622. They asked that we provide functionality in Nova that allows users to show or hide fields based on their selection.

I immediately had a feeling this may be possible using the Filters feature of Nova, so I gave it a quick go and it worked!

Demo

The Code

First thing's first, we start by creating a boolean filter:

php artisan nova:filter FieldsFilter --boolean

We use a boolean filter here so that we can display checkboxes to the user:

Filters

Now that we have our filter, we can start implementing the logic that we need. Within the options method, we define the array of fields that we want the user to be able to toggle:

/**
 * Get the filter's available options.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return array
 */
public function options(Request $request)
{
    return [
        'Name' => 'name',
        'Gravatar' => 'gravatar',
        'Email' => 'email',
    ];
}

Note that with this method the options array uses a value/key syntax.

If we want to enable these fields by default, we'll also need to override the default method. You can do this by adding this code to your filter:

/**
 * Set the default options for the filter.
 *
 * @return array
 */
public function default()
{
    return [
        'name' => true,
        'gravatar' => true,
        'email' => true,
    ];
}

Once we've decided which fields the user should be allowed to toggle, we need to add the filter to our Resource. In the examples I've used above, we're modifying the User resource:

use App\Nova\Filters\FieldsFilter;

/**
 * Get the filters available for the resource.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return array
 */
public function filters(Request $request)
{
    return [
        new FieldsFilter,
    ];
}

We're almost there, stay with me!

All we need to do now is toggle the field if the user wants to display it. Usually we'd use the fields method to describe the fields that you wish to display to a user. Since we only want to allow users to customise which fields are displayed on the index listing, we can use the fieldsForIndex method instead. This is a two part change. Firstly, we need to store the FieldsFilter in a variable so we can access it later:

/**
 * Get the fields displayed by the resource index.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return array
 */
public function fieldsForIndex(Request $request)
{
    $fieldFilter = $request->filters()->first(function ($filter) {
        return $filter->filter instanceof FieldsFilter;
    });

    return [
        // Existing fields...
    ];
}

And finally, we need to modify our fields so that we hide it if the user no longer wants to see it:

return [
    ID::make()->sortable(),

    Gravatar::make('Gravatar')
            ->showOnIndex(Arr::get($fieldFilter->value, 'gravatar', true)),

    Text::make('Name')
        ->showOnIndex(Arr::get($fieldFilter->value, 'name', true)),

    Text::make('Email')
        ->sortable()
        ->showOnIndex(Arr::get($fieldFilter->value, 'email', true)),
];

Notice above how our fields don't contain any rules or help calls? This is because the index listing doesn't need to know any of that! We can make this method a lot cleaner than the usual fields method.

And we're done! Your users can now select which fields they want to display. You could take this further and have fields which are hidden by default, but then allow users to display them.

As a side note, you should be aware that each change will result in a fresh network request. This is required by Nova as changes to filters may result in a callback being handled differently, as demonstrated above with the use of $request->filters.