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

Table Laravel Package

paksuco/table

paksuco/table is a Laravel package for building configurable data tables in PHP. It helps define columns and rows for admin lists and grids, with straightforward Blade integration. Lightweight and easy to add to existing Laravel apps.

View on GitHub
Deep Wiki
Context7

Getting Started

Read the source before you read anything else. There is no meaningful documentation beyond a one-line changelog and a feature blurb. Treat src/ as the spec. Budget the first hour of your spike for orientation, not integration:

  1. Confirm compatibility first — this is the go/no-go gate. Last release was 2021-03-25, which predates Laravel 9. Check composer.json for the illuminate/* and php constraints and diff them against your actual matrix before you run composer require. If you're on Laravel 10/11 or PHP 8.2+, assume you may need a fork; find that out in ten minutes rather than after wiring screens.
  2. Pin exactly, never track main. For a 0.1.0 with an empty changelog, "paksuco/table": "0.1.0" (exact) and a committed composer.lock is the only sane posture. No ^, no dev-main.
  3. Find the service provider and see what it registers and publishes:
    composer require paksuco/table:0.1.0
    php artisan vendor:publish --provider="Paksuco\Table\TableServiceProvider"
    
    Inspect config/ and resources/views/ in the package before publishing — the published views become yours to maintain forever, so publish only what you intend to override.
  4. Locate the column-definition contract. The whole package is essentially "an array/collection of column objects → a Blade view with sort links and a paginator." Find the class or builder that accepts column definitions and the base class (if any) a table config extends. That single file tells you 80% of the API surface.

First use case — pick a read-only index screen. Take one existing index view backed by an Eloquent paginator, ideally a model with 5–8 scalar columns and no complex relations. Do not start with your most complex CRUD screen, and do not start on a write path — this package is a presentation layer, and the spike is meant to measure friction on the cheapest possible surface. Success criterion: the screen renders identically (or better), sorting and filtering work, and the integration is confined to one or two files.

If the spike exceeds your timebox, that friction is the deliverable. Report it.

Implementation Patterns

One table class per resource, in app/Tables/. Don't build column arrays inline in controllers. The package's stated value is "reusable table configurations," so lean into it:

// app/Tables/UserTable.php — wraps the vendor base class
final class UserTable extends \Paksuco\Table\Table
{
    protected function columns(): array
    {
        return [
            // exact key names: confirm against src/, the README does not specify
            [ 'key' => 'id',         'label' => 'ID',       'sortable' => true ],
            [ 'key' => 'name',       'label' => 'Name',     'sortable' => true, 'searchable' => true ],
            [ 'key' => 'email',      'label' => 'Email',    'sortable' => true, 'searchable' => true ],
            [ 'key' => 'created_at', 'label' => 'Created',  'sortable' => true, 'format' => fn ($row) => $row->created_at->diffForHumans() ],
        ];
    }
}

Put a one-file adapter between you and the vendor. This is the single most important pattern given the package's maturity. Every controller and view talks to your abstract base class (App\Tables\BaseTable), and only that class extends the vendor. When the package stalls, forks, or gets swapped, the blast radius is one file. It also gives you a place to add project-wide behaviour — default per-page, consistent date formatting, an actions column — without editing published views.

Drive sorting and filtering from the request, but validate against an allowlist. The package will almost certainly take the sort column from the query string. Never let it touch the query builder unvalidated:

$allowedSorts = ['id', 'name', 'email', 'created_at'];

$sort = in_array($request->query('sort'), $allowedSorts, true)
    ? $request->query('sort')
    : 'created_at';
$dir  = $request->query('dir') === 'asc' ? 'asc' : 'desc';

$table = app(UserTable::class)->from(
    User::query()->with('team')   // eager-load anything a column touches
)->sort($sort, $dir)->search($request->query('q'));

If the package doesn't enforce an allowlist itself, enforce it in your adapter — a whitelist in BaseTable covers every screen at once.

Keep the query yours, the rendering theirs. The safest integration boundary is: you build the Builder (with scopes, eager loads, tenancy constraints, policy-derived filters), and hand it to the table purely for presentation. If the package starts composing queries you don't control, stop and reconsider — that coupling is expensive to unwind.

Eager-load for relation columns. A column that renders $row->team->name inside the Blade cell is a classic N+1. Because the cell rendering is hidden behind the package's view, the query count is invisible unless you look for it. Write one test that asserts DB::getQueryCount() (or use Model::preventLazyLoading() in local) on each table screen.

Pagination must preserve filters. Whatever the package hands to links(), ensure the paginator calls withQueryString() — otherwise page 2 silently drops the active search and sort. If the package's view doesn't do this, override that one partial rather than forking the whole view.

Override at the cell level, not the template level. The package advertises "extensible cells, actions, and formatting hooks." Prefer, in order: a format/render closure on the column definition → a named cell partial you publish → overriding the table view. Each step up that ladder costs you more on upgrade. Record every published/overridden view in a docs/vendor-overrides.md so upgrades are auditable.

Test the rendered output, not the internals. One feature test per table screen asserting the presence of expected rows, the correct order after ?sort=name&dir=asc, and filtered results after ?q=. That suite is your upgrade safety net — it will catch behavioural drift in a package that won't tell you about it in a changelog.

Gotchas and Tips

  • Pre-1.0 with an empty changelog means upgrades are archaeology. Before bumping the version, diff src/ and resources/views/ between tags. Read release diffs, not release notes.
  • The Laravel/PHP constraint check is the most likely dealbreaker. A 2021 package may not run cleanly on modern Laravel. If it doesn't, decide explicitly between (a) pinning your app's framework version, (b) a small maintained fork, or (c) walking away. Don't paper over it with --ignore-platform-reqs.
  • Bus factor of one, one star, no visible CI. Assume no upstream fixes will arrive on your timeline. That's acceptable only because the abstraction seam makes a fork a one-file change. If you can't keep the seam, don't adopt.
  • Sort/filter parameters are user input. Confirm the package parameterises the search term (it should bind, not interpolate) and that sort columns are validated. Grep the source for orderBy( and whereRaw( and read those two lines yourself — this is a five-minute security review you should not skip on unvetted code.
  • Search is likely LIKE %term% per column. Fine for small admin tables, quietly awful on large ones. Check whether it accepts a custom query callback; if not, add an explicit filter in your adapter rather than relying on the built-in search for anything performance-sensitive.
  • Don't put it on a critical path. Index/admin screens: yes. Anything a customer pays for or that has an SLA: no. A table view is commodity UI — you can hand-write 40 lines of Blade as a fallback if the package becomes a liability.
  • Debugging a table that "renders nothing": dump the resolved column configuration and the builder's toSql()/bindings before the render call. Ninety percent of failures are an empty column array, a renamed key that the view silently ignores, or a builder that was cloned and lost its constraints.
  • Watch for state leaking between tables. If table configs are resolved as singletons or cached, mutations in one request can bleed into another. If you see a second table rendering the first table's columns, that's the cause — resolve a fresh instance per use.
  • Check whether the package publishes a config file and whether it reads it at boot. Published config that is read in a service provider won't pick up runtime overrides; you'll need config()->set() before the provider boots or a rebind. A classic hour-lost.
  • Vendor the thing if you adopt it. Keep a fork under your org, pin it via a VCS repository entry, and treat "the upstream is dead" as the default assumption rather than a surprise.
  • The exit is cheap right now; keep it that way. If the integration ever sprawls past the adapter — raw vendor classes in controllers, vendor view names in Blade, vendor query concerns in models — stop and pull it back. That sprawl is what converts a three-day swap into a three-week migration.

Bottom line for a day-to-day developer: treat paksuco/table as a shortcut for commodity admin index screens, adopted behind a single adapter class, exact-pinned, with feature tests on rendered output. It can save you an afternoon of Blade and sort-link plumbing on a prototype; it cannot be handed a production critical path at 0.1.0 with a 2021 release date. Timebox the spike, measure the friction honestly, and re-evaluate at a later tag.

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.
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
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core