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 Havingable Laravel Package

webard/nova-havingable

Adds HAVING clause support to Laravel Nova, letting you filter resources on aggregated values such as counts, sums, and averages. Useful for queries that need conditions on grouped or computed columns, bringing SQL HAVING semantics into Nova's filtering layer.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install with composer require webard/nova-havingable. This is a Nova add-on, so confirm your laravel/nova major (4.x vs 5.x) and PHP version against the constraints in the package's composer.json before you pull it into an existing app — a v1.0.0 initial release usually targets a narrow range.
  • Where to look first, in order: the README usage snippet (it will show the exact trait/class name and method signature), then src/ for the trait or Nova filter/metric class you actually apply, then composer.json for auto-discovery and version pins. There is unlikely to be a publishable config or migration in an initial release; if src/ contains a service provider with publishes(), check whether anything is optional before assuming you need it.
  • First use case: filtering a Nova resource index or lens on an aggregate rather than a column — e.g. "show me customers whose SUM(orders.total) is over 10,000" or "products with more than 5 reviews." Nova's stock filters compile to WHERE clauses, so an aggregate predicate has nowhere to go. This package exists to let you express that HAVING condition through the Nova request pipeline instead of abandoning the filter UI for a custom lens with raw SQL.

Implementation Patterns

  • Attach the trait where the query is built. The package is designed to be opted into per resource/lens, not globally. Apply it to the Nova resource (or the lens, if you want the behaviour scoped to one view) and implement whatever hook the README specifies. Keeping it off your base resource class avoids leaking aggregate filtering into every index in the app.
  • Drive it from a Nova filter, not from hardcoded logic. The durable pattern is a Filter whose apply() sets the aggregate condition and whose options() drives the UI. That way the condition is user-toggleable, shows up in the active-filter pill bar, and survives the request round-trip on sort/pagination — which is exactly where a hand-rolled ->having() in a controller would break.
  • Keep it alongside, not instead of, WHERE filters. Combine a normal Nova filter (narrow the row set first) with the having filter (then constrain the grouped set). Doing the WHERE work first keeps the grouped intermediate small and is usually the difference between a fast index and a slow one on large tables.
  • Use it in metric/lens queries too. Anywhere you already hand-write ->selectRaw('SUM(...) as total')->groupBy(...), the same having helper lets you filter those groups without re-deriving the query. This is the pattern that pays off most day-to-day: one expression of "groups matching a threshold" shared between the index filter and the trend card.
  • Test at the query level, not just the HTTP level. Nova's testing helpers (Nova::actingAs(...)->get('/nova-api/...')) confirm the request doesn't blow up, but they won't tell you the SQL is portable. Add a unit test that asserts the generated SQL/ bindings for your filter on the connection you actually deploy against.

Gotchas and Tips

  • HAVING without GROUP BY is undefined behaviour. MySQL will happily return a single row; Postgres will error. If the filter is optional and the group-by only applies when it's active, make sure both are toggled together — a half-applied state is the most common failure mode for this kind of package.
  • Alias portability is the real trap. MySQL lets you write HAVING total > 100 referencing a SELECT alias; Postgres does not and needs the full expression. If your app targets more than one driver (or you test on SQLite and deploy on MySQL), verify which form the package emits and don't hand-write the other one into your apply() method.
  • ONLY_FULL_GROUP_BY. Every non-aggregated column in your SELECT must appear in GROUP BY, or MySQL rejects the query. Nova's index query adds its own columns and ordering — if the resource has a default sort on a column you didn't group, the query fails only when the feature is switched on. Test with the filter active and a sort applied.
  • Pagination totals. Nova runs a separate count for the paginator. If the count query doesn't carry the same HAVING clause you'll get a page count that disagrees with the rows on screen. Sanity-check the last page number on a filtered index before shipping.
  • Global scopes still apply. Soft deletes and any model-level scopes are composed underneath, which is usually what you want — but a whereHas-style scope combined with a grouped query can silently multiply rows and skew your aggregate. Check the raw SQL with ->toSql() and ->getBindings() in Tinker, and hook DB::listen() in a local request if the filter isn't producing the rows you expect.
  • Extension points: if the trait exposes an overridable method for building the having clause, override it in your resource rather than forking the package — that keeps you on the upgrade path for what is, at v1.0.0, a very young API surface. Expect breaking changes before 1.x settles and pin the version in composer.json accordingly.
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.
jinas/dhivatar
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