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

Filament Date Range Laravel Package

shemyart/filament-date-range

A Filament plugin that adds a date range picker component for admin panels and forms. Let users select start and end dates through a clean, intuitive interface, ideal for filtering reports, dashboards, and resource tables by custom time periods within Laravel Filament apps.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish

composer require <vendor>/<package>
php artisan vendor:publish --tag=<package>-config

The service provider is auto-discovered; only publish the config if you need to change defaults (date format, default range, timezone behaviour). Don't publish assets you won't edit — it makes future upgrades harder to diff.

Where to look first

  1. config/<package>.php — the default format, fallback range, and the list of accepted query keys live here.
  2. The filter classes directory (src/Filters or similar). This is the real API surface; the config is just wiring.
  3. The README's "Available filters" table — read this before writing your own, most day-one needs are already covered.

First use case

The canonical 10-minute win: an admin index page with a "From / To" date picker that filters a listing by created_at.

// routes/web.php
Route::get('/orders', OrderController::class);

// app/Http/Controllers/OrderController.php
public function __invoke(Request $request)
{
    $orders = Order::query()
        ->dateRange($request->only(['from', 'to'])) // registered scope/trait
        ->latest()
        ->paginate(25);

    return view('orders.index', compact('orders'));
}

Wire the form inputs to the query keys the package expects (from / to by default — confirm in config) and you have a working filter. Reach for the package's own validation rule or form request before hand-rolling date parsing.

Implementation Patterns

Scope-first, not controller-first. Apply the filter as a query scope or trait method so the same logic serves HTTP, queued exports, and Artisan commands. Keeping parsing in the request layer and application in the model layer means your export job can pass a CarbonPeriod directly without faking a request.

Compose rather than branch. Most implementations accept an array of filters, which lets you build the parameter set conditionally:

$filters = array_filter([
    'from'     => $request->date('from'),
    'to'       => $request->date('to'),
    'status'   => $request->input('status'),
    'customer' => $request->input('customer'),
]);

$orders = Order::query()->filters($filters)->paginate();

This scales better than if ($request->filled('from')) chains, and a single empty-array call is a harmless no-op.

Validate at the boundary, once. Put date validation in a FormRequest so both the index page and any JSON endpoint reject malformed input identically:

'from' => ['nullable', 'date', 'before_or_equal:to'],
'to'   => ['nullable', 'date', 'after_or_equal:from'],

Without before_or_equal, an inverted range silently returns zero rows and looks like a bug in the filter.

Sensible defaults beat empty screens. For dashboards, default to a rolling window (now()->subDays(30) to now()) and reflect it back into the form inputs. Users read an unfiltered table as "all data" and then misread totals.

Preserve filters across pagination and sorting. $orders->appends($request->query()) on the paginator, and make sort links carry the current date params. This is the single most common "the filter is broken" report and it's never the filter — it's the links.

Exports and reports. Reuse the same scope in an ExportsOrders job and pass an explicit CarbonPeriod so the export captures the range at dispatch time rather than resolving now() when the worker picks it up.

Testing. Use Carbon::setTestNow() for relative defaults, and cover these four cases explicitly: only from, only to, both, neither.

Gotchas and Tips

Inclusive vs. exclusive end dates. A to of 2024-03-31 sent as a bare date parses to midnight and silently drops that entire day. Either use endOfDay() on the bound or filter created_at < $to->copy()->addDay()->startOfDay(). The second form is index-friendly; the first can be too if your column is a DATETIME. Verify which one the package does rather than assuming.

Never use whereDate() on a large table. WHERE DATE(created_at) = ? is non-sargable and turns a range scan into a full scan. If you see that in a generated query, that's your performance problem. Check with ->toSql() and EXPLAIN before shipping.

Timezone drift. Laravel stores created_at in the app timezone (usually UTC) but users pick dates in their own. Parse input in the user's timezone, convert to the app timezone before querying. A filter that "misses the last 8 hours" for a European team is almost always this.

Ambiguous date formats. 03/04/2024 is March 4th or April 3rd depending on locale. Pass an explicit format (or accept ISO-8601 only) — never rely on strtotime heuristics. This bites hardest when a numeric-looking string parses "successfully" into the wrong month.

Empty strings vs. nulls. An HTML form submits from="" when cleared, which is not null. Normalise with array_filter() or $request->date() (which returns null for empty) before the filter sees it, otherwise you may get a validation failure or a 1970-01-01 bound.

Config caching. After changing the config, php artisan config:clear in local dev. In production, remember the deploy step — a cached config will ignore your new default range and there is no error message for this.

Extension points. Most packages of this shape let you register a custom filter class (implement the package's filter interface, then add it to the config array). Prefer that over subclassing the package's built-in filter — subclassing couples you to internal method signatures that change between minors.

Debugging checklist when a filter returns the wrong rows, in order: dump $request->query(); dump the parsed Carbon bounds; ->ddRawSql() the query; confirm the column's actual type (DATE vs DATETIME vs TIMESTAMP changes boundary behaviour); confirm the app timezone.

Upgrade discipline. Pin the major version. Date handling is exactly the kind of thing that gets "fixed" in a minor release (boundary semantics, default format), and such a change is silent — no exception, just different row counts. Re-run your four boundary tests after every update.

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.
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk