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

Filter Bundle Laravel Package

silpo-tech/filter-bundle

A Laravel/PHP package for building reusable, request-driven filters for Eloquent queries. Define filter classes, apply query parameters cleanly, and keep complex search logic organized, testable, and easy to integrate into existing models and APIs. Ideal for standardizing filtering across list endpo...

View on GitHub
Deep Wiki
Context7

Getting Started

Before you install anything, look at the code. This package has 1 star and 0 dependents, so treat it as an internal Silpo utility that happens to be public rather than a community-vetted library. The README is the source of truth; there is no Stack Overflow corpus for it.

  1. Install and pin. Because there is no downstream usage to catch regressions, never use a floating constraint:

    composer require silpo-tech/filter-bundle:^1.0 --prefer-dist
    

    Then lock it (composer.lock committed) and consider vendoring/forking if the repo goes quiet.

  2. Register the bundle. Check whether it ships a Symfony Flex recipe (unlikely at this star count). If not, add it manually to config/bundles.php:

    SilpoTech\FilterBundle\FilterBundle::class => ['all' => true],
    

    Confirm the actual root namespace from composer.json (autoload.psr-4) — the vendor name and namespace often differ.

  3. Read the DI config first. Open src/DependencyInjection/ and config/ (or Resources/config/). That tells you, in five minutes, what services are public, what the main entry-point interface is, and what configuration keys exist. Do this before writing any code — it is faster than guessing from class names.

  4. Check the tags vs. branches. The metadata lists the last release as 2026-09-18, which is in the future relative to most installs. Run git ls-remote --tags and composer show silpo-tech/filter-bundle --all to see what actually exists; you may be pulling dev-master, which is a materially different risk profile.

  5. First use case: turn request query parameters into a filtered Doctrine query. The typical shape of a bundle like this is:

    • you have a QueryBuilder (or a repository method),
    • you hand it the Request plus a whitelist/config describing which parameters are filterable,
    • you get back a constrained QueryBuilder you keep building on.

    Start with one entity and two or three whitelisted filters, assert the generated SQL with the Doctrine profiler, and only then wire it into a shared base controller or repository trait.

Implementation Patterns

Keep the filter layer out of controllers. Whatever the entry point is (a filter service, a factory, or a query-builder decorator), inject it into a dedicated repository method or a thin application service. Controllers should pass a request (or an already-normalised array) and receive a QueryBuilder. This keeps the package swappable — important given the maintenance risk.

Whitelist explicitly, always. The safest pattern for any request-driven filter bundle is a per-entity allowlist defined in config or in a small FilterSet-style class:

final class ProductFilterSet
{
    public const ALLOWED = [
        'name'       => StringFilter::class,
        'price'      => RangeFilter::class,
        'createdAt'  => DateRangeFilter::class,
        'category'   => RelationFilter::class,
    ];
}

Never derive the field name from the request parameter directly. If the bundle does that by default, wrap it.

Normalise input before it reaches the bundle. Browsers and API clients send ?price[from]=10&price[to]=20, ?price_from=10, or ?price=10..20. Pick one canonical form at the edge (a form type, a request DTO, or a small normaliser) and feed the bundle a clean, typed array. This is the single biggest source of "why is my filter silently ignored" bugs.

Compose with pagination and sorting. Filtering is almost never used alone. The order of operations that works reliably:

  1. build base QueryBuilder in the repository (with select and required joins),
  2. apply filters,
  3. apply sort (whitelisted columns only),
  4. hand to your paginator (knp-paginator, Pagerfanta, or a manual setFirstResult/setMaxResults).

If you paginate before filtering, counts will be wrong; if you sort before joining, you can get duplicate rows.

Reuse across a CRUD surface. For an admin panel with many index pages, the highest-leverage pattern is a single abstract controller or trait:

protected function indexQuery(Request $request): QueryBuilder
{
    $qb = $this->repo->createActiveQueryBuilder();

    return $this->filters->apply($qb, $request, $this->filterSet());
}

Then each resource overrides filterSet(). This gives you one place to add a global filter (e.g. tenant or deletedAt IS NULL) later.

Integration with forms / API Platform. If you also expose these resources via API Platform, do not run both the bundle's filters and API Platform's QueryParameter filters over the same query — you will get double constraints and confusing 0-result responses. Pick one layer per endpoint.

Test at the SQL boundary. Because there is no community test corpus, write your own contract tests early. The most useful assertion is not the object graph but the DQL/SQL:

$qb = $this->filters->apply($repo->createQueryBuilder('p'), $request, $set);
$this->assertStringContainsString('p.price >= :price_from', $qb->getDQL());
$this->assertSame(10, $qb->getParameter('price_from')->getValue());

That test suite is also your migration guide if you ever replace the bundle.

Treat it as an adapter, not a foundation. Wrap the bundle behind your own FilterApplierInterface with one or two methods. Everything else in your app depends on your interface; only one small class depends on silpo-tech/filter-bundle. Given 0 dependents and 1 star, this is the difference between a two-hour swap and a two-week refactor.

Gotchas and Tips

Metadata red flags — verify before you commit. The 2026-09-18 release date, 1 star, and 0 dependents together suggest an internal package published with minimal curation. Check: does the tag exist, does it install on your PHP/Symfony version, is composer.json free of dev- constraints on its own dependencies, and is there a LICENSE file matching MIT? A missing or mismatched license file is a legal blocker for some orgs even when Packagist says MIT.

Version constraints can betray you. Small internal bundles frequently declare loose constraints like "symfony/framework-bundle": "^4.4 || ^5.0" while actually relying on 6.x behaviour, or require doctrine/orm: ^2.0 when you are on 3.x. Run composer why-not and a full test suite before assuming compatibility.

Silent no-ops are the classic failure mode. Unknown filter keys are often ignored rather than throwing. If a filter "doesn't work," check in this order: (1) is the key in the whitelist, (2) is the parameter name exactly right (camelCase vs snake_case), (3) is the request actually reaching the service (dump it), (4) is the value type what the filter expects ('10' vs 10 vs ['from' => 10]). Adding a strict mode that throws on unknown keys during development is worth the twenty lines.

Injection risk with field names and directions. Any place where a request value ends up in orderBy(), addSelect(), or a raw DQL fragment is an injection vector — DQL parameters do not protect identifiers. If the bundle exposes sort handling, confirm it maps through an allowlist. If it does not, sort yourself.

Join explosion and duplicate rows. Relation filters typically add INNER JOIN or LEFT JOIN. When a parent has many children, ->getQuery()->getResult() returns duplicates. Use ->distinct() (or ->groupBy('p.id')), and be aware distinct plus pagination has its own performance cost on large tables. Watch the query count in the profiler on a realistic data set, not on fixtures.

Empty and boundary values. ?price= and ?price[from]= are not the same as "no filter," and 0 is not falsy for a range. Audit how the bundle treats empty strings, null, and empty arrays — the most common bug report on filter bundles is "clearing the search box returns nothing." Handle it in your normaliser.

Configuration quirks are undocumented. After install, dump the resolved config:

bin/console config:dump-reference filter
bin/console debug:container --tag=filter  # adjust tag name to what the bundle uses

This reveals default values and whether the bundle registers anything globally (a request listener, a Twig extension, a Doctrine event subscriber). A hidden listener that mutates every query is the sort of thing this kind of bundle does and does not advertise.

Extension points to look for. Small bundles usually offer three: a filter-type interface you can implement for custom logic, a service tag for registering those types, and a compiler pass that collects them. If the types are a hard-coded switch, fork it — that is a five-line change and far cheaper than working around it.

Debugging tips. Enable doctrine.dbal.logging in dev and compare the generated SQL with the SQL of the unfiltered query. For anything request-related, bin/console debug:router plus a dump($request->query->all()) at the entry point resolves half of all issues. If results look stale, check for a second-level or result cache interacting with the added parameters.

Have an exit plan. With 0 dependents and a single maintainer org, assume the package may be abandoned. Keep the adapter boundary described above, keep your contract tests, and record in your README which behaviours you depend on. If it stops receiving updates, the replacement is usually 100–200 lines of your own QueryBuilder filtering — cheaper than you expect, and considerably easier to reason about.

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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib