beyondcode/nova-tinker-tool
Nova Tinker Tool adds an interactive Artisan Tinker console to Laravel Nova, letting admins run PHP and tinker commands directly from the dashboard without opening a terminal. It speeds up debugging, quick data checks, and experimentation in a familiar Nova interface.
Confirm the prerequisites first. This is a Laravel Nova tool, not a stand-alone artisan command. You need a working Laravel app, a paid Nova install, and laravel/tinker present. If you're not on Nova 1–3 era, stop here — the tool predates Nova 4/5 and won't mount cleanly without patching.
Install: composer require beyondcode/nova-tinker-tool. Nova tool providers generally auto-discover; if not, register BeyondCode\NovaTinkerTool\NovaTinkerToolServiceProvider in config/app.php.
Register the tool in app/Providers/NovaServiceProvider.php:
public function tools()
{
return [
new \BeyondCode\NovaTinkerTool\NovaTinkerTool(),
];
}
Where to look first: the tool class (NovaTinkerTool.php), the service provider (for route registration and any canSee/authorize hooks), and the Vue component under resources/js (for the REPL input/output shell). The whole package is ~150 lines — read it end to end before wiring it up, so you know exactly what route the front end posts to and how it hands code to Tinker.
First use case: open Nova → click the Tinker entry in the sidebar → run a read-only probe:
App\Models\User::count()
App\Models\Order::latest()->first()
App\Models\User::find(42)->orders()->pluck('status')
This is the canonical day-to-day loop: inspect a model, check a relationship, sanity-check a query — without SSH, php artisan tinker, or a deploy.
Ad-hoc data inspection during triage. The highest-value daily use: a support ticket references a customer, order, or specific record ID, and you confirm state in seconds from the admin panel. Reach for it when you'd otherwise ssh into a box or add a throwaway route.
Query prototyping before it becomes code. Write the Eloquent query in the REPL, confirm the shape of the result, then move it into a controller/repository/command. This is where the tool actually earns its keep — it shortens the inspect → write → test loop.
Reproducing a customer's state. Load the exact records a reporter sees, walk the relationship graph ($order->customer->subscription->plan), and confirm whether the reported symptom is data or logic. Faster than a local seed for one-off investigation.
Gate it explicitly — do not rely on Nova's default. Attach a canSee/authorization closure so the tool only renders for the right person in the right environment:
(new NovaTinkerTool())->canSee(function ($request) {
return app()->environment('local', 'staging')
&& $request->user()?->email === '[email protected]';
});
Better yet, register the tool conditionally in tools() behind an env check, so a production deploy doesn't even carry the route.
Wrap executions with audit logging. Because the tool posts code to a route and returns output, add a middleware or a small wrapper around the execute endpoint that logs user, timestamp, code, result before returning. This is the difference between a governed internal tool and an unaccountable RCE.
Keep a team snippet file. Maintain a docs/tinker-snippets.md with the ten queries the team actually runs (find-by-email, recent failures, subscription state). Engineers paste them in rather than retyping and getting the shape subtly wrong.
Pair it with your incident runbook. The pattern that works: incident ticket → "confirm record state via Nova Tinker" step → documented query → escalate if the state is wrong. It shortens support turnaround without adding SSH provisioning to the on-call path.
Read-only by convention, not enforcement. There is no destructive-query guardrail. Enforce read-only by policy and by who has access — not by hoping people type carefully. If you need hard guards, fork it and add a statement allowlist before the code reaches Tinker.
composer require from Packagist is a starting point, not a dependency you can count on. Realistic model: fork into your own namespace, pin against your Nova major, own the maintenance.dd() and dump() can break the UI. Output rendering is a simple shell, not a full var-dumper viewer. Prefer returning plain values (->toArray(), ->pluck('id')) to get readable output.Model::all() or an accidental while loop hangs the PHP-FPM worker serving that request. Keep queries bounded with take()/limit(), and kill the worker if it wedges.storage/logs/laravel.log when the panel goes quiet.vendor:publish targets. Configuration is entirely code (registration and gating).NovaServiceProvider::tools(), the provider is discovered, and any canSee closure actually returns true for your user in the current environment.Artisan::call('tinker', ['--execute' => $code]) with an explicit allowlist, env check, and audit log reproduces the useful 90% in a few dozen lines you actually own.How can I help you explore Laravel packages today?