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

Fetch Xml Bundle Laravel Package

horlyk/fetch-xml-bundle

A Symfony bundle for fetching and parsing XML feeds, simplifying integration with external XML sources in your PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

This package adds declarative, request-driven filtering on top of Eloquent query builders. Day-to-day it means: define which filters an endpoint accepts, map them to columns or relations, and let the package translate ?filter[...] query strings into a constrained query.

  1. Install and confirm auto-discovery:
    composer require <vendor>/<package>
    php artisan vendor:publish --tag=<package>-config   # only if you need to change defaults
    
  2. Read the README sections in this order: allowed filters, aliases, relation filters. The alias API in v1.5.0 is the part most likely to differ from examples you find in older blog posts.
  3. First use case — an index endpoint with a whitelist:
    // app/Http/Controllers/PostController.php
    public function index()
    {
        return PostResource::collection(
            QueryBuilder::for(Post::class)
                ->allowedFilters(['status', 'title'])
                ->allowedSorts(['created_at'])
                ->paginate()
        );
    }
    
    Hit it with ?filter[status]=active and confirm the generated SQL via ->toSql() or DB::listen() before wiring up the frontend.

Validate the happy path on a model with one relation before rolling the pattern out across the app — relation filtering is the part that changes shape the most between versions.

Implementation Patterns

Centralise filter definitions per resource, not per controller. Keep a filter class / allowed-filters method next to the model or in app/Queries, so HTTP verb and filter vocabulary stay in sync:

class PostFilters
{
    public static function all(): array
    {
        return [
            'status',                          // direct column
            AllowedFilter::exact('author'),    // relation
            AllowedFilter::scope('published'),
        ];
    }
}

Use aliases as the public API contract. The v1.5.0 alias support for relations is the main lever for decoupling your frontend from your schema. Alias to the internal path, not the other way around:

AllowedFilter::exact('state', 'status'),             // column rename safety
AllowedFilter::exact('author', 'author.name'),       // relation alias
AllowedFilter::exact('writer', 'author.name'),       // second public name, same target

This lets you rename status → state, or move author.name behind a join, without breaking clients. Treat alias keys as versioned API surface: document them alongside the endpoint.

Filter on relations deliberately. Decide between whereHas semantics (filter parent by existence of a matching child — usually what you want for ?filter[author]=jane) and join-based filtering (needed for sorting by a related column). Sorting and filtering on the same relation is the common integration point that requires a join; add it explicitly rather than relying on the filter to do it.

Lean on empty-value handling rather than frontend guards. Since the v1.5.0 bugfix, empty filter values are ignored instead of applied. That means a select box with <option value="">All</option> can submit ?filter[status]= safely. Remove the client-side "strip empty params" helpers you may have written — but only after checking each endpoint's behaviour (see Gotchas).

Compose with the rest of the query pipeline. Standard order that stays predictable: filters → scopes → sorts → eager loads → pagination. Add ->allowedIncludes([...]) for relation loading separately from filtering so ?include=author doesn't double as an implicit filter.

Test with SQL assertions, not just JSON. Feature tests will pass even when a filter is silently dropped. Assert the query either via DB::listen() or by seeding rows that differ only in the filtered field:

$this->getJson('/api/posts?filter[writer]=jane')
     ->assertJsonCount(2, 'data');

Include a test per alias, since alias mapping failures are silent (the filter just never matches).

Gotchas and Tips

  • Empty value is now a no-op. The v1.5.0 fix means ?filter[title]= no longer filters for an empty string. If any endpoint intentionally filters for blank/null (?filter[deleted_at]=) you now need an explicit scope or AllowedFilter::scope('trashed'). Grep your clients for filters that can legitimately be empty.
  • Aliases are not validated for collisions. Two aliases resolving to the same column, or an alias shadowing a real column name, behaves in whatever order the package resolves them. Keep an eye on duplicates in code review — this is the most common silent bug in alias-heavy setups.
  • Relation aliases fail quietly. If the relationship name in the alias doesn't exist or is misspelled, you typically get an empty result set rather than an exception. Verify with ->toSql() and Log::info on the query builder during development.
  • Whitelist strictly. Only filters listed in allowedFilters() should reach the query; anything else should be dropped or 400'd. Never feed raw request arrays into the builder — that's how aliases become an injection-adjacent footgun.
  • Config caching. After changing the package config, run php artisan config:clear / config:cache. A stale cached config is a classic "works locally" cause when toggling the package's feature flags.
  • Performance. Relation filters compile to whereHas subqueries by default; for large tables combine with indexes on the foreign key and consider a join-based custom filter for hot endpoints. Check with ->explain() on the real query, not the test fixture.
  • Debugging recipe. QueryBuilder::for(...)->allowedFilters(...)->tap(fn ($q) => logger($q->toSql(), $q->getBindings())), or listen globally in a service provider while investigating. Compare the SQL against the raw Post::where(...) you expected.
  • Extension points. Prefer a AllowedFilter::custom() / custom filter class over modifying package internals; keep custom filters in one namespace so a future breaking release touches a single directory. Scopes are the least fragile escape hatch because they survive filter API changes.
  • Upgrade hygiene. Pin the version in composer.json before deploying. Minor releases in the 1.x line have changed filter-value semantics, so re-run your full filter test suite — not just the new feature tests — after composer update.
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