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

Queries Laravel Package

useless-soft/queries

Laravel query utilities from Useless Soft. Organize reusable database queries into dedicated, testable classes, chain filters, and keep controllers and models lean. Simple to install and framework-friendly, ideal for cleaner data access without heavy abstractions.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check before you install. useless-soft/queries is archived, has zero dependents, and ships without a recognized license (NOASSERTION). Treat it as a reference implementation of the query object pattern rather than a dependency you drop into a production composer.json. If you still want to evaluate it, install from source, not Packagist:

composer require useless-soft/queries:dev-main --prefer-source
# or, cleaner for evaluation:
git clone https://github.com/UselessSoft/queries.git vendor-eval/queries

Where to look first, in this order:

  1. composer.json — check the illuminate/* constraints and the autoload PSR-4 namespace. This tells you immediately whether it targets the Laravel version you're on. An archived package almost always pins an old major.
  2. src/ — find the abstract base class (usually Query, BaseQuery, or AbstractQuery) and the contract/interface. That base class is the package; everything else is convenience.
  3. tests/ — the fastest way to learn the intended API. Archived packages frequently have tests that no longer run, but they document the author's mental model better than the README.
  4. README.md — last, and skeptically. READMEs on abandoned packages drift from the code.

First use case to validate: a filtered, paginated index endpoint. Something like GET /orders?status=paid&from=2024-01-01. Build one query class for it, wire it into a controller, and confirm the generated SQL with ->toSql(). If the package can't cleanly express that, it won't help you with the harder cases either.

Implementation Patterns

One query class per read use case, not per model. The value of the pattern is naming intent: PaidOrdersInRangeQuery, ActiveSubscribersQuery, StaleCartQuery. If you find yourself writing OrderQuery with fifteen boolean flags, you've rebuilt a scope with extra steps.

Inject, don't instantiate. The workflow that pays off day-to-day:

// app/Queries/PaidOrdersInRangeQuery.php
final class PaidOrdersInRangeQuery extends Query
{
    public function __construct(
        private readonly CarbonImmutable $from,
        private readonly ?CarbonImmutable $to = null,
    ) {}

    public function builder(): Builder
    {
        return Order::query()
            ->where('status', OrderStatus::Paid)
            ->when($this->to, fn ($q) => $q->where('paid_at', '<=', $this->to))
            ->where('paid_at', '>=', $this->from);
    }
}

Then resolve it from the container in the controller or a form request, and hand the builder to the resource collection. This keeps controllers thin and — more importantly — makes the query greppable by name when someone asks "where do we filter paid orders?" six months later.

Compose queries rather than inherit deeply. If the package exposes a criteria/filter pipeline, use it for cross-cutting concerns (soft-delete handling, tenant scoping, default ordering) and keep the concrete query classes flat. Deep inheritance chains in query objects are the classic failure mode: three levels down nobody knows what applyBaseConstraints() actually adds.

Reuse across surfaces. The same query object should serve your HTTP controller, an artisan command, a queued export job, and a scheduled report. This is the main justification for the pattern over inline Eloquent — the second consumer is free. If a query is only ever used in one controller method, a local scope is simpler and you should use that instead.

Test at the SQL level, not the HTTP level. Point the query at an in-memory SQLite connection, call ->toSql() and ->getBindings(), and assert on structure. These tests are fast, don't need factories, and survive refactors of your controllers. Add one integration test per query against the real schema to catch column typos that SQLite tolerates and MySQL doesn't.

Keep write operations out. If the package's base class encourages update()/delete() on the same builder, resist. Query objects that mutate are just repositories with worse ergonomics, and you lose the ability to reason about them as pure read paths (and to cache them, and to run them against a read replica).

Gotchas and Tips

License is the blocker, not the code. NOASSERTION means GitHub couldn't identify a recognized license file. Without a clear grant, you have no distribution or commercial-use rights by default. Get legal sign-off or vendor the pattern (reimplement the 40 lines of base class yourself) rather than the package. For a query object this is genuinely a half-day of work and removes the risk entirely.

Archived means the Laravel support matrix is frozen. Check for illuminate/database and illuminate/support constraints. If the package caps at Laravel 9 or 10 and you're on 11+, expect autoload failures, deprecated Builder method calls, and enum handling that predates native enum casts. Do not assume composer update will resolve this — archived packages often have no upper bound and fail silently at runtime instead of at install time.

Zero dependents is a signal about API stability, not just popularity. There is no ecosystem of blog posts, no Stack Overflow answers, no downstream code exercising edge cases. Every non-obvious behavior you hit — how it handles whereHas, nested orWhere grouping, eager-load interaction — you will debug alone by reading src/.

Debugging tips that apply to query objects generally:

  • Dump the final SQL with ->toSql() plus ->getBindings() before guessing. Nine out of ten "the filter didn't apply" bugs are a closure that returned null from when().
  • Watch for binding-order surprises when chaining raw whereRaw() after builder methods — bindings are positional and a misplaced selectRaw will silently shift them.
  • If the package overrides newQuery() or toBase(), ->dd() and ->dump() may bypass your constraints. Verify with the raw SQL every time you add a layer.
  • Use DB::listen() in a service provider during development to confirm the query object is actually being hit and not shadowed by a cached result or an earlier scope.

Extension points worth knowing: the base class (override builder() or the equivalent hook), any container binding for resolving queries (bind concrete queries so they're mockable), and macros if the package registers them. Bind the contract in a service provider so tests can swap in a fake — this is the one piece of infrastructure worth keeping even if you later drop the package.

Migration path if you outgrow it: Laravel's native local scopes plus a app/Queries directory gets you 80% of the benefit with zero dependency risk. Reach for a maintained alternative (e.g. spatie/laravel-query-builder for API filtering, or a hand-rolled base class) if you need composable filter pipelines. Given that this package is archived with no license and no users, the honest recommendation for day-to-day work is: read it once for the pattern, then write your own twenty-line base class.

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