Skip to content

v1.5.0

Choose a tag to compare

@mpge mpge released this 09 Sep 14:59
· 6 commits to main since this release
788f858

563 tests / 2076 assertions. Pint and PHPStan level 5 clean; the suite runs on SQLite, MySQL 8 and Postgres 16.

Four fixes, all found consuming 1.4.0 in production. No breaking changes.

Upgrading

If you are on 1.4.0 or earlier, republish the panel assets on this upgrade. The compiled Vue bundle is a published file under public/, so updating the package replaces the PHP and leaves that file exactly where it is. A panel in that state passes every server-side check — the version constant, the new classes, the new methods — while rendering the previous frontend, and the only symptom is that the feature you upgraded for appears to be missing.

On a stock Laravel application the new laravel-assets tag (below) handles this for you on this upgrade already: Composer writes the new package files before running post-update-cmd, and vendor:publish resolves its paths from the booted provider — 1.5.0's by then. Run php artisan saddle:upgrade by hand if you removed that line from post-update-cmd, or if you deploy from a build artifact where Composer scripts do not run.

The one that cost a deploy

The compiled bundle now publishes under laravel-assets as well as saddle-assets. (#25)

laravel-assets is the tag a fresh Laravel application's own composer.json calls on post-update-cmd, and it is the only thing that republishes a package's compiled frontend automatically. Published under saddle-assets alone, every consumer hit the stale-bundle failure above on every upgrade unless they had happened to wire saddle:upgrade into their deploy — a silent failure that misdiagnoses itself. Two of 1.4.0's five features were invisible in production for one consumer this way.

A test asserts the laravel-assets tag carries the bundle and nothing else. It runs unattended on every composer update, so config, migrations or language files riding along on it would silently overwrite a host's customised copies.

Fixes

A false boolean cell no longer shares the em dash that means "no value". (#26)

true rendered an accent check and false rendered —, so the pair read as "yes / unknown" rather than "yes / no". That is actively misleading, because an em dash already means no value elsewhere in the panel — it is what a StatWidget shows for a null. On a table of integrations, "switched off" and "we have no idea" rendered identically, and those are different facts.

false now renders a muted cross. The em dash is reserved for a boolean cell with no value at all, which BooleanColumn never produces on its own but formatUsing() now can:

BooleanColumn::make('connected')
    ->formatUsing(fn (mixed $value, Model $record) => $record->checked_at === null ? null : $value),

The read-only view page had the same collision and gets the same fix. All three cell states carry a translated aria-label across the fourteen shipped locales — the old Yes/No labels were hardcoded English, and adding an untranslated Unknown would have made that worse.

Developer experience

A widget hidden for having no $resource now says so. (#27)

The 1.4.0 fail-closed default is right, but a widget that declares no resource and does not override canSee() simply did not appear, with nothing anywhere saying why — the dashboard was just missing a tile. It now throws a LogicException naming the class in the local environment and logs a warning everywhere else. The split is deliberate: a live panel losing a tile must not become a live panel losing its dashboard.

There is nothing to silence — declaring $resource, overriding canSee(), or turning require_widget_resource off all make the widget authorizable, and an authorizable widget says nothing. Nor does one whose policy simply denies the current user; that is a decision rather than a misconfiguration.

Form::toArray() and Table::toArray(). (#28)

Field::toArray() and Column::toArray() serialize a leaf, while the containers named the same operation toInertia() — so Form::toArray(), the reasonable guess, returned Call to undefined method. Both names now work on both classes. Aliases, not renames: toInertia() is public API. Tests assert the two names return the identical array and that neither bypasses canSee().