vrana/adminer
Adminer for Laravel embeds the single-file Adminer database manager as a drop-in phpMyAdmin replacement. Publish the config, open the route, and browse, query and edit MySQL, PostgreSQL, SQLite and other databases inside your app, with auth and middleware support.
vrana/adminer is not a Laravel package — it is the upstream Adminer distribution shipped as a Composer dependency. There is no service provider, no config file, no auto-discovery. Installing it gets you a large adminer.php inside vendor/, and nothing else. The whole job is getting that file reachable from your app.
composer require vrana/adminer --dev
Then the quickest possible win: symlink or copy the entry file into public/ and open it.
ln -s ../vendor/vrana/adminer/adminer.php public/adminer.php
php artisan serve
# http://localhost:8000/adminer.php
Log in with the same credentials as your .env (DB_HOST, DB_DATABASE, DB_USERNAME, DB_PASSWORD), pick the driver (MySQL/PostgreSQL/SQLite), and you have schema browsing and ad-hoc SQL for the current project without installing a desktop client.
Where to look first inside vendor/vrana/adminer/:
adminer.php — the entry point; read the top ~100 lines to see how it bootstraps and where adminer_object() is resolved.plugins/ — one file per plugin, each a working example of the extension API. This is the best documentation the project has.designs/ — CSS-only themes.lang/ — translations, loaded by locale.First real use case: inspect a migration you just wrote, or run a EXPLAIN on a slow query, without leaving the repo. Do not try to replace artisan migrate, seeders, or tinker with it.
Route-based mounting (preferred over a public file). A raw file in public/ bypasses all Laravel middleware, so you can't gate it by environment. Mount it through the router instead:
// routes/web.php
if (app()->environment('local')) {
Route::get('/adminer', function () {
require base_path('vendor/vrana/adminer/adminer.php');
})->withoutMiddleware([
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
]);
}
Adminer emits its own headers and calls exit on several paths, so treat the route as a thin passthrough, not a controller you can post-process.
Use a wrapper for team installs. onecentlin/laravel-adminer wraps vrana/adminer, publishes config, registers the route, and applies auth middleware. If more than one developer needs this, the wrapper is less friction than hand-rolling the route. You still end up depending on vrana/adminer underneath.
Prefill credentials. Adminer reads $_GET/$_POST for server, username, db, driver. Seed those from Laravel's config before the require so devs never retype:
$_GET += [
'server' => config('database.connections.mysql.host'),
'username' => config('database.connections.mysql.username'),
'db' => config('database.connections.mysql.database'),
'driver' => 'server',
];
require base_path('vendor/vrana/adminer/adminer.php');
Recent 5.x releases also honor ADMINER_DEFAULT_SERVER / ADMINER_DEFAULT_DRIVER / ADMINER_DEFAULT_DB environment variables, but pre-seeding $_GET is the version-independent path.
Plugins via adminer_object(). Define the function in a dedicated file and load it before adminer.php:
function adminer_object(): \Adminer\Plugins {
return new \Adminer\Plugins([
new \Adminer\PluginLoginPasswordLess,
new class extends \Adminer\Plugin {
public function headers(): void { /* ... */ }
},
]);
}
Read-only guard for shared/staging environments. A small \Adminer\Plugin subclass that refuses POST when the query is destructive, or that overrides dumpFormat(), is far cheaper than trusting everyone to be careful. The readonly plugin in plugins/ is a starting point.
Auto-login bridge. Subclass \Adminer\Adminer (or a plugin hooking login()) to permanently log in using Laravel's DB config, then gate the route behind auth middleware. Good for internal tools; never for anything internet-facing.
/adminer 404s, you skipped the mounting step.Adminer / AdminerPlugin to Adminer\Adminer / Adminer\Plugins. Most blog posts and plugin snippets you'll find are 4.x and will fatal. Check the installed version before copying code.ValidateCsrfToken blocks Adminer's form submissions. Either exclude the route (->withoutMiddleware(...)) or mount it in a middleware group without CSRF. Symptom: "Page expired" on login.session_name('adminer') before the require, or isolate the route on a subdomain.exit/die bypasses the framework. Laravel's termination callbacks, Telescope, Debugbar, and exception handler never see Adminer's output or errors. Debug Adminer with PHP's own error log, not storage/logs/laravel.log.LOAD DATA surface is enormous. Keep it in require-dev, rely on composer install --no-dev in deploys, and additionally gate the route by app()->environment('local') and auth. Belt and braces. If you must expose it remotely, put it behind an IP allowlist plus basic auth plus throttle.database/database.sqlite; no server config needed and it doubles as a quick sanity check that your migrations produced the schema you expected.mysqldump/pg_dump; use Adminer's export only for small tables and schema-only dumps.adminer.php is a committed build artifact and upstream restructures it between majors (see the namespace change). Constrain with "vrana/adminer": "^5.0" in require-dev so a composer update doesn't silently break your adminer_object() or plugin classes.adminer_object() must be global. Defining it inside a namespaced file or a class method means Adminer won't find it and plugins silently don't load. Put it in a plain app/Support/adminer.php with no namespace, and require it first.adminer.css next to the entry point or reference one from designs/; there is no config setting for it.How can I help you explore Laravel packages today?