laravelrus/sleepingowl
SleepingOwl Admin is an open-source Laravel admin panel for quickly building CRUD interfaces, managing Eloquent models, and customizing dashboards with Blade views, filters, and access controls—ideal for back-office tools.
Install via Composer and run the package's installer:
composer require laravelrus/sleepingowl
php artisan sleepingowl:install
On Laravel 5.5+ the service provider is auto-discovered; on older versions add SleepingOwl\Admin\SleepingOwlServiceProvider to config/app.php. The install command is the important step — it publishes config/sleepingowl.php, copies the admin CSS/JS into public/, and scaffolds an app/Admin/ directory containing a bootstrap.php, routes.php, and a Models/ folder for per-model configuration classes.
Check your Laravel version first. The current 10.x line still runs on older Laravel, but the maintainers have announced that the next major release drops everything below Laravel 9 and requires PHP >= 8.1. If you're starting a new project on Laravel 8 or below, treat SleepingOwl as a short-term choice and budget for the upgrade (or pin to 10.x and stay there).
Then register a model in app/Admin/bootstrap.php:
use SleepingOwl\Admin\Admin;
Admin::registerModel(\App\Models\User::class);
The panel is served at /admin by default, using your existing Laravel auth guard. From there you get a fully working CRUD interface for the model — listing, create, edit, delete, bulk actions — before you write a single form field.
Where to look first, in order:
config/sleepingowl.php — route prefix, middleware, auth guard, title/logo, dashboard widgets.app/Admin/bootstrap.php — the single entry point that registers everything.app/Admin/Models/ — one configuration class per model; this is where 90% of your day-to-day work lives.vendor/laravelrus/sleepingowl/src) when the docs run dry — the base Column/Field classes are readable and show the full method surface.The canonical first use case: take an existing Eloquent model (User, Post, Product), register it, add a few columns and a few form fields, and you have a usable back office in under an hour.
One configuration class per model. For every registered model, create a matching class in app/Admin/Models/ that extends the package's base model configuration. The class exposes columns(), form(), and display() methods that return arrays of element objects:
namespace App\Admin\Models;
use SleepingOwl\Admin\Columns\Column;
use SleepingOwl\Admin\Display\Display;
use SleepingOwl\Admin\Form\Field;
class Post extends \SleepingOwl\Admin\Models\Model
{
public function columns()
{
return [
Column::text('id', 'ID'),
Column::text('title', 'Title'),
Column::related('author.name', 'Author'),
Column::datetime('created_at', 'Created'),
Column::action('edit'),
];
}
public function form()
{
return [
Field::text('title')->required(),
Field::select('status')->options([
'draft' => 'Draft',
'published' => 'Published',
]),
Field::wysiwyg('body'),
Field::image('cover')->disk('public')->path('covers'),
Field::related('author_id', 'Author')->relatedModel(User::class),
];
}
public function display()
{
return [
Display::text('title')->label('Title'),
Display::text('body')->label('Body'),
];
}
}
Use the fluent element builders. Fields and columns are chainable. Common tweaks: ->required(), ->helpText(), ->setWidth(), ->orderable(), ->hideOnCreate(), ->filterable(), ->sortable(). Reaching for these covers most UI requests without touching package internals.
Handle relations explicitly. For belongsTo use a related-field/related-column. For hasMany/belongsToMany you typically embed a related-table section inside the form, or manage them through their own registered model and expose them via navigation. Relation handling is where most configuration effort goes.
Customise the dashboard and menu. Widgets are added in the config or a dedicated admin service provider. The navigation is usually built with Admin::addMenu(...)-style calls or by generating sections automatically from registered models; either way, keep menu definition in one place (a service provider or bootstrap.php) rather than scattering it.
Use model hooks for business logic that isn't a form rule. If a slug must be generated before save, or an image needs resizing after upload, register save/delete callbacks on the model configuration rather than duplicating logic in controllers. This keeps the admin behaviour declarative and testable in isolation.
Override views instead of editing the package. Publish the views you need (vendor:publish with the package's tag) into resources/views/vendor/sleepingowl and override only the templates you care about. Same for CSS/JS — put overrides in your own asset file, not in the published vendor bundle.
Access control. Use the middleware and auth settings in config/sleepingowl.php (e.g. ['web', 'auth', 'can:access-admin']) as the outer gate. For per-section restrictions, add conditional logic in the model configuration or route middleware rather than editing package source — the package isn't opinionated about roles, so wire it into your existing Gate/Policy setup.
Extend with custom fields and columns. When a built-in element doesn't fit (e.g. a map picker, a colour picker, a JSON editor), extend the base field/column class, implement the render/format methods, and register it. This is the sanctioned way to add UI without forking.
laravelrus/sleepingowl to the current major in composer.json so a routine composer update can't drag you across the boundary.composer.json Laravel/PHP constraints are the source of truth; verify against your app version before adopting it. If you're on the latest Laravel, pin versions and test the upgrade path in a branch rather than in place.public/vendor/sleepingowl folder is the classic "admin page looks broken / CSS missing" cause. Re-run the installer (or vendor:publish --force with the package tag) and hard-refresh the browser.php artisan config:clear, view:clear, and a browser cache-bust. A surprising number of "my changes don't apply" reports are a config or view cache.bootstrap.php, (b) the configuration class's namespace/path matches what the package expects, and (c) storage/logs for a swallowed exception. Keep a copy of bootstrap.php open while debugging — registration is where most mistakes happen.Column::related('author.name') will fire a query per row unless the relation is eager-loaded. Set $model->with([...]) on the section or use the package's "with" hook, otherwise an admin listing of 500 rows becomes 500+ queries.php artisan storage:link plus an explicit ->disk('public') and path is the safest setup; the default disk often differs between environments./admin overlaps with an existing route, change route_prefix in the config. Make sure the auth middleware stack matches what you use elsewhere — the admin panel is a real part of your app, not a separate one.How can I help you explore Laravel packages today?