poowf/otter
Poowf Otter is a Laravel package for quickly building admin panels and CRUD screens. It provides configurable resources, authentication, role-based access, and a clean interface for managing your application’s data without building a backend from scratch.
Install and first login (roughly 15 minutes):
composer require poowf/otter
php artisan otter:install # publishes config, migrations, assets, seeds an admin
php artisan migrate
Then visit /otter in your browser and log in with the seeded credentials (they're printed by the install command / stored in config/otter.php — change them immediately).
Where to look first, in order:
config/otter.php — this is the heart of the package. The resources array is where you declare every model you want a CRUD screen for. Everything else (fields, labels, validation) hangs off this.config/auth.php and the published Otter migrations — Otter ships its own guard and user table (otter_users) so admin operators are separate from your application users. Confirm the guard name and middleware before you start wiring permissions.public/vendor/otter/ — the compiled Vue/Vuetify front end. Understanding that it's a pre-built bundle tells you early what you can and can't customize.First use case — pick a low-risk internal model, not your User table. A Product, Ticket, or Article model with 5–10 fields is ideal. Register it:
// config/otter.php
'resources' => [
'products' => [
'model' => App\Models\Product::class,
'label' => 'Products',
'fields' => [
['name' => 'name', 'type' => 'text', 'rules' => 'required|max:255'],
['name' => 'price', 'type' => 'number', 'rules' => 'required|numeric'],
['name' => 'published', 'type' => 'boolean'],
],
],
],
Run php artisan config:cache (or at minimum config:clear), reload /otter, and you have a working list/create/edit/delete screen. This is the moment Otter earns its keep: a back-office panel in an afternoon instead of a week.
Before you commit, check compatibility. Otter's last meaningful activity was around Laravel 6/7. Verify the illuminate/* constraints in composer.json against your app version; on Laravel 9+ you may need to fork and bump constraints. Given it has ~0 dependents, treat it as "vendor-and-own" rather than "install-and-forget."
Config-as-schema. The resources array is your admin schema. Keep it close to your models but don't duplicate truth: put rules on fields for form-level validation, and let model casts / mutators handle type coercion. Group resources by domain with a label and icon so the sidebar stays navigable as you add screens.
Relationship fields for foreign keys. Use belongsTo fields rather than raw select where the option list is large — Otter's searchable relationship inputs query via its own API and degrade badly on unindexed tables. Always define the display column (e.g. name) that the search dropdown shows, and add a DB index on it.
Custom actions for anything with consequences. Rather than letting an operator toggle a status column directly, define a custom action (approve, refund, resend, archive) that calls a service class in your app. This keeps business logic in one place, auditable, and testable — Otter becomes the UI, not the brain. This pattern also solves cross-field validation, which per-field rules can't express.
Separate the admin operator model from app users. Otter's own guard means you can hand an ops contractor an Otter account without giving them an application login. If you need to map the two, do it with a nullable user_id foreign key on the Otter user, not by merging tables.
Treat /otter as an internal surface. In config/otter.php, set the route prefix and middleware to something defensible: IP allowlist, auth:otter plus a role check, and ideally a separate DB connection user with limited grants. In production, never leave the seeded demo credentials active.
Use it as a stopgap, scoped honestly. Otter is at its best covering internal CRUD over 5–30 tables where the alternative is a week of hand-built Blade screens. If you need fine-grained authorization, audit trails, custom dashboards, or a design system that matches your product, price out Filament/Nova/Backpack — Otter will fight you on all four.
Iterate schema in source control. Because screens are defined in config/otter.php, resource changes are reviewable diffs. Commit them alongside the migration that adds the column — reviewers can see the admin impact of a schema change in the same PR.
config/otter.php on a cached environment does nothing until php artisan config:clear. "My new field doesn't show up" is almost always this.public/vendor/otter. Re-publish after upgrades and run php artisan view:clear, but don't expect to restyle it without rebuilding from source — budget for that or accept the default look.password type on a model that already has a hashed cast or a setPasswordAttribute mutator, you'll hash twice and lock users out. Verify with Hash::check after a test save.storage/logs/laravel.log — the errors are there, just not in the UI.config/auth.php — Otter's guard is separate. Mixing Auth::user() from your app middleware into Otter views returns null and produces confusing authorization behavior.otter:install twice casually — it re-publishes config and can overwrite local edits or reseed users. Read the command's prompts before confirming.packages/ and take ownership, because upstream is quiet.How can I help you explore Laravel packages today?