beyondcode/nova-laravel-update-card
A Laravel Nova card that displays your app's installed Laravel version alongside the latest release, flagging available framework updates at a glance so you can keep dependencies current and plan upgrades right from your Nova dashboard.
Before you install anything, answer one question: what Nova major are you on? This card’s only release is 0.0.1 (2018-08-22) and the repo is archived. It was compiled against Nova 1.x/Vue 2. Nova 4+ ships Vue 3, and a 2018-era dist/js/card.js will not mount — you get a blank card and a silent console error, not a helpful failure. If you’re not on Nova 1.x, read this package as reference material, not a dependency.
Step 1 — Reconnaissance spike (throwaway app only). Never try this first in a shared install.
composer require beyondcode/nova-laravel-update-card:0.0.1 --dry-run
--dry-run is the whole point: it surfaces the constraint resolution before it touches your lockfile. If Composer wants to downgrade illuminate/* or PHP to satisfy a 2018 constraint, stop. That is the "this package becomes the reason we can't upgrade" failure mode, and it is far more expensive than the card is worth.
Step 2 — Register the card. In app/Providers/NovaServiceProvider.php:
public function cards()
{
return [
new \BeyondCode\LaravelUpdateCard\LaravelUpdateCard(),
];
}
If the card doesn’t appear, check auto-discovery in vendor/composer/installed.json, then php artisan nova:publish (asset registration in the 2018 era required published assets), then the browser network tab for a 404 on the card’s JS bundle.
Step 3 — Read the source first. Three files tell you everything:
src/LaravelUpdateCard.php — the card class; look at what ->withMeta() it pushes to the front end.src/*ServiceProvider.php — the Nova::serving() closure, which is where Nova::script() registers the bundle.dist/js/card.js — the compiled component; grep it for a URL to find out whether the version lookup happens in the browser or server-side.That last question dominates everything: if the compiled JS calls Packagist or the GitHub API directly from the browser, you inherit CORS behaviour, per-IP rate limits, and an unauthenticated external call made by every admin’s browser session.
First real use case. The card answers exactly one question — "is laravel/framework behind latest stable?" — as a human-visible nudge on a dashboard someone actually looks at. It is not an upgrade process, not a CI gate, and not a dependency-drift monitor.
If you keep the card, scope it so it doesn’t clutter the main dashboard:
public function cards()
{
return [
(new \BeyondCode\LaravelUpdateCard\LaravelUpdateCard())
->onlyOnDashboards(['platform-health']),
];
}
onlyOnDashboards() / canSee() are Nova card APIs, so if they’ve changed, that is the first thing to check when the card renders everywhere or nowhere.
The feature is small enough that ownership wins. Generate a card with your current Nova version and reuse the package’s source as the spec:
php artisan nova:card acme/laravel-currency
Then keep the outbound call on the server, behind a cache, behind one adapter:
// app/Support/LatestLaravelVersion.php
class LatestLaravelVersion
{
public function __invoke(): ?string
{
return Cache::remember('laravel:latest', now()->addHour(), function () {
$res = Http::timeout(3)->retry(2, 200)
->get('https://repo.packagist.org/p2/laravel/framework.json');
if ($res->failed()) {
return null;
}
$versions = collect($res->json('packages.laravel/framework'))
->pluck('version')
->reject(fn ($v) => str_contains($v, 'dev') || str_contains($v, '-'))
->map(fn ($v) => ltrim($v, 'v'));
return $versions->sort(fn ($a, $b) => version_compare($a, $b))->last();
});
}
}
The card itself becomes a thin renderer of two cached strings — installed vs. latest — with a last-checked timestamp. It never blocks a dashboard render on an HTTP call.
The card is for humans; the enforce-able version of the same fact belongs in a job:
composer outdated laravel/framework --direct --strict
--strict exits non-zero when anything is outdated, so it can fail a nightly job and open a ticket. This is the pattern that actually keeps the framework current; the dashboard card is the ambient reminder, and should never be the only signal.
If you do adopt the package as-is, put a single adapter class in front of whatever it calls and point a contract test at it:
it('resolves a latest laravel version', function () {
$this->assertNotNull(app(LatestLaravelVersion::class)());
});
When the upstream endpoint changes shape, this fails in CI instead of silently rendering a stale green card for six months.
Design the failure state explicitly in your own card: if the lookup fails, show Unknown — last checked <time> in a neutral/warning style. A card that keeps showing yesterday’s cached "up to date" after a failed check is worse than no card, because people trust it.
0.0.1 from 2018 resolved against PHP 7.x and illuminate/* of that era. Always --dry-run first, always exact-pin ("0.0.1", no caret) if you proceed, and never run composer update -W to force it.nova.laravel.com Composer repository — including the contract test above. Budget for that before wiring the card into automated checks.dist/js/card.js means the package’s own package.json and webpack version from 2018; expect to nvm use 10 and fight it. This is the strongest argument for php artisan nova:card against current Nova instead.version_compare handles 8.0.0-RC1 < 8.0.0 correctly, but you must strip a leading v, filter dev-* branch aliases, and decide whether you compare major.minor or full semver. Comparing 8.83.27 against latest 11.x is correct; comparing branch aliases is not.laravel/framework. Not Symfony components, not Nova itself, not transitive dev tooling. Do not let a green card stand in for dependency management — pair it with composer outdated or a dependency bot.nova:publish → browser console for a mount error → the service provider’s Nova::serving() closure. The card class itself is almost never the problem.How can I help you explore Laravel packages today?