useless-soft/queries-bundle
A Symfony bundle for organizing database queries as reusable services. Define query classes, wire them through dependency injection, and keep complex read logic out of controllers and repositories. A simple, testable foundation for CQRS-style read models in Doctrine-backed applications.
useless-soft/queries-bundle is a thin Symfony bridge over the UselessSoft/queries library — it exists to register the library's services in the container and expose a config tree, not to add behaviour of its own. Treat it as glue code, not a framework.
Before you install, read the signals: 1 star, 0 dependents, archived. There will be no security fixes, no Symfony 7 compatibility work, and no answers to issues. If you are evaluating this for production, the realistic path is to read the bundle source (it is small) and reimplement the ~50 lines of DI wiring in your own config/services.yaml. If you are using it for a prototype or to understand the intended integration, proceed.
Minimal steps:
composer require useless-soft/queries-bundle — then immediately pin it. Because it is archived, use an exact version constraint ("useless-soft/queries-bundle": "1.0.0") rather than ^1.0, and commit composer.lock.config/bundles.php; on older skeletons it is app/AppKernel.php. Confirm the bundle class name from composer.json's autoload.psr-4 section before guessing it.DependencyInjection/ — the *Extension.php class tells you the entire public surface (which parameters exist, which services are aliased, what the config tree accepts). Second, read Resources/config/services.* for the concrete service ids. Third, read the UselessSoft/queries README, because the bundle documents almost nothing the library doesn't.bin/console debug:container --show-private | grep -i quer to see what actually landed in the container. This is faster than reading the source and tells you the real service ids in your installed version.bin/console config:dump-reference <alias> to print the bundle's config tree. The alias is defined in the Extension class and is often shorter than the package name.First use case: define one query object representing a single read (e.g. FindActiveSubscriptionsForUser), resolve its handler from the container in a controller, and return the result. Do not attempt to wire your whole read side on day one — you want to verify the bundle's autoconfiguration actually tags your handlers before you write twenty of them.
One query class per question. The value of a queries library is that read intent becomes a named, testable, loggable object. Resist the urge to build a generic QueryBuilder wrapper around it — that is exactly the abstraction the package is trying to replace.
Handler registration is the thing to verify early. Most bundles of this shape rely on either (a) autoconfiguration via registerForAutoconfiguration() in the Extension, or (b) an explicit tag you must add yourself. Determine which by dropping a handler in src/ and running debug:container --tag=<guessed-tag>. If the tag list is empty, add the tag explicitly in config/services.yaml:
App\Query\Handler\:
resource: '../src/Query/Handler/'
tags: ['<tag-from-extension>']
Explicit tagging is the safer pattern regardless — it survives a bundle upgrade that changes autoconfiguration behaviour, and it makes the wiring visible in review.
Inject the dispatcher, not the container. If the bundle exposes a bus/dispatcher service, type-hint that interface in your controller or console command. Injecting ContainerInterface and pulling handlers by id is the failure mode this pattern is meant to prevent.
Decorate for cross-cutting concerns. Logging, timing, and caching of reads belong in a decorator around the dispatcher service, not in each handler. This works cleanly because the bundle registers services by id:
App\Query\LoggingDispatcher:
decorates: '<bundle dispatcher service id>'
arguments: ['@App\Query\LoggingDispatcher.inner']
This is also the cleanest way to add instrumentation to a package you cannot modify.
Integration points that pay off quickly:
config/services_test.yaml with a stub that returns fixtures. Because queries are objects, you get assertion-friendly inputs for free.Archived means the compatibility matrix is frozen. Check the bundle's composer.json require block against your Symfony version before installing. A symfony/framework-bundle: ^4.4|^5.0 constraint will fight with a Symfony 6/7 app and Composer will resolve to something surprising or fail. If you must use it on a newer Symfony, fork it — the diff will be a few lines of services.yaml and possibly a ReturnTypeWillChange attribute.
Bundle registration is version-dependent and silently ignored. On Symfony 5.4+, a bundle that is installed but missing from config/bundles.php produces no error — its services simply do not exist, and you get a confusing "cannot autowire" message. Confirm registration first when anything is "not found".
Config key guessing wastes time. The bundle alias rarely matches the package name (queries_bundle vs useless_soft_queries vs queries). Get it from the Extension class's getAlias(), or just run config:dump-reference with each candidate until one prints a tree. An unknown alias throws rather than returning empty, so this is a fast loop.
Cache is the usual culprit. After changing bundle config, tags, or a compiler pass, run bin/console cache:clear. In dev, config changes to third-party bundles sometimes need a full clear rather than the automatic container rebuild — if services look stale, clear before debugging anything else.
Debugging checklist, in order: debug:container --tag=<tag> (are handlers tagged?) → debug:container <service-id> (is the dispatcher wired with the right arguments?) → lint:container (are there type mismatches in the graph?) → debug:autowiring quer (what is actually injectable?). This sequence resolves the large majority of integration problems with thin bundles.
Extension points are thin, so plan to override, not extend. There is unlikely to be an event system or plugin API. Use service decoration (above), a compiler pass if you need to batch-tag handlers, and a local services.yaml that redefines a bundle service id to point at your own class. All three are standard Symfony and do not depend on the bundle's cooperation.
Do not let the query layer leak into your domain. Because this package is unmaintained, keep handlers as thin adapters over your own repository/read-model interfaces. If the bundle disappears, you delete the wiring and keep the domain. The inverse — domain classes extending or type-hinting library base classes — makes the eventual migration a rewrite.
Audit before you commit. Run composer audit and read the bundle's source end-to-end; it is small enough to review in ten minutes, and with 1 star and no dependents you are effectively the only reviewer it will ever have.
How can I help you explore Laravel packages today?