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

Admin Listing Laravel Package

brackets/admin-listing

Laravel package for building admin listing screens with a fluent API: searchable and filterable tables, sorting, pagination, customizable columns and row actions. Helps you ship consistent CRUD index views faster.

View on GitHub
Deep Wiki
Context7

Getting Started

brackets/admin-listing is a small support package from the BRACKETS admin suite. It does not ship routes, views or a UI — it wraps an Eloquent query with search, ordering, pagination and a response transformer so an admin index action can be a one-liner. Most people get it transitively via brackets/admin-ui or brackets/admin-generator; installing it alone is only worth it if you want the query helper without the rest of the suite.

composer require brackets/admin-listing
php artisan vendor:publish --provider="Brackets\AdminListing\AdminListingServiceProvider" --tag=config

That publishes config/admin-listing.php (default limit, default order.column / order.direction, search behaviour, and — since v3.5.0 — the name of the request attribute used for the current page). Read that file first, then read the source — the package is only a handful of files: the AdminListing class, the AdminListing facade, and the HasSearchableAttributes trait. Ignore the repo README if it lags; the generated code from admin-generator is the best documentation.

First real use case — replace a hand-written index query:

// before
$posts = Post::query()->latest()->paginate(15);
return view('admin.posts.index', compact('posts'));

// after
return AdminListing::create(Post::class)->processRequestAndReturn($request, 'admin.posts.index');

To make search work, add the trait and declare the columns on the model:

use Brackets\AdminListing\Traits\HasSearchableAttributes;

class Post extends Model
{
    use HasSearchableAttributes;

    public $searchable = ['id', 'title', 'slug'];
}

Then hit the page with ?search=foo&orderBy=title&orderDirection=desc&page=2&perPage=25 and confirm the result.

Implementation Patterns

One-liner vs. explicit chain. processRequestAndReturn($request, $view) reads search, orderBy, orderDirection, the page attribute and perPage from the request and returns either a paginator or, for AJAX requests, the JSON envelope the admin Vue tables expect. That convenience is exactly why you should not use it in a non-BRACKETS frontend — it will shape the response for you. For custom consumers, build explicitly:

$listing = AdminListing::create(Post::class, 25)
    ->attachSearchQuery($request->query('search'))
    ->attachOrdering($request->query('orderBy'), $request->query('orderDirection'))
    ->attachMeta();

return $listing->get();   // paginator / collection

(Method names have shifted between releases — confirm attachOrdering vs attachOrderBy against the installed source before copying this.)

Custom limit per controller. AdminListing::create(Post::class, 50) overrides the config default without touching admin-listing.php globally. Useful when one table is much smaller than the rest.

Custom page parameter (v3.5.0). The request attribute used for the page number is now configurable instead of being hard-coded to page. This matters when a listing is embedded next to another paginated component, or when your frontend already uses a different name (p, seite, page[number]). Set it once in config/admin-listing.php and — because the paginator must agree — pass the same name to the underlying LengthAwarePaginator (->paginate($perPage, ['*'], $pageName)), otherwise the links the paginator renders will point at a parameter nobody reads. Confirm the exact config key in the published file rather than guessing; it is new and the README has not caught up.

Transformers. The third argument to processRequestAndReturn (or a ->transform(...) call) lets you reshape rows after pagination — attach computed attributes, flatten relations, or hide columns. This runs on the result set, not the query, so it is cheap and safe, but it cannot fix N+1s. For eager loading, constrain the base query (global scope or a subclass that overrides the built query) rather than transforming afterwards.

Filters map to model scopes. attachFilters($request->all()) is the pattern for "status = published" style toggles; the convention is that each filter key resolves to a scope on the model. Write the scopes as normal Eloquent local scopes and the listing does the wiring. Verify the exact key/scope naming convention in your installed version — this is the part of the API that has churned most.

Use it as a base class. For anything non-trivial (multi-tenant scoping, joins for search across relations), subclass AdminListing, override the query build step, and keep the controllers thin. That is the intended extension point and it keeps you off the package's internals in your day-to-day code.

Gotchas and Tips

  • It is a suite component, not a community package. 7 stars and 0 dependents means the "docs" are the generator output and the source. Budget an afternoon to read all of it on first adoption; it is small enough that you will.
  • AJAX response shape is opinionated. processRequestAndReturn returns a data + meta envelope for XHR requests (current_page, last_page, per_page, total, search, orderBy, orderDirection). If your frontend isn't the BRACKETS admin UI, call ->get() and shape the response yourself.
  • Whitelist orderBy yourself. Never pass $request->query('orderBy') straight through if you are on an older release; SQL injection via ORDER BY is a classic and the package's validation is version-dependent. Keep an allow-list in the controller or in the config.
  • Search is LIKE %term% across $searchable. No relevance, no full-text, no relation traversal. On tables past a few hundred thousand rows this becomes the slowest query in the app — add composite indexes or move to Scout/Meilisearch instead of fighting the helper.
  • A typo in $searchable fails at request time, not at boot. Add a test that hits the index route with ?search=x for every listing you add; it catches missing columns, missing scopes and broken transformers in one shot.
  • Case sensitivity. On PostgreSQL the default LIKE is case-sensitive; the config exposes a switch for this. Set it once per project rather than per query.
  • Config default ordering wins silently. If orderBy is absent the config's order.column is used — which can be a column that doesn't exist on a newer table. Set sensible defaults per model by overriding in a subclass.
  • Pagination cap. perPage from the request is bounded by the limit config in most releases; confirm the behaviour, because an unbounded perPage=10000 will happily serialise your whole table.
  • Renaming the page parameter is a breaking change for hard-coded callers. After v3.5.0's configurable page attribute, any test, Blade view, Vue table or bookmarklet that appends ?page= by hand will silently reset to page 1. Grep for page= when you flip the setting, and keep the frontend and the paginator's pageName in sync.
  • Version pinning. v3.5.0 is the first release that explicitly declares Laravel 10 support (previously 8–9 era); Laravel 11+ is still unverified. Check the illuminate/* constraint in composer.json and run the index route as a smoke test before trusting it in production. There are no documented breaking changes in 3.5.0, so the upgrade from 3.4.x should be a drop-in.
  • $searchable is a public property on the model. Harmless on its own, but it collides conceptually with Laravel Scout and with any of your own search abstractions — be consistent about which one owns the property in a given model.
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.
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
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle