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

Bruno Laravel Package

emilianosuarez/bruno

Bruno is a lightweight Laravel package by Emiliano Suárez that adds a clean, fluent API for common application tasks. It ships with sensible defaults, publishable configuration, and straightforward integration so you can drop it into any project and start using it quickly.

View on GitHub
Deep Wiki
Context7

Getting Started

Bruno is a Laravel package for declarative Eloquent filtering. Version 1.1.0 is a minor, non-breaking feature release: it adds support for applying filters across relationships, so a single filter definition can now reach into related models instead of being limited to the base query's own columns.

Start here, in order:

  1. composer require emilianosuarez/bruno:^1.1 — pin to ^1.1 rather than ^1.0, since relationship filtering is the capability you'll likely be reaching for. Upgrading an existing 1.0.0 install is safe; nothing in the release notes indicates removed or renamed behaviour.
  2. Read the README's filter-definition section and then the package's own test suite — tests are the most reliable documentation of how relationship filters are expressed, especially for a feature this new.
  3. Publish any config (php artisan vendor:publish) and check for a default allowlist/denylist of filterable keys before wiring it into a controller.

First use case: an index endpoint that currently accumulates if ($request->filled('status')) { $query->where(...) } chains. Move those conditions into a filter definition, then add one relationship filter — e.g. filtering a list of posts by an attribute on the author — and confirm the generated SQL with DB::listen() or ->toSql() before shipping.

Implementation Patterns

Controller as thin adapter. Keep the pattern Model::filter($request->validated())->paginate() in the controller and push all key→column/operator mapping into the filter definition. The controller should never enumerate filterable fields; that's what makes the package worth adopting over hand-rolled conditionals.

Form Requests as the allowlist. The safest way to prevent arbitrary filter keys is validating with 'filter.*' => ['sometimes', 'string', 'max:255'] plus an explicit only() list in the Form Request. Combine with whatever allowlist the package exposes — belt and braces, because a filter surface is a query surface.

Relationship filters. With 1.1.0, treat relationship filters as scoping, not joining: the natural implementation is a correlated subquery (whereHas-style). That means result sets stay deduplicated automatically. If you need to sort by a related column, that's a separate concern — relationship filtering doesn't imply ordering support, so verify before assuming.

Composition over conditionals. Build named, reusable filter fragments (e.g. ActiveOnly, OwnedBy, Recent) and compose them per endpoint. This keeps the same semantics consistent between /posts and /users/{user}/posts.

Eager-loading discipline. Relationship filters do not eager-load. Pair the filtered query with ->with('author') when the resource serialises related data, or you'll trade a clean API for N+1 queries in the transformer.

Testing pattern. Assert on behaviour, not SQL strings, but use DB::listen() in a debug test to confirm a relationship filter emits a single subquery rather than a join fan-out. Cover: unknown filter key (ignored or 422, per your validation), empty string value, array value, and a filter on a relationship that doesn't exist on the model.

Gotchas and Tips

  • Column ambiguity. Unqualified where clauses on a query that also joins (for pagination counts, ordering, or eager-load constraints) will throw ambiguous-column errors. Qualify columns as posts.status in filter definitions and relationship paths.
  • Nested depth is a performance cliff. Relationship filters translate to subqueries; two or three levels deep on large tables can be far slower than the equivalent join. Add indexes on the foreign keys involved, and benchmark against production-shaped data — not a seeder with 50 rows.
  • orWhere grouping. If you mix package-managed filters with hand-written orWhere, ensure the package's constraints are wrapped in their own group. A stray ungrouped orWhere will silently widen result sets and is the classic filtering bug.
  • Empty and falsy values. Decide explicitly whether ?status= (empty), ?status=0, and repeated keys are meaningful. Most filtering bugs are here, not in the query builder.
  • Pagination + relationship filters. paginate() runs a count() on the same query; a costly subquery doubles up. Consider simplePaginate() on large relationship-filtered endpoints.
  • No caching layer implied. Filter results are not memoised by the package. Cache at the response layer with a key derived from the normalised filter array if an endpoint is hot.
  • Pin the minor version in composer.json and add a smoke test for relationship filters, so a future release that changes the relationship strategy fails loudly in CI rather than quietly returning different rows.
  • Extension point: if the built-in operators don't cover a case, prefer a custom named filter over dropping back to raw closures in the controller — otherwise you lose the centralised definition that justified the dependency.
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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums