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

digital-creative/nova-range-input-filter

A Laravel Nova filter that adds a numeric range input to resource indexes, letting users filter records by minimum and maximum values. Easy to configure, it provides a clean range UI for prices, quantities, scores, and other numeric fields, with no custom filter code required.

View on GitHub
Deep Wiki
Context7

Getting Started

Verify the foundation before you write a line of code. This package is a Nova filter component — it has zero value outside a Nova admin panel. Confirm two things first:

  1. You're on Nova 4. v1.1.0 is the Nova 4 compatibility release (PR #39). Nova 5 is not addressed by any tagged release.
  2. You're on a Laravel version that satisfies the package's composer.json constraints.
composer require dcasia/nova-range-input-filter:^1.1

Pin it. There is effectively one meaningful tag (v1.1.0) whose entire changelog is "Upgrade Nova 4 / Fix various issues." Don't float the constraint — treat it as a fixed dependency and read the upstream diff whenever you bump.

Read the source before the README. The README is thin. Open vendor/dcasia/nova-range-input-filter/src and read two files:

  • The base filter class — its namespace, the $component it declares, and any constructor or config setters for min/max/step/placeholders.
  • Whether it ships a default apply(). That method is the real specification for what the filter does to your query.

That source read is your first hour of work and saves you a day of guessing later. The single most important thing to confirm: the exact shape of $value when apply() is called (array keys, nulls, partial payloads).

First use case: a numeric band on a resource index. The canonical shape is a filter class in app/Nova/Filters that subclasses the package filter and owns the query semantics.

// app/Nova/Filters/OrderTotalRange.php
namespace App\Nova\Filters;

use DigitalCreative\NovaRangeInputFilter\RangeInputFilter; // confirm exact namespace in src
use Illuminate\Http\Request;

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

    public function apply(Request $request, $query, $value)
    {
        $min = data_get($value, 'min');
        $max = data_get($value, 'max');

        if ($min === null && $max === null) {
            return $query;
        }

        if ($min !== null && $max !== null) {
            return $query->whereBetween('total', [(float) $min, (float) $max]);
        }

        return $min !== null
            ? $query->where('total', '>=', (float) $min)
            : $query->where('total', '<=', (float) $max);
    }
}

Register it on the resource:

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

That subclass-with-overridden-apply() shape is where you'll live day to day. The base class contributes the Vue range control; you contribute the query semantics.

Implementation Patterns

Treat apply() as the only source of truth. The range control is presentational. Every correctness concern — inclusivity, null handling, casting, unbounded sides — lives in that one method. Write the branches explicitly (min-only, max-only, both, neither) so each case is individually readable and testable. Never inherit a default apply() and assume it matches your product's definition of "in range."

Pick one query idiom per codebase and stick to it. Use whereBetween for fully-bounded ranges and paired >= / <= for half-open ranges. Mixing idioms across resources makes inclusivity and null behavior subtly inconsistent — which surfaces months later as "why did this filter hide that row?"

Cast deliberately for decimal or numeric-string columns. Cast $value to float/int inside apply() before building the constraint. Passing raw strings into whereBetween usually works but can produce surprising ordering on some engines. It's a one-line fix versus an hour of debugging.

Prefer config setters over subclassing for the simple majority. For commodity price/quantity screens you often don't need a subclass at all — instantiate the package filter with its min/max/step options and let the default behavior run. Reserve subclassing for custom null handling, casts, or operators. Less code means fewer places to inherit a Nova upgrade surprise.

Seed a sensible opening window with ->default([...]). Nova's ->default([...]) gives the filter an initial value so the panel opens on a sane band rather than an empty control. Useful on ops screens where users almost always narrow.

Compose freely with other filters. Nova runs every active filter's apply() sequentially against the same builder. This package touches nothing outside apply(), so stacking it with a search filter, a status filter, or a second range filter on a different column (price + stock) is conflict-free. Just ensure each targets a distinct column.

Keep filter classes in app/Nova/Filters and test them like PHP classes. A cheap feature test — POST to the Nova index endpoint with the filter active and assert on returned IDs — catches most regressions, including behavior changes introduced by a version bump. Run it against all four value branches.

Formatting lives outside the query path. $name and ->withMeta() control what the panel displays. Leave numeric formatting to the Vue component rather than fighting it in PHP — it's cosmetic and doesn't affect filtering.

Gotchas and Tips

  • The Nova version gate is the real risk, not the integration. v1.1.0 targets Nova 4; Nova 5 has no upstream signal. If you're facing a Nova 5 upgrade, don't force it with --ignore-platform-reqs — plan to fork. The code is small (a PHP class plus precompiled Vue), so a day's work is realistic. Smoke-test against your exact Nova 4 minor, not just "Nova 4."
  • Read the v1.0 → v1.1.0 diff before trusting it. "Fix various issues" is precisely the category of change that can silently alter bound inclusivity or null handling. Enumerate the diff when you bump instead of inheriting prior assumptions.
  • Empty-side semantics need an explicit product decision. Does a blank min mean "unbounded" or "zero"? The package may coerce one way; your apply() should decide deliberately. Test min-only, max-only, both, and neither — those four cases are where filtered listings go wrong.
  • whereBetween is inclusive on both ends and drops NULL rows. Correct for "price between $50 and $200"; wrong if NULL should render as "unknown, show anyway." Handle NULLs explicitly when the product cares.
  • Index the filtered column. A range filter on an unindexed column turns a cheap admin screen into a table scan. Run EXPLAIN once on a realistic dataset. On large or revenue-adjacent tables this is not optional, and the filter will never warn you.
  • Guard the value shape everywhere. Don't assume $value is always a two-key array. A cleared filter, a partial submit, or a programmatic call can hand you null or a partial payload. Use data_get() with defaults at every read.
  • Don't repurpose it for date ranges without verification. It's built and documented as a numeric range input. Date columns carry different equality, timezone, and format semantics — confirm support in the source or write a dedicated date filter.
  • Forking is cheap and often correct. Single maintainer, ~17 stars, zero downstream dependents, and a long gap before v1.1.0. If you carry this across more than a couple of resources or on a revenue-adjacent screen, vendor the files or fork into an internal namespace so Nova upgrades and bug fixes become your controlled problem rather than an upstream-waiting one. MIT keeps that clean.
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.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings