admin-panel/admin-bundle
Admin Bundle is a simple Symfony admin generator for building entity lists and custom actions. Supports Doctrine ORM and Doctrine DBAL. Versioned by Symfony branch (2.7+ and 3.x), with documentation in the doc/ directory.
Installation
composer require admin-panel/admin-bundle
For Symfony 3.0+:
composer require admin-panel/admin-bundle:^3.0
Enable the Bundle
Add to config/bundles.php:
AdminPanel\AdminBundle\AdminPanelAdminBundle::class => ['all' => true],
Configure Database Connection
Update config/packages/admin_panel_admin.yaml (auto-generated):
admin_panel_admin:
connection: default # or your custom Doctrine connection name
First Admin Controller
Generate a CRUD controller for an entity (e.g., User):
php bin/console generate:admin:crud --entity=App\Entity\User
This creates:
src/Controller/Admin/UserCrudController.php)templates/admin/user/crud/index.html.twig)/admin/user)Access the Admin Panel
Visit /admin/user in your browser. The bundle auto-generates:
Modify the auto-generated controller to add a custom action (e.g., suspend):
// src/Controller/Admin/UserCrudController.php
use AdminPanel\AdminBundle\Action\ActionManager;
use AdminPanel\AdminBundle\Action\Actions;
use AdminPanel\AdminBundle\Action\CrudAction;
class UserCrudController extends AbstractCrudController
{
public function configureActions(): Actions
{
return Actions::create()
->add(CrudAction::new()->setRoute('edit'))
->add(CrudAction::new('suspend')
->setIcon('fa-ban')
->setLabel('Suspend')
->setCallback([$this, 'suspendUser'])
);
}
public function suspendUser(User $user): void
{
$user->setIsActive(false);
$this->getEntityManager()->flush();
$this->addFlash('success', 'User suspended');
return $this->redirectToRoute('admin_user_index');
}
}
Define admin-specific metadata for your entity via annotations or YAML:
# config/admin_panel_admin.yaml
admin_panel_admin:
entities:
App\Entity\User:
list:
fields: [id, email, username, createdAt]
max_per_page: 20
form:
fields: [email, username, roles, isActive]
validation_groups: [Default, 'admin']
Key Annotations (alternative to YAML):
use AdminPanel\AdminBundle\Annotation\Admin;
/**
* @Admin\Config(
* list = @Admin\ListConfig(fields = {"id", "email"}),
* form = @Admin\FormConfig(fields = {"email", "username"})
* )
*/
class User {}
Customize how fields are displayed in lists/forms:
// src/Controller/Admin/UserCrudController.php
protected function configureListFields(): array
{
return [
'id' => ListField::new('id')->setLabel('ID'),
'email' => ListField::new('email')->setLabel('Email Address'),
'roles' => ListField::new('roles')
->setTemplate('admin/user/fields/roles.html.twig')
->setLabel('Permissions'),
'createdAt' => ListField::new('createdAt')
->setType('datetime')
->setLabel('Registered')
];
}
Twig Template Example (templates/admin/user/fields/roles.html.twig):
{% for role in object.roles %}
<span class="label label-{{ role === 'ROLE_ADMIN' ? 'danger' : 'primary' }}">
{{ role }}
</span>
{% endfor %}
Add bulk operations to the list view:
public function configureActions(): Actions
{
return Actions::create()
->add(BulkAction::new('delete')
->setIcon('fa-trash')
->setLabel('Delete Selected')
->setCallback([$this, 'deleteSelectedUsers'])
);
}
public function deleteSelectedUsers(Request $request): Response
{
$ids = $request->request->get('ids', []);
$users = $this->getEntityManager()->getRepository(User::class)->findBy(['id' => $ids]);
foreach ($users as $user) {
$this->getEntityManager()->remove($user);
}
$this->getEntityManager()->flush();
return $this->redirectToRoute('admin_user_index');
}
Extend the default form with custom fields:
protected function configureFormFields(): array
{
return [
'email' => FormField::new('email')
->setType('email')
->setLabel('Email'),
'password' => FormField::new('plainPassword')
->setType('password')
->setLabel('Password')
->setRequired(false),
'roles' => FormField::new('roles')
->setType('entity')
->setClass('App\Entity\Role')
->setMultiple(true)
->setLabel('Roles')
];
}
Restrict admin routes via Symfony security:
# config/packages/security.yaml
access_control:
- { path: ^/admin/, roles: ROLE_ADMIN }
Or per-controller:
// src/Controller/Admin/UserCrudController.php
public function __construct()
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
}
Override default templates by copying them from:
vendor/admin-panel/admin-bundle/Resources/views/
to:
templates/admin_panel_admin/
default, configure it in admin_panel_admin.yaml:
admin_panel_admin:
connection: my_custom_connection
php bin/console doctrine:database:list-connections
Gedmo\Timestampable\TimestampableEntity, ensure the field is mapped correctly.ListField::new('createdAt')->setType('datetime')
max_per_page is set too low or if the query is complex.max_per_page is 30. Adjust in YAML or controller:
admin_panel_admin:
entities:
App\Entity\User:
list:
max_per_page: 50
configureListQuery():
protected function configureListQuery(): QueryBuilder
{
return $this->createQueryBuilder('u')
->where('u.isActive = :active')
->setParameter('active', true);
}
{{ form_start(form, { attr: { 'data-csrf-token': app.request.csrfToken } }) }}
Or for bulk actions, pass the token via JavaScript:
const token = document.querySelector('meta[name="csrf-token"]').content;
fetch('/admin/user/bulk-delete', {
method: 'POST',
headers: { 'X-CSRF-Token': token },
body: JSON.stringify({ ids: selectedIds })
});
config/packages/translation.yaml:
imports:
- { resource: '@AdminPanelAdminBundle/Resources/config/translation.yaml' }
translations/messages.en.yaml:
admin:
user:
suspend: 'Deactivate Account'
->addSelect() in configureListQuery() to limit loaded fields:
$qb = $this->createQueryBuilder('u');
return $qb->addSelect(['u.id', 'u.email',
How can I help you explore Laravel packages today?