beartropy/tables
Beartropy Tables is a Livewire table component for the TALL stack with search, sorting, filters, bulk actions, and column toggling. It includes inline editing and boolean toggles with per-field authorization hooks to control writes.
updatedFilters() crashed with Undefined array key "type" when a request updated a filter key the server doesn't know — a stale browser tab, or keys regenerated by a later setFilters() call. Livewire writes the incoming value before the hook runs, leaving a bare ['input' => ...] entry in $filters; the hook then treated it as a real filter. Such entries are now dropped and the update is ignored.editable() now owns persistence. Previously setColumns() nulled out every closure so Livewire could serialize the column objects, which left a closure editableCallback silently unreachable: the callback never ran, and updateField() fell through to the default write and saved the field anyway. The callback now runs, and — matching how a string callback has always behaved — it replaces the default write rather than running alongside it. A table using editable('input', [], fn ($id, $field, $value, $table) => ...) that relied on the field still being saved must now persist it from inside the closure.updateField() dispatched table-field-updated before doing any work, so a denied or failed update announced itself as a successful one. It is now dispatched only once the write has actually happened — after a callback runs, after a successful model save, or after the array row is updated. A write refused by authorizeFieldUpdate()/authorizeFieldUpdateRaw(), or aimed at a record that does not exist, no longer dispatches it.updateField() resolves the column through getFreshColumns() instead of the serialized $this->columns, which is what makes closure callbacks reachable at all. This matches how Sort, Search and Data already resolve columns when they need callbacks intact.Neither change is reachable through the package's own views: the inline editor only renders for columns marked ->editable(), and toggles only render for toggle columns. Both guards reject input the UI never produces, so the affected surface is programmatic calls and hand-crafted Livewire requests.
updateField() now refuses to write a column that was not marked with ->editable(). Previously any declared column could be written through the inline-edit action, including display-only ones. Tables that call updateField() programmatically on a non-editable column must add ->editable() to that column's definition.toggleBoolean() now refuses a column key that is not part of the table's columns(). A key absent from both columns() and the row data previously raised Undefined array key and now returns quietly. The genuine behaviour change is the other case: a key present in the row data but never declared as a column — for example a data() row carrying is_admin with no matching column — used to flip successfully and is now refused.authorizeFieldUpdate() was only consulted on the Eloquent write path. A column with a string editableCallback, and every array/stdClass backed table, wrote with no authorization check at all. Both paths are now gated by the new authorizeFieldUpdateRaw() hook.authorizeFieldUpdateRaw($id, string $field, mixed $value): bool on Editable, defaulting to true. It is the counterpart of authorizeFieldUpdate() for the write paths that have no resolved Eloquent model — a column with a custom update callback, and array/stdClass backed tables — which receive the raw row id instead of a record. authorizeFieldUpdate()'s own signature is unchanged, so existing overrides typed Model $record keep working; override the new hook as well to cover the non-model paths.authorizeToggle($id, string $column): bool on RowManipulators, defaulting to true. Override it to gate boolean toggles the way authorizeFieldUpdate() gates field writes.__PHP_Incomplete_Class under Laravel 13's hardened cache unserialization — which broke rendering with htmlspecialchars(): Argument #1 ($string) must be of type string, __PHP_Incomplete_Class given. Stringable values (Carbon, etc.) are cached as their string form, preserving rendered output.getCachedData() now self-heals — a non-array cache hit (a legacy cached TableCollection or an incomplete-class artifact from a pre-upgrade entry) is discarded and rebuilt from source instead of rendering garbage.TableCollection object, and re-wrapped into a TableCollection on read. This keeps the cache compatible with Laravel 13's hardened unserialization (cache.serializable_classes) without consumers having to allow-list TableCollection. Legacy cached collections are tolerated for the remaining TTL window after upgrading.pestphp/pest: ^4.0, pestphp/pest-plugin-laravel: ^4.0).livewire/livewire: ^3.7|^4.0).orchestra/testbench: ^9.0|^10.0|^11.0)._original, _disabled, _hidden, _card_title) no longer leak into exported data. Previously only _original was stripped.Collection subclass that carries column label metadata from processCollection() through to GenericExport, enabling automatic label resolution without requiring changes to existing export methods.key() helper function instead of key: named argument in [@livewire](https://github.com/livewire)() directive for inline expanded row components. The named argument syntax isn't handled by Livewire's Blade compiler regex, causing a closure parameter error when expanding task rows.isHtml as a class property (public $isHtml = false) so property_exists() returns true on PHP 8.2+. Previously, isHtml was set dynamically in the toHtml() trait method, causing the raw-HTML branch in Blade templates to never trigger.secondaryHeader(callable $callback) method to display a secondary header row below the main header with computed content (e.g., subtotals, averages). The callback receives the current page's rows as a Collection and supports HTML output.soft to glass to match v2.9.5 change.query() method — scope table data by tenant, permissions, or any custom condition without global scopes.newQuery() wrapper that applies $with eager loading automatically on top of query().soft to glass.outline to soft (light tinted background with colored text) for a consistent, subtle look across all header buttons.$yat_button_variant instead of a hardcoded outline attribute, ensuring all buttons match.ArrayObject instead of plain arrays, preserving both $row->key and $row['key'] access in user callbacks (fixes "Attempt to read property on array" regression from v2.9.2).processCollection() and addRowToTable(), preventing "Cannot use object of type stdClass as array" errors when data() returns stdClass objects (e.g. from json_decode() or API responses).setTheme() not propagating to header components.YATBaseTable to BeartropyTable. The old name continues to work via class_alias — no changes needed in existing user code.YATProvider to BeartropyTableServiceProvider. The old name extends the new one for backward compatibility.make:btable now generates extends BeartropyTable instead of extends YATBaseTable.BeartropyTable throughout. Doc files renamed from yat-base-table to bear-table.bt-tables-setup updated with correct artisan command (make:btable) and BeartropyTable references.bt-tables-docs-maintenance updated with correct service provider path.ComponentDocs schema example updated from yat-base-table to bear-table.setComponentSize() method to uniformly resize all header UI components.mixed type from $model property to restore child class compatibility. Typed parent properties cannot be overridden without matching the type exactly in PHP 8.Column logic (instantiation, unique keys, modifiers) and Options logic.TestCase setup with SQLite in-memory database and Pest configuration.yat-table.blade.php:
yat-table.blade.php for better readability and performance.YATableComponent.stub with improved code styling and type hints.How can I help you explore Laravel packages today?