Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Nova Range Input Filter Laravel Package

juniora/nova-range-input-filter

Adds a range filter to Laravel Nova resource indexes, letting users narrow records by a numeric or date range through min and max inputs. Drop it into your filters array to filter listings by price, quantity, dates, and similar range-based columns.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and verify compatibility first. The package targets Laravel Nova, so confirm the Nova major version supported before anything else:

composer require juniora/nova-range-input-filter
# Check the constraints before you commit
cat vendor/juniora/nova-range-input-filter/composer.json | grep -A3 '"require"'
ls vendor/juniora/nova-range-input-filter/src
cat vendor/juniora/nova-range-input-filter/README.md

At zero stars and a single 2024-12-21 release, treat the README as advisory and the source as authoritative. Skim src/ for the base class name and its apply() / options() signatures — that is your real API.

First use case: a numeric column on an index screen where two separate Number filters (min/max) feel clunky — e.g. order totals, price, age, stock counts.

Generate a filter and swap the parent class for the package's one:

php artisan nova:filter OrderTotalRange
<?php

namespace App\Nova\Filters;

use Juniora\RangeInputFilter\RangeInputFilter; // confirm namespace in src/

class OrderTotalRange extends RangeInputFilter
{
    public $name = 'Order Total';

    public function apply($request, $value)
    {
        $value = (array) $value;
        $min = $value['min'] ?? null;
        $max = $value['max'] ?? null;

        return $this->builder
            ->when($min !== null && $min !== '', fn ($q) => $q->where('total', '>=', (float) $min))
            ->when($max !== null && $max !== '', fn ($q) => $q->where('total', '<=', (float) $max));
    }
}

Register it on the resource:

public function filters(NovaRequest $request)
{
    return [new OrderTotalRange];
}

Then load a Nova index page, drag the slider or type endpoints, and verify the generated query with ->dd() or Nova's query log before trusting it.

Implementation Patterns

Normalize the value shape in one place. Range filters typically deliver an array (['min' => '10', 'max' => '99']) or a JSON string depending on how the Vue component serializes. Write a small private helper on a shared base filter and reuse it, rather than repeating (array) casts in every filter:

protected function bounds($value): array
{
    $value = is_string($value) ? json_decode($value, true) : $value;
    $value = (array) $value;

    return [
        $this->normalize($value['min'] ?? null),
        $this->normalize($value['max'] ?? null),
    ];
}

private function normalize($v): ?float
{
    return ($v === null || $v === '') ? null : (float) $v;
}

Inclusive ranges with whereBetween are cleaner when both bounds exist:

[$min, $max] = $this->bounds($value);

return $this->builder->when(
    $min !== null && $max !== null,
    fn ($q) => $q->whereBetween('total', [$min, $max]),
    function ($q) use ($min, $max) {
        $q->when($min !== null, fn ($q) => $q->where('total', '>=', $min))
          ->when($max !== null, fn ($q) => $q->where('total', '<=', $max));
    }
);

Reuse one filter class across columns by adding a configurable column property, so you aren't copy-pasting eight near-identical filters:

class NumericRange extends RangeInputFilter
{
    public function __construct(public string $column, ?string $name = null)
    {
        parent::__construct();
        if ($name) { $this->name = $name; }
    }

    public function apply($request, $value) { /* uses $this->column */ }

    public function key() { return 'numeric-range-'.$this->column; }
}

Set sensible slider bounds via options() or meta (min/max/step) so the UI doesn't default to 0–100 on a column that ranges into the millions:

public function options(NovaRequest $request)
{
    return ['min' => 0, 'max' => 500000, 'step' => 100];
}

Confirm the exact meta keys in the package's Vue component — that is the contract.

Test apply() directly. Nova filters are plain classes; a unit test with a fake NovaRequest and a query builder against a SQLite table is faster and more reliable than clicking through the UI:

$filter = new OrderTotalRange;
$query = $filter->apply(NovaRequest::create('/', 'GET'), ['min' => 10, 'max' => 50]);
$this->assertStringContainsString('between', $query->toSql());

Compose with existing constraints. $this->builder already carries the resource query, so soft deletes, global scopes, and relationship constraints survive — just don't call ->newQuery() inside apply().

Gotchas and Tips

  • if ($value) will eat 0. A user legitimately filtering ">= 0" or maxing at 0 is a common case. Always compare with !== null && !== ''.
  • Everything arrives as a string. '10' >= '9' is true lexicographically in PHP in some comparisons; cast to (float) or (int) before it hits the query builder, and be explicit about whereBetween on decimal columns.
  • Empty inputs may be '', not null, and both endpoints can be empty simultaneously. Guard the fully-empty case and return the builder untouched, otherwise you'll apply >= '' and silently match nothing.
  • Filter values are round-tripped through the query string. Nova base64/JSON-encodes the filter value into the URL, so changing your expected array shape breaks bookmarks and any saved links. If you subclass for multiple columns, override key() to keep URLs stable and distinct.
  • Nova 5 asset compatibility is the main risk. Older filter packages ship a compiled dist/js built against Nova 4's mix/Vite pipeline; on Nova 5 the component can render blank or throw in the console. Open devtools first — if the component 404s or errors on Nova.booting, you'll need npm run dev in the package (or a fork) rather than debugging your PHP.
  • Publish assets if the component doesn't appear: php artisan vendor:publish --tag=nova-range-input-filter-assets (tag name per README) and php artisan nova:publish.
  • Zero dependents means you are the QA. Pin the version in composer.json, keep a one-file fork strategy in mind, and avoid extending deep into its internals — override apply() and options() only, and reimplement the ~30-line Vue component yourself if the package goes stale.
  • Debugging: dd($request->input('filters')) inside apply() shows exactly what Nova sent; ->ddSql() on the returned builder confirms the generated SQL. That pair resolves nearly every "the filter does nothing" report in under a minute.
  • Filters only apply to index and relation screens, never to detail pages or metrics. If you need the same range logic in a card or metric, extract it to a query scope on the model and call it from both places.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource