kunicmarko/sonata-auto-configure-bundle
A Symfony bundle for Sonata Admin that uses convention over configuration to automatically register and configure admin classes and services, reducing boilerplate so you can focus on building CRUD screens and management interfaces with less manual setup.
Treat this as scaffolding, not infrastructure: it is a small compiler-pass bundle (MIT, archived since 2019) whose entire job is to turn App\Admin\FooAdmin into a tagged sonata.admin service so you stop writing service definitions by hand. It targets PHP 7.1+, Symfony 4.x, and Sonata Admin 3.x with the Doctrine ORM admin.
Minimal path:
composer require kunicmarko/sonata-auto-configure-bundle:^0.7.1
config/bundles.php):
KunicMarko\SonataAutoConfigureBundle\SonataAutoConfigureBundle::class => ['all' => true],
src/Admin:
# config/packages/sonata_auto_configure.yaml
sonata_auto_configure:
admin:
suffix: Admin
manager_type: doctrine_orm
# dir: '%kernel.project_dir%/src/Admin'
The pass scans that directory and registers every *Admin class, deriving model_class by stripping the suffix and swapping the admin namespace for the entity namespace (App\Admin\PostAdmin → App\Entity\Post).namespace App\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Form\FormMapper;
final class PostAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $form): void
{
$form->add('title');
}
}
bin/console debug:container --tag=sonata.admin
First place to look when behaviour surprises you: the bundle's DependencyInjection/Configuration.php (every option and default) and its compiler pass (the naming and entity-inference rules). Those two files are the package; everything else is a one-line extension class.
Naming is the API. <Name>Admin in the admin namespace maps to <Name> in the entity namespace. Keep that mapping one-to-one and the bundle stays invisible; the moment it isn't one-to-one you are fighting it.
Convention first, manual escape hatch. Auto-configure the bulk and hand-write the two or three admins that need a custom admin_code, a non-standard model_class, or a specific translation domain. Verify the pass actually skips classes that already have a definition — if it doesn't, you get two definitions for one class and the tag can end up on the loser.
Global defaults in config, per-admin overrides in configure(). CRUD controller, group, label, translation domain belong in the YAML so they're applied uniformly; anything admin-specific belongs in the class.
Global (Sonata) extensions stack — on 0.7.1+ only. If you register several global admin extensions (e.g. a label/translation extension plus your own), keep them in services.yaml as usual and pin ^0.7.1: earlier 0.7.x wired only the first one, which manifests as "extension is registered but never runs" on some admins. Re-verify after the bump with debug:container --tag=sonata.admin.extension that all of them are present.
Multiple managers. Use one config block per manager type pointed at separate directories (src/Admin/Orm, src/Admin/Odm) rather than trying to have manager_type resolved per class. Same trick applies to multiple entity namespaces.
Keep base classes out of the scan. An abstract BaseAdmin sitting in src/Admin gets registered as a concrete admin and shows up on the dashboard or blows up routing. Put parents in src/Admin/Base/ or make them genuinely abstract and confirm the scanner reflects on that.
Autowiring is the thing to validate on day one. A compiler pass that builds Definition objects by hand does not inherit _defaults: autowire: true unless it explicitly copies them. Write one admin with a constructor-injected repository before you migrate twenty, and check it with debug:container App\Admin\FooAdmin.
CI guard. A small kernel/container test asserting the exact set of sonata.admin tagged ids catches the real failure mode of this bundle: someone renames an entity or moves a class and an admin silently disappears (or a wrong model_class appears) instead of erroring.
Coexistence. Many teams leave services.yaml resource-loading for App\Admin\ and use the bundle only for the tag attributes. That's the least surprising split, but pick one mechanism per directory.
^0.7.1 if you use more than one global admin extension; otherwise 0.7.0 and 0.7.1 are behaviourally identical and either is fine. There is no maintainer to backport anything below your pin.bin/console cache:clear when it "doesn't show up."model_class. Sonata fails late — at dashboard or route time — not at compile time, so the error message rarely points at the naming convention.services.yaml also resource-loads the same directory, you get two definitions for one class; one wins silently and the tag may be on the other. Symptom: "the admin is registered but has no tag."admin_code collisions when two admins share a short name in different subnamespaces (Blog\PostAdmin vs Shop\PostAdmin).manager_type or a missing sonata_doctrine_orm_admin surfaces as a runtime exception about no manager being available, not as a config error at boot.ROLE_* in the hierarchy or Sonata's security settings looks exactly like "the admin isn't there."debug:container --tag=sonata.admin → debug:container App\Admin\FooAdmin → debug:container --tag=sonata.admin.extension (when extensions misbehave) → debug:router | grep admin → dashboard. That narrows registration vs. wiring vs. extensions vs. routing vs. security in five commands.admin_code/model_class/manager_type, copy _defaults) gives you the same ergonomics with your Symfony and Sonata versions, plus room for things this bundle can't do — multiple entity prefixes, attribute-driven overrides, skipping subclasses automatically, and reliable multi-extension wiring.How can I help you explore Laravel packages today?