Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Easyadmin8 Laravel Laravel Package

easyadmin8/easyadmin8-laravel

EasyAdmin8 for Laravel provides a fast, ready-to-use admin backend for Laravel applications, with common admin features such as authentication, menu management, CRUD scaffolding, and role-based access control to help you build management panels quickly.

View on GitHub
Deep Wiki
Context7

Getting Started

EasyAdmin8-Laravel is a port of the EasyAdmin8 admin framework into Laravel: RBAC (roles, menus, granular permission nodes), menu administration, CRUD scaffolding, auth, and a bundled admin UI. v8.3.5 is a patch release off 8.3.4 with only a compare link, so treat it as a routine dependency bump, not a migration. The class names and artisan signatures below are indicative — the package's own stubs/README are the source of truth, and the fastest way to learn the API is to generate one module and read what it emits.

Install (verify the exact Packagist name first with composer show -a or the repo README; both easyadmin8/easyadmin8-laravel and a shorter alias appear in the wild):

composer require easyadmin8/laravel
php artisan vendor:publish --provider="EasyAdmin8\Providers\EasyAdminServiceProvider"
php artisan migrate --seed
php artisan storage:link

Configure .env for the things that diverge between staging and production:

ADMIN_PATH=admin
ADMIN_GUARD=admin
ADMIN_UPLOAD_DISK=public
ADMIN_PAGINATION=20

Where to look first, in this order:

  1. config/easyadmin.php — route prefix, guard, upload disk, layout/pagination defaults. This is the contract surface; everything else sits behind it.
  2. The package route file (published or vendor) — how the admin route group, middleware, and prefix are assembled.
  3. app/Http/Controllers/Admin/ — where generated controllers land. Read a generated one top to bottom before writing your own.
  4. resources/views/ + the published vendor views — grid and form templates you can override.
  5. The ea_* tables (ea_admin, ea_role, ea_menu, ea_config, plus whatever pivot links roles to menus) — menus and permissions are data, not code. Most "why can't this role see this?" questions end here.

First use case — the loop that exercises ~90% of the day-to-day surface:

Pick an existing Eloquent model (say Order). Scaffold one admin CRUD module for it, register its menu node, attach that node to a non-super-admin role, then log in as that role and confirm (a) the menu item renders, and (b) hitting the controller URL directly returns a 403. If you can do that end to end in an afternoon, the package fits. If the permission gate lets the direct URL through, stop and understand the middleware stack before building anything else on top of it.

Implementation Patterns

CRUD modules. The workflow is: create the model/migration in Laravel normally, then generate (or hand-write) an admin controller extending the package's base admin controller. Define the grid (columns, searchable fields, filters, row actions) and the form (fields, validation, relationships) declaratively. Illustratively:

namespace App\Http\Controllers\Admin;

use App\Models\Customer;
use App\Models\Order;
use EasyAdmin8\Controllers\AdminBaseController; // confirm namespace from a generated stub

class OrderController extends AdminBaseController
{
    protected $model = Order::class;

    protected function grid(): array
    {
        return [
            'columns' => [
                'id'           => ['label' => 'ID', 'sortable' => true],
                'number'       => ['label' => 'Order #', 'searchable' => true],
                'customer.name'=> ['label' => 'Customer', 'searchable' => true],
                'total'        => ['label' => 'Total', 'sortable' => true],
                'status'       => ['label' => 'Status', 'filterable' => true],
                'created_at'   => ['label' => 'Created', 'sortable' => true],
            ],
            'perPage' => config('easyadmin.pagination'),
        ];
    }

    protected function form($id = null): array
    {
        return [
            'fields' => [
                'number'      => ['type' => 'text',    'rules' => 'required|max:32'],
                'customer_id' => ['type' => 'select',  'options' => Customer::pluck('name', 'id')],
                'total'       => ['type' => 'decimal', 'rules' => 'required|numeric|min:0'],
                'status'      => ['type' => 'select',  'options' => OrderStatus::options()],
            ],
        ];
    }
}

Keep grids lean. Every exposed column is a query cost and a rendering cost; every eager-loaded relationship in a grid is an N+1 waiting to happen. Constrain with() explicitly on the list query.

Permissions are data — seed them, don't click them. Menu nodes and role grants live in ea_* tables and are edited through the admin UI, which is fine for exploration and terrible for reproducibility. Put permission nodes in an idempotent seeder keyed on a stable identifier (the permission string), and attach role grants the same way:

$menu = DB::table('ea_menu')->updateOrCreate(
    ['permission' => 'order.index'],   // stable key
    [
        'name'      => 'Orders',
        'parent_id' => 0,
        'url'       => 'order/index',
        'icon'      => 'fa-shopping-cart',
        'sort'      => 10,
        'status'    => 1,
    ]
);

// Pivot/table name for role→menu grants varies by version — confirm against the schema
DB::table('ea_role_menu')->updateOrInsert(
    ['role_id' => $opsRoleId, 'menu_id' => $menu->id],
    []
);

Run this seeder in CI so every environment (and every new dev's laptop) has the same permission graph. Idempotency matters more here than anywhere else in the app: a duplicate permission node produces confusing "granted but denied" behavior that is hard to diagnose from the UI.

Route and middleware integration. Admin routes sit behind a dedicated guard and middleware stack. Add your own cross-cutting concerns (tenant scoping, IP allowlist, audit logging, impersonation guards) by appending middleware to the admin group via config or a service provider — never by editing vendor route files, because those get overwritten on upgrade. For front-end or mobile consumption, expose read-only JSON endpoints outside the admin prefix and reuse the same models/services rather than reaching through admin controllers.

Guard discipline in shared code. The admin guard is not web. Any service, policy, observer, or Blade partial that calls Auth::user() may silently get null inside admin context. Resolve explicitly:

Auth::guard(config('easyadmin.guard', 'admin'))->user();

If you share services between the customer-facing app and the admin, pass the acting user in as a parameter rather than reaching for the facade inside the service.

Config and environment. Drive prefix, guard, disk, and pagination from env. Override only the keys you actually change in config/easyadmin.php; don't fork the whole file, or patch releases that add new keys will leave you with a config that's missing them. When a new config key appears in an upgrade diff, merge it into your override rather than regenerating.

Extending the UI. Publish and override Blade views; never edit vendor files. Keep custom CSS/JS in your own Vite/webpack pipeline, not in the published asset directory — a vendor:publish --force during an upgrade will erase anything you put there. Use the package's view-override path or a view namespace so your overrides take precedence deterministically.

Audit and heavy operations. Wire login/operation logs into your existing log channel and alerting. Admin controllers are synchronous by default: wrap multi-table writes in DB::transaction() and push anything slow (exports, bulk status changes, third-party sync) onto a queued job with a progress indicator. An admin request that takes 30 seconds is a support ticket.

Gotchas and Tips

  • Stale menu/permission cache is the #1 "it doesn't work" cause. After seeding menus or changing role grants, clear the application cache. Then verify the role has the leaf node, not just the parent menu — parent visibility does not imply child authorization.
  • Auth guard mismatch. Auth::user() returning null in an admin-context service, a policy, or a shared Blade component is almost always the guard default. Check config/auth.php and always resolve the admin guard explicitly in admin-facing code.
  • Asset skew after upgrade. Patch releases can change bundled CSS/JS. After composer update, re-publish assets and hard-refresh. A half-updated asset set produces layout and JS errors that look like application bugs and waste an hour of debugging.
  • Route collisions. A custom route sharing the admin prefix can shadow package routes or be shadowed by catch-alls. Run php artisan route:list --path=<prefix> and confirm the middleware and controller actually handling each URL. Register specific routes before wildcards.
  • Upload failures are usually infrastructure, not the package. Check php artisan storage:link, the configured disk's root and permissions, and PHP's upload_max_filesize / post_max_size. Silent no-op uploads skew heavily toward disk/permission misconfiguration.
  • Seeder idempotency. Blind insert() into ea_menu / grant tables fails on re-run and duplicates nodes. Use updateOrCreate / updateOrInsert keyed on the permission string.
  • Debugging order: storage/logs/laravel.log → APP_DEBUG=true locally → php artisan route:list → log the resolved role and permission set at the gate. Don't guess at why a permission was denied; print the set the middleware actually evaluated.
  • Prefer stable seams. Custom middleware, published config, published views, model observers, and events are all safe across patch releases. Deep subclassing of vendor controllers is not — keep overrides shallow and one layer thick.
  • Pin the version. Use "easyadmin8/laravel": "8.3.*" (or the exact constraint matching how you installed it) in composer.json. Because release notes for 8.3.5 are just a compare link, review the commit range yourself before any bump — particularly for changes touching auth, middleware, permissions, or migrations.
  • The one-day rule. If a screen fights the generator for more than a day — unusual layout, heavy computed columns, non-standard workflows — build that screen directly as a plain Laravel controller + Blade view inside the same admin layout. The package's value is the conventional 80%; force-fitting the other 20% is where the time goes.
  • Keep customizations in your own layer. Your controllers, your views, your middleware, your seeders. Nothing in vendor/. That single discipline is what makes the next patch bump a five-minute composer update instead of a merge project.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect