lag/adminbundle
Lag AdminBundle provides a ready-made admin panel for Laravel apps, with CRUD scaffolding, authentication, and customizable dashboards. It streamlines backend management so you can focus on building your application's core features.
First, a reality check for a Laravel developer: lag/adminbundle is a Symfony bundle ("Generic Admin for Symfony"), not a Laravel package. There is no service provider, no facade, no config/adminbundle.php. It expects Symfony's HttpKernel, Routing, Form, Twig and Doctrine wiring. You cannot meaningfully composer require it into a Laravel app. The practical ways to leverage it are: (a) run a small Symfony admin application against the same database as your Laravel app, or (b) if you are being asked to evaluate it as an admin layer, compare it against Laravel-native options (Filament, Nova, Backpack) — this bundle will cost you a second framework, not a package install.
If you are working inside Symfony, the minimal path is:
composer require lag/adminbundle — but understand the release situation before you do. The 1.x line is now tagged v1.0.3, a maintenance bugfix release that deliberately re-adds Symfony 3.4 compatibility "as it is required for the recipe." That is the single most informative line in the changelog: 1.x is being kept alive for legacy/recipe users, not modernised. Meanwhile the repository's own rules describe a v2.0 line targeting PHP 8.4 / Symfony 7.0. Pin explicitly, and if you need modern Symfony go to a commit on feat/v2.0; if you need stability on an old app, take v1.0.3.LAG\AdminBundle\LAGAdminBundle in config/bundles.php, import its routes, and provision the database the admin reads from.assets/, produced through Docker with make assets. Never edit public/build/ by hand.Where to look first, in this order:
src/Metadata/Attribute/ — this is the entire declarative surface. Every admin feature you will use day-to-day is a PHP attribute on an entity class. Read these files before reading any prose; they are the real API.src/DependencyInjection/Configuration.php — the lag_admin config keys. Grep them out rather than guessing.config/services/ — one PHP service file per concern, explicitly wired.templates/ and templates/components/ — the HTML you will be overriding. Note that v1.0.3 fixed the top menu not rendering on some actions (#171) — if you are on an earlier 1.x patch you will be overriding around a bug rather than styling.docs/ — if it exists on your branch. The repository's own documentation-audit skill warns that docs may live unmerged on a docs/* branch or in a sibling worktree. Run git ls-files docs/, git branch -a --list '*docs*' and git worktree list before concluding "nothing is documented."Your first use case: take one existing Doctrine entity, decorate it with the resource attribute plus a handful of property attributes, declare an index operation, and load the generated grid route in the browser. That single round-trip teaches you the whole mental model: attributes → metadata → provider/processor → view builder → Twig component. As a first upgrade sanity check on v1.0.3, do it on a CRUD action too — that is exactly what the top-menu fix (#171) touched.
Attribute-driven resources. Declare admin resources by annotating entities. Operation and property metadata are attributes too. Metadata objects are immutable — variants are produced with with*()-style methods, never by mutation. Treat the attribute classes as the authoritative reference for every available argument; the skill guidance in this repo explicitly says concept pages explain and reference pages enumerate, and the code is the source of truth when docs lag.
Program against interfaces, not implementations. The bundle is built around ResourceInterface, OperationInterface, PropertyInterface, ProviderInterface and ProcessorInterface. Type-hint the interface in your own services. This is not style pedantry here — the concrete classes are wired explicitly in config/services/, and decorating an interface-typed dependency is the supported extension route.
Extend by decoration and events, not by subclassing controllers. The design convention in this repository is explicit: new behaviour is added by decorating a provider or processor, or by listening to a ResourceControllerEvent. Discover the event names with grep -rhoE "const string [A-Z_]+ = '[a-z_.]+'" src/Event/ and dispatch-side effects (audit logging, cache busting, notifications) from listeners. Forking the controller is the path to upgrade pain.
State provider / processor split. A provider resolves the data an operation needs (list, item, form initial data); a processor handles the write path. For non-Doctrine data sources — an external API, a read replica, a legacy table — implement a provider rather than fighting the ORM integration. For side effects on save, decorate the processor.
Grids and views. Grid definitions are assembled through view builders, then rendered by Twig components. Data transformers live under src/Grid/DataTransformer/ and condition matchers under src/Condition/Matcher/; both are the right hook for computed columns and conditional display without touching templates. Column labels come from the bundle's translation layer — v1.0.3 fixed a bug there, so if you have locally worked around wrong column headers with hardcoded label options, re-test after upgrading and consider dropping the override.
Twig components and overrides. The bundle renders through Twig components with a PHP class in src/Twig/Component/ and a template under templates/components/. Override a template by recreating it at templates/bundles/LAGAdminBundle/components/<name>.html.twig in your application — the bundle is a standard Symfony bundle, so getPath() returns its root and that is the override path. Swap a component wholesale by redefining the template and exposing the same context its PHP class provides. If you have a custom base layout that patches around the missing top menu, v1.0.3 makes that patch redundant — remove it rather than stacking two fixes.
Bridges for optional integrations. Third-party integrations live under src/Bridge/<Vendor>/ and must degrade gracefully when the dependency is absent. If you need an integration that is not there, follow the same layout: it keeps the core free of hard dependencies and makes the feature removable.
Testing your admin. Functional tests boot LAG\AdminBundle\Tests\Application\TestKernel and need the MySQL/MariaDB container. Fixtures use zenstruck/foundry factories in tests/fixtures/, and final classes are mockable through dg/bypass-finals. Mirror the src/ tree under tests/unit/ for unit tests. Copy this harness into your own app if you want regression coverage on your resource declarations — the metadata layer is very testable without a browser.
feat/v2.0 line (PHP 8.4 / Symfony 7.0) remains unreleased. Decide which tree you are on, pin it, and read the code on that tree. Half the complaints about "missing features" against this bundle are branch confusion.make var-dump-checker scan that fails on dump()/dd()/var_dump()/print_r() — wire an equivalent into your CI so your forks and overrides never ship one.src/. Budget accordingly, and expect to maintain your own upgrade notes — v1.0.3 is a good moment to start one.with*() variant and rebuild the chain.ResourceControllerEvent listeners. A controller override will compile and work — and then break on the next minor change. Note that #171 was a menu rendering bug in controller-adjacent plumbing; that is precisely the kind of fix you get for free by staying out of the controller path.templates/bundles/LAGAdminBundle/… — a typo, or creating the file under your app's templates/components/ without the bundle segment, means Symfony silently uses the bundle's version and you debug a template that is never loaded. bin/console debug:twig is the fastest way to confirm which file actually wins. After upgrading to v1.0.3, re-check any menu/layout override: the bundle's own template changed, and a stale override will quietly shadow the fix.public/build/. Front-end assets come from Encore via make assets. Edits vanish on the next build and produce confusing diffs.declare(strict_types=1) on every file, @Symfony + @Symfony:risky rulesets, trailing commas in multiline calls, no Yoda conditions, elseif only, PHPStan level 6 on src/ and config/ with annotated array shapes and generics. Write code that passes on the first try rather than leaning on make cs and make rector.AdminDataCollector so it does not try to display data when an exception occurred during configuration. Practical upshot: a broken resource declaration may now show up as an empty admin panel entry in the web debug toolbar rather than a loud stack trace. When something is wrong, check the toolbar panel and the logs together — the profiler no longer guarantees it will surface the failure for you.grep -oE "(arrayNode|scalarNode|booleanNode|integerNode)\('[a-z_]+'\)" src/DependencyInjection/Configuration.php | grep -oE "'[a-z_]+'" | tr -d "'" | sort -u gives you the authoritative list, including keys whose documentation is missing or stale. The same trick works for Twig functions/filters (src/Twig/Extension/) and component keys (config/services/twig.php).How can I help you explore Laravel packages today?