rmzamora/admin-bundle
An admin bundle for Laravel that packages backend administration scaffolding—layouts, dashboards, CRUD resources, and access control—into one installable set, helping teams stand up a management panel quickly while keeping configuration centralized and customizable.
Before anything else: rmzamora/admin-bundle is a fork of sonata-project/admin-bundle (the Symfony admin generator), it is archived, has 0 stars and 0 dependents, and keeps the upstream Sonata\AdminBundle\* namespace. Unless you have a specific reason to pin to this fork (e.g. a vendored commit that fixes something upstream already fixed years ago), the correct first step is:
composer require sonata-project/admin-bundle
If you are genuinely onboarding onto this fork, treat it as "SonataAdminBundle with an unknown patch level":
composer.json first, not the README. Check the replace/provide block and the symfony/* and sonata-project/* version constraints — a fork's constraints are usually frozen at the moment it was cut, and that is what will bite you at install time.git diff <upstream-tag>..HEAD). That diff is the entire value proposition of the fork; if it is empty or only composer/CI noise, you have no reason to be here.config/bundles.php (or AppKernel::registerBundles() on older Symfony) alongside its hard dependencies: SonataCoreBundle, SonataBlockBundle, SonataDoctrineORMAdminBundle, and KnpMenuBundle. The admin bundle alone does not boot.sonata_admin (title, templates), sonata_block (register sonata.block.service.text), a security firewall for /admin, and sonata_doctrine_orm_admin if you want the ORM datagrid.First use case to validate the install is a single-entity admin: one Admin class over one Doctrine entity, registered as a service, visible on the dashboard, with working list/edit/create/delete. Do not start with dashboards, custom actions or security voters — if the CRUD triad works, the wiring is correct.
Because this is a Symfony bundle, "day-to-day" for a Laravel developer usually means one of two things: you are maintaining a legacy Symfony app and inherited this dependency, or you are evaluating whether the Sonata admin paradigm is worth porting. In the second case, stop — Laravel has filament, nova, orchid, or a plain backpack setup that does the same job natively.
The Admin class is the unit of work. Every resource gets a class extending AbstractAdmin (or Sonata\AdminBundle\Admin\AbstractAdmin) with three mandatory concerns:
final class ProductAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $form): void
{
$form->with('Content', ['class' => 'col-md-8'])
->add('name', TextType::class)
->add('description', TextareaType::class)
->end()
->with('Meta', ['class' => 'col-md-4'])
->add('price', MoneyType::class)
->add('publishedAt', DatePickerType::class, ['required' => false])
->end();
}
protected function configureListFields(ListMapper $list): void
{
$list->addIdentifier('name')
->add('price')
->add('publishedAt', null, ['format' => 'Y-m-d'])
->add(ListMapper::NAME_ACTIONS, null, [
'actions' => ['edit' => [], 'delete' => []],
]);
}
protected function configureDatagridFilters(DatagridMapper $filter): void
{
$filter->add('name')->add('publishedAt');
}
}
Register it with the sonata.admin tag, one service per admin. The tag attributes (manager_type, group, label, pager_type, code) drive the menu. In modern Symfony use autoconfigure plus #[Admin]-style tagging in services.yaml:
App\Admin\ProductAdmin:
tags:
- { name: sonata.admin, manager_type: orm, group: Catalog, label: Products }
arguments: [null, App\Entity\Product, null]
The three constructor args are the admin code, the model class, and the base controller — the base controller being an argument you can override, which is how you inject behaviour into every action without touching each admin.
Datagrid over repository over query builder. Resist writing custom repository queries for the list view; use configureQuery on the ModelManager or a DatagridFilter with a callback. The moment you write a custom SQL list you lose pagination, sorting and export for free. If you need a filtered list, create a second admin class (a "child" via getParent() or a sibling service) rather than branching logic inside one class with if ($this->getRequest()->get('...')).
Extensions are the extension point. Anything you would otherwise repeat across admins goes into an AbstractAdminExtension (or AdminExtensionInterface) registered as sonata.admin.extension, optionally as a global extension with global: true. Typical uses: stamp createdBy/updatedBy, attach a MappedParameter, add audit tabs to show views, gate actions per role. This is the single most valuable pattern in the bundle and the one most commonly skipped in favour of copy-pasted prePersist() hooks.
Dashboards and blocks. sonata_admin.dashboard.groups controls the menu groups; blocks are registered under sonata.block and rendered via Sonata's block service. If a block does not appear, it is almost always because it was not registered in the sonata_block.blocks config, not because the template is wrong.
Batch actions and exports are configured per-admin (configureBatchActions() / getExportFields()) and are the fastest way to give non-technical users bulk operations. They respect the datagrid filters, so build the filters first.
Templates are overridable per-admin (getTemplate()), and the bundle's templates live under Resources/views/. Override one template, not the whole CRUD/ directory — the latter is a maintenance trap when you eventually (hopefully) move back to upstream.
AbstractAdmin, service tags, Doctrine ModelManager) have no Laravel equivalents you can lean on. Do not evaluate this package as a Laravel dependency.composer require. Because the fork keeps Sonata\AdminBundle\*, Composer will conflict if any transitive dependency pulls the real sonata-project/admin-bundle. Confirm the fork declares replace: {"sonata-project/admin-bundle": "*"}; if it does not, you will spend an afternoon reading "cannot be installed because it conflicts with itself".composer.json typically pins symfony/* to whatever was current when it was cut. Upgrading PHP or Symfony will produce resolution errors that look like the fork's fault but are just age. Test on a branch with --with-all-dependencies and expect to patch constraints.sonata.admin tag exists on the service, the group attribute is set (no group = no menu entry), the admin class is public/auto-wired correctly, and sonata.admin.pool contains the code (bin/console debug:container --tag=sonata.admin). The bundle fails silently here rather than throwing.bin/console debug:router | grep admin. Missing routes almost always mean the bundle was not registered or the routing import (sonata_admin: { resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml' }) is absent.AbstractAdmin constructor signature drift. Between Sonata 3.x and 4.x the constructor changed (the $baseCodeRoute/$request args). If you copy admin classes from blog posts or from this fork's own tests, they may target the wrong major version and fail with an unhelpful Too few arguments error.add('foo') where foo is not a mapped property silently renders nothing on edit. FormMapper::add() with a wrong property_path is the most common "my field disappeared" cause.vendor/. With an archived fork it is tempting to hotfix in place. Put a patch in a local patches/ directory via cweagans/composer-patches so it survives composer install, and file it as technical debt.AdminExtension, custom AdminController passed as the third constructor argument, custom FieldDescription types, sonata.admin.event_dispatcher events (sonata.admin.event.configure.*), and per-admin template overrides. Anything outside these five is a future upgrade conflict.sonata.admin.security.handler.acl) is off by default. If your admins are role-gated, verify the firewall and the security_handler config explicitly — a misconfigured admin is an unauthenticated CRUD panel, and in an unmaintained fork there is nobody to ship the fix.composer.json (not a branch), vendor-lock the fork ("rmzamora/admin-bundle": "dev-master#<sha>"), and write a one-page migration note to upstream 4.x. That note is the only thing that will make the exit cheap.How can I help you explore Laravel packages today?