slowlyo/owl-admin
Owl Admin is a Laravel-based admin panel built on the amis frontend framework, designed to speed up CRUD and internal tool development. It offers configurable pages, forms, tables, menus, and permission controls, reducing boilerplate for management systems and backend dashboards.
Owl Admin (slowlyo/owl-admin) is a Laravel admin panel built on top of amis — you write PHP controllers that return amis JSON schema, and the package renders the UI. Day-to-day it replaces hand-rolled Blade CRUD screens.
Install and bootstrap:
composer require slowlyo/owl-admin
php artisan admin:install # publishes config, migrations, seeds admin user + frontend assets
php artisan migrate
php artisan storage:link
Then log in at the route prefix from config/admin.php (default /admin) with the seeded account — change that password immediately.
Where to look first, in this order:
config/admin.php — route prefix, auth guard, database connection, upload disk, layout/title/logo, asset path. Almost every "why does it look wrong" question ends here.app/Admin/Controllers — every admin screen is a controller returning an amis schema. This is where you will spend your time.admin_pages table — pages are registered as DB records pointing at a controller, so a new page can exist as a file but not be routable until the record exists.php artisan list admin — discover the available admin:* commands for your installed version before assuming a generator command exists.First real use case: build one CRUD screen for an existing model end-to-end (list + filter + create/edit form + delete), wire it into a menu, and confirm it works for a non-super-admin role. That single loop exercises routing, schema, permissions, uploads and the asset pipeline — the four things that break on upgrade.
After every composer update slowlyo/owl-admin, run:
php artisan admin:publish --assets --force
php artisan optimize:clear
This is not optional. The package ships compiled frontend assets in public/; PHP code and JS drift apart silently if you skip it.
Controller-as-schema. Treat the admin controller as a view layer, not a place for business logic. Return schema arrays built from small private methods (listSchema(), formSchema()), and push all writes through a service class or model method so the same logic is callable from jobs, imports and tests. When a screen gets complicated you want to be able to reproduce the bug without the browser.
Keep large schemas out of PHP. A 600-line nested array is unmaintainable. Either split into per-component methods, or store the schema as a .json file / Blade partial and json_decode it, so frontend-heavy edits don't require a PHP redeploy.
One base controller for your app. Extend the package controller once (App\Admin\BaseController) and put shared behaviour there: common response helpers, current-admin accessors, audit-log writes, permission checks. Upgrades then touch one file instead of thirty.
Permissions as a first-class step. New pages are invisible to non-super-admins until the permission entry exists. Build the habit: create page record → assign permission to role → verify with a non-super-admin login. Do it in the same commit as the page, not "later".
Auth guard discipline. The panel uses a separate admin guard. Anywhere outside the admin request cycle — middleware, policies, queued jobs, notifications — resolve the user explicitly (Auth::guard('admin')->user()), never auth()->user().
Menu and navigation. Register menus alongside the page rather than hand-editing the DB; keep menu structure in a seeder or a small registration service so a fresh environment reproduces production navigation from code.
Custom frontend, minimally. If you need custom JS/CSS or amis custom components, register them at the config/asset level and keep the source in resources/, never in the published asset directory. One custom component file plus a build step is sustainable; a patched vendor asset is not.
Environment parity. Admin routes, seeding and the page generator should be disabled or guarded in production. Gate panel access by permission, not just by "the URL is obscure".
--assets --force overwrites published frontend files. Anything you hand-edited under the published asset path is gone after an upgrade. This is the number one upgrade incident for this package. Rule: published assets are build output, not source. Overrides go in your own public/ or resources/ path and are referenced through config.
Stale assets after composer update. Symptoms: blank panel, components missing, "component not found" errors, console 404s on hashed JS. Cause: new PHP with old JS. Fix: admin:publish --assets --force + hard refresh (the browser caches amis bundles aggressively). Put both steps in your deploy script:
composer install --no-dev --optimize-autoloader
php artisan admin:publish --assets --force
php artisan migrate --force
php artisan optimize
PDF preview (the v4.7.5 fix). The pdf viewer needs a resolvable pdf.worker.js URL. Historically a broken default made PDFs render blank or spin forever. After upgrading: clear config cache, then verify the worker source setting in config/admin.php resolves correctly when assets are served from a CDN, a subdirectory, or a non-default APP_URL. If PDFs still fail, check the browser network tab for the worker request — a 404 there is the whole bug.
Small-screen layout (the v4.7.5 fix). The sidebar collapse/auto-switch now keys off matchMedia against the viewport instead of a narrower heuristic. More reliable on real phones and when the window is resized, but if you override layout CSS or breakpoints, re-test around 768px and after rotation. Custom CSS that assumed the old trigger point may now fight the built-in behaviour.
Config and route caching. Pages live in the database, so route:cache/config:cache can mask newly created pages and edited config. When "the page isn't there", the answer is almost always php artisan optimize:clear, not a code bug.
Uploads and previews are path-sensitive. storage:link missing, a non-public disk, or a misconfigured asset/URL base breaks image and PDF preview while the upload itself succeeds silently. Check the stored file exists on disk before debugging the frontend.
Errors inside the panel come back as JSON. With APP_DEBUG=true a PHP exception surfaces as an amis error toast or a broken component rather than a Laravel error page. Reproduce the failing request directly (network tab → copy the endpoint → curl/Postman with the session cookie) and read storage/logs/laravel.log.
Pin the minor line. ^4.7 is appropriate: 4.7.x has been fixes-only (asset/config corrections, responsive behaviour), no schema or API breaks announced. But since the frontend ships precompiled, treat every patch as "requires republish" and version your deploy step accordingly — a rollback must roll back the assets too.
Extension points to prefer. Package config → custom base controller → custom amis components → (last resort) vendor patches. Vendor patches vanish on the next composer update and are the hardest thing to explain to whoever inherits the project.
How can I help you explore Laravel packages today?