rmzamora/doctrine-orm-admin-bundle
A Symfony bundle that generates admin interfaces for Doctrine ORM entities, giving you CRUD screens, list and form views, filters, and batch actions with minimal configuration.
This package is a fork of sonata-project/doctrine-orm-admin-bundle; it is archived and carries no maintainers, so the first step in any greenfield work is to confirm you actually need the fork. If you do not depend on fork-specific patches, require the upstream package instead and treat everything below as applicable to both, since the public API is effectively identical.
Minimal setup:
sonata-project/admin-bundle, doctrine/orm, and either this fork or the upstream ORM bridge.SonataDoctrineORMAdminBundle), and register SonataAdminBundle plus SonataBlockBundle if you have not already — the admin UI will not render without the block and twig dependencies.doctrine.orm with at least one mapped entity, and point Sonata's sonata_admin config at your admin services.First use case — a CRUD screen for a single entity:
final class PostAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $form): void
{
$form->add('title', TextType::class)
->add('body', TextareaType::class);
}
protected function configureListFields(ListMapper $list): void
{
$list->addIdentifier('title')
->add('createdAt', null, ['format' => 'Y-m-d H:i']);
}
}
Register it as a service tagged sonata.admin with manager_type: orm, then load /admin — the ORM bridge is what wires your entity into Sonata's ModelManager, datagrid, and form builder.
Look first at the Admin/ORMAdminBundle classes (AbstractAdmin, ModelManager, ProxyQuery, Form/DataTransformer), then at the upstream docs for the version you pinned.
Admin-as-service. Every admin must be a service with the sonata.admin tag and the manager_type: orm attribute. Constructor-arg order is $code, $class, $baseControllerName. Omitting manager_type is the most common cause of "no model manager" exceptions.
Extending AbstractAdmin vs ORMAdminBundle\Admin\ORMAdmin. Extend AbstractAdmin unless you need ORM-specific behaviour in configureDatagridFilters or getNewInstance. The ORM bridge auto-detects the model manager from the entity's metadata, so most admins never touch ORM classes directly.
Datagrid and filters. Use configureDatagridFilters with field names matching your entity properties; the bridge translates them into Doctrine\ORM\QueryBuilder constraints through ProxyQuery. For associations, prefer doctrine_orm_choice (or the appropriate ModelType-backed filter) over string, otherwise you will get SQL errors on joined columns.
Custom queries. Override configureQuery(ProxyQueryInterface $query) on the admin to add leftJoin/where clauses. Do not mutate $query->getQueryBuilder() outside of that method — the datagrid reuses the proxy and your changes may be dropped or double-applied on pagination.
Formatters and templates. Register custom field types and sonata_type_* formatters in config.yml under sonata_admin if you need custom rendering; the bridge delegates form building to FormBuilder and expects standard Symfony form types. Custom DataTransformers belong in configureFormFields via addModelTransformer, not on the entity.
Programmatic admin operations. From a controller or console command, fetch the admin by service id ($container->get('app.admin.post')), call getNewInstance() and create() / update() on it, and let the ModelManager handle persistence. Do not call $em->persist() yourself and then expect the admin's batch actions or audit trail to line up.
Batch actions and exports. Add batch_actions and export_fields in configureBatchActions/configureExportFields; the bridge maps these straight to ORM updates and iterates with iterate() on a ProxyQuery, so keep your queries paginated for large entities.
sonata-project/* and doctrine/* version in composer.json, and plan a migration to upstream. If you only need the fork for one patch, cherry-pick it onto upstream instead of depending on this.addIdentifier ordering, batch deletes, and many filter types.ProxyQuery::execute() returns Doctrine\ORM\Query and hydration is controlled by the datagrid's hydrate setting. If you have custom QueryBuilder logic that mutates the SELECT, pagination counts will disagree with the displayed rows — override getCountQueryBuilder or use QueryBuilder::select('DISTINCT …') carefully.add('field', Type::class) is almost always safer than relying on the guess, especially with embeddables, DateTimeInterface, and JSON columns.sonata_admin config or service tags is mandatory. Stale admin routes are a classic symptom of a forgotten cache:clear.sonata_admin.options debug settings, check the Symfony profiler's Doctrine panel for the actual query, and tail var/log/dev.log — the ProxyQuery wraps errors before they reach ExceptionListener.ProxyQuery subclass, or a form DataTransformer already solves the problem. Most "I need to customize the list view" requests are solved by formatters and a custom template registered under sonata_admin.templates.How can I help you explore Laravel packages today?