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

Listable Laravel Package

staccato/listable

Staccato Listable is a Laravel package for adding list functionality to Eloquent models. Create named lists, attach any model via a polymorphic relationship, and manage list items with simple traits and helpers—useful for favorites, watchlists, collections, and more.

View on GitHub
Deep Wiki
Context7

Getting Started

staccato/listable is an abstraction layer that lets you treat an Eloquent model, an array, a collection, or an arbitrary data source as a uniform "listable" repository with a shared list / paginate / filter / sort contract. With only 2 stars and zero dependents, treat the source and the README as the authoritative docs — there is no ecosystem of blog posts to fall back on.

Minimal steps:

  1. Install into a Laravel app (or anywhere with Composer + a PSR-4 autoloader):
    composer require staccato/listable
    
  2. Read these files first, in order:
    • README.md — the package's only real usage guide.
    • src/ — identify the base class/interface (likely something like Listable, AbstractListable, or a ListableRepository contract) and the contract methods it declares.
    • tests/ — the fastest way to see real, working calls without guessing method signatures.
  3. Identify your integration points. The package advertises "any kind of repository," so expect it to define:
    • a contract your repo implements,
    • a filter/sort input object or array shape,
    • a pagination return contract (likely Laravel's LengthAwarePaginator-compatible).
  4. First use case — paginate an Eloquent source through the abstraction. Wrap a model's query in the package's listable base class, feed it a Request-derived filter/sort array, and return the paginator. This proves the wiring end-to-end before you build custom filters.

If the README is thin (common at this star count), a 10-minute tests/ read saves hours of trial-and-error with method names.

Implementation Patterns

One listable class per resource. The abstraction pays off when you have parallel resources — UserListable, OrderListable, ProductListable — each extending the same base, so controller and API code becomes uniform. Avoid one giant listable that serves everything.

Filter and sort as first-class inputs. The package's raison d'être is turning ?filter[status]=active&sort=-created_at into a deterministic result. Pattern:

class OrderListable extends BaseListable
{
    protected array $sortable = ['created_at', 'total', 'status'];
    protected array $filterable = ['status', 'customer_id'];
}

Keep an explicit allow-list. Never pass the raw request array into the listable and hope it ignores unknown keys.

Driver-agnostic repositories. Because the contract is "listable source," you can swap the backing store without changing the controller:

  • EloquentOrderListable → query builder + paginator.
  • ArrayOrderListable → for tests and fixtures.
  • ApiOrderListable → for an external service, mapping to the same filter/sort semantics.

Write controllers against the contract, bind the concrete driver in the container. This is the pattern the package exists for; if you only ever use one driver, plain Eloquent is simpler.

Compose filters, don't branch. If the package exposes a filter hook, implement small filter classes (e.g., StatusFilter, DateRangeFilter) registered per listable. This keeps conditional logic out of the listable and makes individual filters unit-testable against the array driver.

Wire to API Resources. Return the paginator and let JsonResource::collection() handle the shape. Don't let the listable own response formatting — that couples your transport to your data layer.

Testing pattern. Always test listables against the array/collection driver: fast, no database, and it forces you to keep the contract honest. Add one integration test per concrete Eloquent driver to catch query bugs.

Caching seam. If the package exposes a "resolve" step, wrap the source in a cache decorator that implements the same listable contract. Cross-driver consistency (same filter+sort+page ⇒ same shape) is what makes this safe.

Gotchas and Tips

Documentation is the biggest risk. With 2 stars and 0 dependents, expect the README to lag the code. When README and src/ disagree, src/ wins. Pin an exact version (composer require staccato/listable:^1.x with a composer.lock committed) — a maintainer patch could silently rename the base class.

Filter/sort injection is on you. The abstraction centralizes query input, which also centralizes the attack surface. If the package forwards filter keys to where() without validating against your $filterable allow-list, a client can filter on password or a relation column. Audit the filter application path in src/ before exposing it publicly.

Sorting has a hidden cost. A sort=-created_at on an unindexed column will full-scan once your table grows. Keep $sortable tight and aligned with indexes. Also confirm the package validates direction — a hand-rolled orderBy($request->sort) style implementation is a classic injection point.

Cross-driver behavioral drift. "Any listable source" sounds uniform, but pagination boundaries, null sort ordering, and case-insensitive filtering differ between SQL, arrays, and external APIs. Define expected semantics in tests per driver; don't assume the Eloquent behavior transfers.

Watch pagination contract mismatches. If your concrete driver returns a custom paginator shape and Laravel's JsonResource::collection() expects LengthAwarePaginator, you'll get subtly broken meta (total, links). Assert the shape in a test.

Extension points to look for in src/:

  • a filter() / sort() hook to override per listable,
  • a base paginate() you can override to change page size resolution,
  • a contract you can decorate (cache, logging, metrics). If none exist, prefer composition (wrap the listable) over subclassing internals — safer against upstream changes.

Debugging recipe. When results look wrong, instrument in this order: (1) dump the normalized filter/sort array your listable receives, (2) dump the query the driver builds, (3) run the same filter/sort against the array driver. If array output matches expectations and SQL doesn't, it's a driver bug; if array output is already wrong, it's filter parsing.

N+1 trap. Listables that paginate an Eloquent source and then render relationships will N+1 unless the package exposes eager-load hooks. If it doesn't, override the source query in your concrete driver to preload relations.

Low-adoption reality check. At 2 stars / 0 dependents, this is a "read the source, adopt the pattern" package as much as a dependency. Copy the contract into your app (App\Contracts\Listable) and implement it locally if you're uncomfortable depending on an unmaintained library — you get the same design benefit with none of the upgrade risk. If you do depend on it, wrap it behind your own interface so a fork or replacement is a one-file change.

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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource