### Install EasyAdmin
Source: https://symfony.com/bundles/EasyAdminBundle/current/index.html
Run this command to install EasyAdmin in your application.
```bash
$ composer require easycorp/easyadmin-bundle
```
--------------------------------
### Install Flysystem Bundle
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/FileField.html
Composer command to install the league/flysystem-bundle.
```bash
$ composer require league/flysystem-bundle
```
--------------------------------
### TextField Examples
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields.html
Examples of how to configure labels for TextField in EasyAdmin.
```php
TextField::new('firstName'),
TextField::new('firstName', null),
TextField::new('firstName', false),
TextField::new('firstName', 'Customer Name')
```
--------------------------------
### Install Symfony ExpressionLanguage Component
Source: https://symfony.com/bundles/EasyAdminBundle/current/security.html
Install the ExpressionLanguage component using Composer.
```bash
$ composer require symfony\/expression-language
```
--------------------------------
### ComparisonFilter Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/filters.html
Example of configuring a generic ComparisonFilter.
```php
use EasyCorp\Bundle\EasyAdminBundle\Filter\ComparisonFilter;
$filters->add(ComparisonFilter::new('score'));
```
--------------------------------
### URL Menu Item Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/dashboards.html
Example of linking to a relative or absolute URL.
```php
use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
public function configureMenuItems(): iterable
{
return [
MenuItem::linkToUrl('Visit public website', null, '/'),
MenuItem::linkToUrl('Search in Google', 'fab fa-google', 'https://google.com'),
// ...
];
}
```
--------------------------------
### Install Assets Manually
Source: https://symfony.com/bundles/EasyAdminBundle/current/design.html
Command to manually install backend assets if automatic copying or symlinking fails.
```bash
php bin/console assets:install --symlink
```
--------------------------------
### Dashboard Controller Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/dashboards.html
Example of a DashboardController configuring menu items using MenuItem::linkToDashboard, MenuItem::section, and MenuItem::linkTo.
```php
use App\Controller\Admin\BlogPostCrudController;
use App\Controller\Admin\CategoryCrudController;
use App\Controller\Admin\CommentCrudController;
use App\Controller\Admin\UserCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminDashboard;
use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;
#[AdminDashboard(routePath: '/admin', routeName: 'admin')]
class DashboardController extends AbstractDashboardController
{
// ...
public function configureMenuItems(): iterable
{
return [
MenuItem::linkToDashboard('Dashboard', 'fa fa-home'),
MenuItem::section('Blog'),
MenuItem::linkTo(CategoryCrudController::class, 'Categories', 'fa fa-tags'),
MenuItem::linkTo(BlogPostCrudController::class, 'Blog Posts', 'fa fa-file-text'),
MenuItem::section('Users'),
MenuItem::linkTo(CommentCrudController::class, 'Comments', 'fa fa-comment'),
MenuItem::linkTo(UserCrudController::class, 'Users', 'fa fa-user'),
];
}
}
```
--------------------------------
### Custom Global Action Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/actions.html
Example of creating a custom global action and adding it to the index page.
```php
$goToStripe = Action::new('goToStripe')
->linkToUrl('https://www.stripe.com/')
->createAsGlobalAction()
;
$actions->add(Crud::PAGE_INDEX, $goToStripe);
```
--------------------------------
### Dashboard Menu Item Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/dashboards.html
Example of linking to the homepage of the current dashboard.
```php
use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
public function configureMenuItems(): iterable
{
return [
MenuItem::linkToDashboard('Home', 'fa fa-home'),
// ...
];
}
```
--------------------------------
### Install Flysystem Adapter (e.g., AWS S3)
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/FileField.html
Composer command to install a specific Flysystem adapter, like the one for Amazon S3.
```bash
$ composer require league/flysystem-aws-s3-v3
```
--------------------------------
### Composer Install Money PHP Library
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/MoneyField.html
Command to install the Money PHP library.
```bash
$ composer require moneyphp/money
```
--------------------------------
### Route Menu Item Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/dashboards.html
Example of linking to any of the routes defined by your Symfony application.
```php
use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
public function configureMenuItems(): iterable
{
return [
MenuItem::linkToRoute('The Label', 'fa ...', 'route_name'),
MenuItem::linkToRoute('The Label', 'fa ...', 'route_name', ['routeParamName' => 'routeParamValue']),
// ...
];
}
```
--------------------------------
### DuplicateActionExtension Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/actions.html
An example of a PHP class implementing the ActionsExtensionInterface to add a 'Duplicate' action.
```php
// /src/DuplicateActionExtension.php
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Action\ActionsExtensionInterface;
final class DuplicateActionExtension implements ActionsExtensionInterface
{
// return true in this method to enable the extension for
// the current backend request
public function supports(AdminContext $context): bool
{
// enable the extension only on some pages
return $context->getCrud()->getCurrentPage() === Crud::PAGE_DETAIL;
// enable it on all except some entities
$entityFqcn = $context->getCrud()->getEntityFqcn();
return null !== $entityFqcn && !\in_array($entityFqcn, ['...'], true);
// or use any other admin context data to make the decision
}
public function extend(Actions $actions, AdminContext $context): void
{
$duplicate = Action::new('duplicate', 'Duplicate', 'fa fa-clone')
->linkToCrudAction('duplicate')
->asSuccessAction();
$actions->add(Crud::PAGE_DETAIL, $duplicate);
// you can add single actions, groups of actions, etc.
// you can also remove or update existing actions
}
}
```
--------------------------------
### TextEditorField Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/TextEditorField.html
This is a basic example of how to use the TextEditorField.
```php
yield TextEditorField::new('...')->setNumOfRows(30);
```
--------------------------------
### Controller Menu Item Examples
Source: https://symfony.com/bundles/EasyAdminBundle/current/dashboards.html
Examples of linking to CRUD controllers, including options for label, icon, action, and sorting.
```php
use App\Controller\Admin\CategoryCrudController;
use App\Controller\Admin\LegacyCategoryCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
public function configureMenuItems(): iterable
{
return [
// ...
// links to the 'index' action of the Category CRUD controller
// the label is auto-derived from the entity name (e.g. "Categories")
MenuItem::linkTo(CategoryCrudController::class),
// you can pass an explicit label and icon
MenuItem::linkTo(CategoryCrudController::class, 'Categories', 'fa fa-tags'),
// links to a different CRUD action
MenuItem::linkTo(CategoryCrudController::class, 'Add Category', 'fa fa-tags')
->setAction(Action::NEW),
MenuItem::linkTo(CategoryCrudController::class, 'Show Main Category', 'fa fa-tags')
->setAction(Action::DETAIL)
->setEntityId(40585),
// uses custom sorting options for the listing
MenuItem::linkTo(CategoryCrudController::class, 'Categories', 'fa fa-tags')
->setDefaultSort(['createdAt' => 'DESC']),
];
}
```
```php
use App\Controller\Admin\AnalyticsDashboardController;
MenuItem::linkTo(AnalyticsDashboardController::class, 'Analytics', 'fa fa-chart-line');
```
--------------------------------
### ProductCrudController Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields.html
A basic structure for a ProductCrudController extending AbstractCrudController.
```php
namespace App\Controller\Admin;
use App\Entity\Product;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
class ProductCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return Product::class;
}
public function configureFields(string $pageName): iterable
{
// ...
}
// ...
}
```
--------------------------------
### Association Field Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/AssociationField.html
This is a basic example of how to use the AssociationField.
```php
yield AssociationField::new('...');
```
--------------------------------
### Section Menu Item Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/dashboards.html
Example of creating a visual separation between menu items, optionally with a label.
```php
use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
public function configureMenuItems(): iterable
{
return [
// ...
MenuItem::section(),
// ...
MenuItem::section('Blog'),
// ...
];
}
```
--------------------------------
### Example Symfony Controller
Source: https://symfony.com/bundles/EasyAdminBundle/current/actions.html
A standard Symfony controller with logic to calculate and display business statistics.
```php
namespace App\Controller;
use App\Stats\BusinessStatsCalculator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_ADMIN')]
class BusinessStatsController extends AbstractController
{
public function __construct(BusinessStatsCalculator $businessStatsCalculator)
{
$this->businessStatsCalculator = $businessStatsCalculator;
}
#[Route("/admin/business-stats", name: "business_stats_index")]
public function index()
{
return $this->render('admin/business_stats/index.html.twig', [
'data' => $this->businessStatsCalculator->getStatsSummary(),
]);
}
#[Route("/admin/business-stats/{id}", name: "business_stats_customer")]
public function customer(Customer $customer)
{
return $this->render('admin/business_stats/customer.html.twig', [
'data' => $this->businessStatsCalculator->getCustomerStats($customer),
]);
}
}
```
--------------------------------
### setUploadDir Option
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/ImageField.html
Example of using the setUploadDir option to change the directory where uploaded images are stored.
```php
yield ImageField::new('...')->setUploadDir('assets/images/');
// the property will only store the file path relative to this dir
// (e.g. 'logo.png', 'venue/layout.jpg')
```
--------------------------------
### Event Subscriber Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/events.html
This example shows how to use an event subscriber to set the `slug` property of the `BlogPost` entity before persisting it.
```php
# src/EventSubscriber/EasyAdminSubscriber.php
namespace App\EventSubscriber;
use App\Entity\BlogPost;
use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityPersistedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class EasyAdminSubscriber implements EventSubscriberInterface
{
private $slugger;
public function __construct($slugger)
{
$this->slugger = $slugger;
}
public static function getSubscribedEvents()
{
return [
BeforeEntityPersistedEvent::class => ['setBlogPostSlug'],
];
}
public function setBlogPostSlug(BeforeEntityPersistedEvent $event)
{
$entity = $event->getEntityInstance();
if (!($entity instanceof BlogPost)) {
return;
}
$slug = $this->slugger->slugify($entity->getTitle());
$entity->setSlug($slug);
}
}
```
--------------------------------
### ChoiceFilter Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/filters.html
Example of configuring a ChoiceFilter for a property with a predefined list of choices.
```php
use EasyCorp\Bundle\EasyAdminBundle\Filter\ChoiceFilter;
$filters->add(ChoiceFilter::new('status')->setChoices([
'Draft' => 'draft',
'Published' => 'published',
'Archived' => 'archived',
]));
```
--------------------------------
### Creating Global Action Groups
Source: https://symfony.com/bundles/EasyAdminBundle/current/actions.html
Example of creating global action groups for the index page.
```php
public function configureActions(Actions $actions): Actions
{
$createActions = ActionGroup::new('create')
->createAsGlobalActionGroup()
->addAction(Action::new('new', 'Create Post')->linkToCrudAction('...'))
->addAction(Action::new('draft', 'Draft Post')->linkToCrudAction('...'))
->addAction(Action::new('template', 'Create from Template')->linkToCrudAction('...'));
$sendActions = ActionGroup::new('send', 'Send ...')
->addAction(Action::new('sendEmail', 'Send by Email')->linkToCrudAction('...'))
->addAction(Action::new('sendSlack', 'Send to Slack')->linkToCrudAction('...'))
->addAction(Action::new('sendTelegram', 'Send via Telegram')->linkToCrudAction('...'));
return $actions
// ...
->add(Crud::PAGE_INDEX, $createActions)
->add(Crud::PAGE_INDEX, $sendActions)
;
}
```
--------------------------------
### Configuring fields with fieldsets
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields.html
Example of how to use `addFieldset()` to group form fields.
```php
use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;
public function configureFields(string $pageName): iterable
{
return [
// fieldsets usually display only a title
FormField::addFieldset('User Details'),
TextField::new('firstName'),
TextField::new('lastName'),
// fieldsets without titles only display a separation between fields
FormField::addFieldset(),
DateTimeField::new('createdAt')->onlyOnDetail(),
// fieldsets can also define their icon, CSS class and help message
FormField::addFieldset('Contact information')
->setIcon('phone')->addCssClass('optional')
->setHelp('Phone number is preferred'),
TextField::new('phone'),
TextField::new('email')->hideOnIndex(),
// fieldsets can be collapsible too (useful if your forms are long)
// this makes the fieldset collapsible but renders it expanded by default
FormField::addFieldset('Contact information')->collapsible(),
// this makes the fieldset collapsible and renders it collapsed by default
FormField::addFieldset('Contact information')->renderCollapsed(),
];
}
```
--------------------------------
### NullFilter Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/filters.html
Example of how to add a NullFilter to filter properties based on whether they are null or not null.
```php
use EasyCorp\Bundle\EasyAdminBundle\Filter\NullFilter;
$filters->add(NullFilter::new('deletedAt'));
```
--------------------------------
### MaskedEmailConfigurator Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields.html
An example of a custom field configurator that masks the local part of an email address on the index page.
```php
namespace App\Admin\Configurator;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldConfiguratorInterface;
use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
use EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto;
use EasyCorp\Bundle\EasyAdminBundle\Field\EmailField;
final class MaskedEmailConfigurator implements FieldConfiguratorInterface
{
public function supports(FieldDto $field, EntityDto $entityDto):
{
return EmailField::class === $field->getFieldFqcn();
}
public function configure(FieldDto $field, EntityDto $entityDto, AdminContext $context): void
{
if (Crud::PAGE_INDEX !== $context->getCrud()->getCurrentPage()) {
return;
}
$email = (string) $field->getValue();
if (!str_contains($email, '@')) {
return;
}
[$local, $domain] = explode('@', $email, 2);
$field->setFormattedValue(substr($local, 0, 1).'***@'.$domain);
}
}
```
--------------------------------
### Setting Flysystem Storage
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/ImageField.html
Example of explicitly setting the Flysystem storage service ID.
```php
yield ImageField::new('...')->setFlysystemStorage('default.storage');
```
--------------------------------
### Field Design Options Example 1
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields.html
Demonstrates adding form themes and CSS classes to a field.
```php
TextField::new('firstName', 'Name')
// use this method if your field needs a specific form theme to render properly
->addFormTheme('@FOSCKEditor/Form/ckeditor_widget.html.twig')
// you can add more than one form theme using the same method
->addFormTheme('theme1.html.twig', 'theme2.html.twig', 'theme3.html.twig')
// on the 'index' page, CSS class/classes are applied both to the `
` and the `
`
// of the field in all rows; on the 'detail', 'edit', and 'new' pages, they are applied
// to the row that wraps the contents of the field
// use this method to add new classes to the ones applied by EasyAdmin
->addCssClass('text-large text-bold')
// use this other method if you want to remove any CSS class added by EasyAdmin
->setCssClass('text-large text-bold')
// this defines the Twig template used to render this field in 'index' and 'detail' pages
// (this is not used in the 'edit'/'new' pages because they use Symfony Forms themes)
->setTemplatePath('admin/fields/my_template.html.twig')
// useful for example to right-align numbers/money values (this setting is ignored in 'detail' page)
->setTextAlign('right')
;
```
--------------------------------
### Basic Usage
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/PercentField.html
This is a basic example of how the PercentField is rendered in a form.
```php
yield PercentField::new('...');
```
--------------------------------
### Render Expanded
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/CollectionField.html
Example of how to render all items in the collection expanded on page load.
```php
yield CollectionField::new('...')->renderExpanded();
```
--------------------------------
### Configure Actions Method
Source: https://symfony.com/bundles/EasyAdminBundle/current/actions.html
Example of the `configureActions()` method in a CRUD controller to define available actions.
```php
namespace App\Controller\Admin;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
class ProductCrudController extends AbstractCrudController
{
// ...
public function configureActions(Actions $actions): Actions
{
// ...
}
}
```
--------------------------------
### Using `ea.templatePath()` in Twig
Source: https://symfony.com/bundles/EasyAdminBundle/current/crud.html
Example of how to use the `ea.templatePath()` Twig function to get the correct path for EasyAdmin templates.
```twig
{{ include(ea.templatePath('flash_messages')) }}
{% if some_value is null %}
{{ include(ea.templatePath('label/null')) }}
{% endif %}
```
--------------------------------
### EntityFilter Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/filters.html
Example of using the EntityFilter for Doctrine associations, including an example with autocomplete for large datasets.
```php
use EasyCorp\Bundle\EasyAdminBundle\Filter\EntityFilter;
$filters->add(EntityFilter::new('category'));
// with autocomplete for large datasets
$filters->add(EntityFilter::new('author')->autocomplete());
```
--------------------------------
### Render as Text
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/TimeField.html
Example of rendering the time as a single text input field.
```php
yield TimeField::new('...')->renderAsText();
```
--------------------------------
### Split Button Dropdowns
Source: https://symfony.com/bundles/EasyAdminBundle/current/actions.html
Example of rendering an action group as a split button with a main action.
```php
$publishActions = ActionGroup::new('publish', 'Publish')
->addMainAction(Action::new('publishNow', 'Publish Now')->linkToCrudAction('...'))
->addAction(Action::new('schedule', 'Schedule...')->linkToCrudAction('...'))
->addAction(Action::new('publishDraft', 'Save as Draft')->linkToCrudAction('...'));
```
--------------------------------
### Entity Translations in YAML
Source: https://symfony.com/bundles/EasyAdminBundle/current/dashboards.html
Example of defining entity translations using YAML format.
```YAML
```
--------------------------------
### BooleanFilter Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/filters.html
Example of configuring a BooleanFilter for a boolean property.
```php
use EasyCorp\Bundle\EasyAdminBundle\Filter\BooleanFilter;
$filters->add(BooleanFilter::new('published'));
```
--------------------------------
### Submenu Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/dashboards.html
Defines a submenu with nested menu items using the `subMenu()` item type.
```php
use App\Controller\Admin\BlogPostCrudController;
use App\Controller\Admin\CategoryCrudController;
use App\Controller\Admin\CommentCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
public function configureMenuItems(): iterable
{
return [
MenuItem::subMenu('Blog', 'fa fa-article')->setSubItems([
MenuItem::linkTo(CategoryCrudController::class, 'Categories', 'fa fa-tags'),
MenuItem::linkTo(BlogPostCrudController::class, 'Posts', 'fa fa-file-text'),
MenuItem::linkTo(CommentCrudController::class, 'Comments', 'fa fa-comment'),
]),
// ...
];
}
```
--------------------------------
### Batch Action Configuration
Source: https://symfony.com/bundles/EasyAdminBundle/current/actions.html
Example of adding a batch action to the action configuration in a Crud controller.
```php
namespace App\Controller\Admin;
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
class UserCrudController extends AbstractCrudController
{
// ...
public function configureActions(Actions $actions): Actions
{
return $actions
// ...
->addBatchAction(Action::new('approve', 'Approve Users')
->linkToCrudAction('approveUsers')
->addCssClass('btn btn-primary')
->setIcon('fa fa-user-check'))
;
}
}
```
--------------------------------
### Configure Assets
Source: https://symfony.com/bundles/EasyAdminBundle/current/design.html
Example of using the `configureAssets()` method to add various types of assets like AssetMapper entries, Webpack Encore entries, CSS files, and JS files. It also shows how to add raw HTML content to the head and body.
```php
namespace App\Controller\Admin;
use EasyCorp\Bundle\EasyAdminBundle\Config\Assets;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
class ProductCrudController extends AbstractCrudController
{
// ...
public function configureAssets(Assets $assets): Assets
{
return $assets
// imports the given entrypoint defined in the importmap.php file of AssetMapper
// it's equivalent to adding this inside the element:
// {{ importmap('admin') }}
->addAssetMapperEntry('admin')
// you can also import multiple entries
// it's equivalent to calling {{ importmap(['app', 'admin']) }}
->addAssetMapperEntry('app', 'admin')
// adds the CSS and JS assets associated to the given Webpack Encore entry
// it's equivalent to adding these inside the element:
// {{ encore_entry_link_tags('...') }} and {{ encore_entry_script_tags('...') }}
->addWebpackEncoreEntry('admin-app')
// it's equivalent to adding this inside the element:
//
->addCssFile('build/admin.css')
->addCssFile('https://example.org/css/admin2.css')
// it's equivalent to adding this inside the element:
//
->addJsFile('build/admin.js')
->addJsFile('https://example.org/js/admin2.js')
// use these generic methods to add any code before or
// the contents are included "as is" in the rendered page (without escaping them)
->addHtmlContentToHead('')
->addHtmlContentToBody('')
->addHtmlContentToBody('')
;
}
}
```
--------------------------------
### Login Controller Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/dashboards.html
A Symfony controller demonstrating how to render the EasyAdmin login page with various customization options.
```php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends AbstractController
{
#[Route("/login", name: "login")]
public function login(AuthenticationUtils $authenticationUtils): Response
{
$error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('@EasyAdmin/page/login.html.twig', [
// parameters usually defined in Symfony login forms
'error' => $error,
'last_username' => $lastUsername,
// OPTIONAL parameters to customize the login form:
// the translation_domain to use (define this option only if you are
// rendering the login template in a regular Symfony controller; when
// rendering it from an EasyAdmin Dashboard this is automatically set to
// the same domain as the rest of the Dashboard)
'translation_domain' => 'admin',
// by default EasyAdmin displays a black square as its default favicon;
// use this method to display a custom favicon: the given path is passed
// "as is" to the Twig asset() function:
//
'favicon_path' => '/favicon-admin.svg',
// the title visible above the login form (define this option only if you are
// rendering the login template in a regular Symfony controller; when rendering
// it from an EasyAdmin Dashboard this is automatically set as the Dashboard title)
'page_title' => 'ACME login',
// the string used to generate the CSRF token. If you don't define
// this parameter, the login form won't include a CSRF token
'csrf_token_intention' => 'authenticate',
// the URL users are redirected to after the login (default: '/admin')
'target_path' => $this->generateUrl('admin_dashboard'),
// the label displayed for the username form field (the |trans filter is applied to it)
'username_label' => 'Your username',
// the label displayed for the password form field (the |trans filter is applied to it)
'password_label' => 'Your password',
// the label displayed for the Sign In form button (the |trans filter is applied to it)
'sign_in_label' => 'Log in',
// the 'name' HTML attribute of the used for the username field (default: '_username')
'username_parameter' => 'my_custom_username_field',
// the 'name' HTML attribute of the used for the password field (default: '_password')
'password_parameter' => 'my_custom_password_field',
// whether to enable or not the "forgot password?" link (default: false)
'forgot_password_enabled' => true,
// the path (i.e. a relative or absolute URL) to visit when clicking the "forgot password?" link (default: '#')
'forgot_password_path' => $this->generateUrl('...', ['...' => '...']),
// the label displayed for the "forgot password?" link (the |trans filter is applied to it)
'forgot_password_label' => 'Forgot your password?',
// whether to enable or not the "remember me" checkbox (default: false)
'remember_me_enabled' => true,
// remember me name form field (default: '_remember_me')
'remember_me_parameter' => 'custom_remember_me_param',
// whether to check by default the "remember me" checkbox (default: false)
'remember_me_checked' => true,
// the label displayed for the remember me checkbox (the |trans filter is applied to it)
'remember_me_label' => 'Remember me',
]);
}
}
```
--------------------------------
### Grouping Actions with ActionGroup
Source: https://symfony.com/bundles/EasyAdminBundle/current/actions.html
Example of creating an action group with multiple actions for the edit page.
```php
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\ActionGroup;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
public function configureActions(Actions $actions): Actions
{
$publishActions = ActionGroup::new('publish', 'Publish')
->addAction(Action::new('publishNow', 'Publish Now')->linkToCrudAction('...'))
->addAction(Action::new('schedule', 'Schedule...')->linkToCrudAction('...'))
->addAction(Action::new('publishDraft', 'Save as Draft')->linkToCrudAction('...'));
return $actions
// ...
->add(Crud::PAGE_EDIT, $publishActions)
;
}
```
--------------------------------
### NumericFilter Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/filters.html
Example of how to add a NumericFilter to filter numeric properties.
```php
use EasyCorp\Bundle\EasyAdminBundle\Filter\NumericFilter;
$filters->add(NumericFilter::new('price'));
```
--------------------------------
### DateTimeFilter Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/filters.html
Example of using the DateTimeFilter for date and time properties.
```php
use EasyCorp\Bundle\EasyAdminBundle\Filter\DateTimeFilter;
$filters->add(DateTimeFilter::new('createdAt'));
```
--------------------------------
### Formatting Options Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields.html
Demonstrates using the formatValue() method to apply PHP callables to field values before rendering.
```php
IntegerField::new('stock', 'Stock')
// callbacks usually take only the current value as argument
->formatValue(static fn ($value): int|string => $value < 10 ? sprintf('%d **LOW STOCK**', $value) : $value)
;
TextEditorField::new('description')
// callables also receives the entire entity instance as the second argument
->formatValue(static fn ($value, $entity): int|string => $entity->isPublished() ? $value : 'Coming soon...')
;
```
--------------------------------
### setUploadedFileNamePattern with closure
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/FileField.html
Shows how to use a closure to dynamically set the uploaded file name pattern, generating a random number and using the original file's name and extension.
```php
yield FileField::new('...')->setUploadedFileNamePattern(
fn (UploadedFile $file): string => sprintf('upload_%d_%s.%s', random_int(1, 999), $file->getFilename(), $file->guessExtension()))
);
```
--------------------------------
### TextFilter Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/filters.html
Example of how to add a TextFilter to filter string and text properties.
```php
use EasyCorp\Bundle\EasyAdminBundle\Filter\TextFilter;
$filters->add(TextFilter::new('title'));
```
--------------------------------
### Field Design Options Example 2
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields.html
Demonstrates adding CSS files, JS files, Webpack Encore entries, and HTML contents to the head and body.
```php
TextField::new('firstName', 'Name')
->addCssFiles('bundle/some-bundle/foo.css', 'some-custom-styles.css')
->addJsFiles('admin/some-custom-code.js')
->addWebpackEncoreEntry('admin-maps')
->addHtmlContentsToHead('')
->addHtmlContentsToBody('')
;
```
--------------------------------
### Using allowedDashboards and deniedDashboards
Source: https://symfony.com/bundles/EasyAdminBundle/current/actions.html
Examples of using the 'allowedDashboards' and 'deniedDashboards' options in the #[AdminRoute] attribute to control which dashboards an action is available on.
```php
use App\Controller\Admin\GuestDashboardController;
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminRoute;
// Use the 'allowedDashboards' option to NOT generate a route for ANY dashboards
// except those listed explicitly:
#[AdminRoute('...', name: '...', allowedDashboards: [DashboardController::class, '...'])]
class BusinessStatsController extends AbstractController
// Use the 'deniedDashboards' option to generate a route for ALL dashboards
// except those listed explicitly:
#[AdminRoute('...', name: '...', deniedDashboards: [GuestDashboardController::class, '...'])]
class BusinessStatsController extends AbstractController
```
--------------------------------
### setUploadedFileNamePattern with string pattern
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/FileField.html
Demonstrates how to set a pattern for uploaded file names using special values like date parts, original name, slug, and hashes.
```php
yield FileField::new('...')
->setUploadedFileNamePattern('[YYYY]/[MM]/[DD]/[slug]-[contenthash].[extension]');
```
--------------------------------
### showSample option
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/ColorField.html
If you prefer to not display that sample, use this option:
```php
yield ColorField::new('...')->showSample(false);
```
--------------------------------
### Unmapped Filter Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/filters.html
Example demonstrating how to use an unmapped filter for properties of related entities.
```php
namespace App\Controller\Admin;
use App\Admin\Filter\CustomerCountryFilter;
use EasyCorp\Bundle\EasyAdminBundle\Config\Filters;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Filter\BooleanFilter;
class OrderCrudController extends AbstractCrudController
{
// ...
public function configureFilters(Filters $filters): Filters
{
return $filters
// 'country' doesn't exist as a property of 'Order' so it's
// defined as 'not mapped' to avoid errors
->add(CustomerCountryFilter::new('country')->setFormTypeOption('mapped', false))
;
}
}
```
--------------------------------
### Manual Configuration - Twig Component Configuration
Source: https://symfony.com/bundles/EasyAdminBundle/current/index.html
Create a configuration file for Twig Components at `config/packages/twig_component.yaml`. This is only required for applications not using Symfony Flex.
```yaml
# config/packages/twig_component.yaml
twig_component:
anonymous_template_directory: 'components/'
defaults:
# Namespace & directory for components
App\Twig\Components\: 'components/'
```
--------------------------------
### ArrayFilter Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/filters.html
Example of configuring an ArrayFilter for a property that stores arrays or collections, with custom choices.
```php
use EasyCorp\Bundle\EasyAdminBundle\Filter\ArrayFilter;
$filters->add(ArrayFilter::new('tags')->setChoices([
'Tech' => 'tech',
'News' => 'news',
'Sports' => 'sports',
]));
```
--------------------------------
### Set Flysystem Storage Method
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/FileField.html
PHP example demonstrating the `setFlysystemStorage()` method to link a FileField to a configured Flysystem storage.
```php
yield FileField::new('...')->setFlysystemStorage('default.storage');
```
--------------------------------
### CurrencyFilter Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/filters.html
Example of using the CurrencyFilter to filter by currency codes, with an option to include only specific currencies.
```php
use EasyCorp\Bundle\EasyAdminBundle\Filter\CurrencyFilter;
$filters->add(CurrencyFilter::new('currency')
->includeOnly(['USD', 'EUR', 'GBP', 'JPY'])
);
```
--------------------------------
### Miscellaneous Field Options Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields.html
Illustrates various other field configuration options like sortability, help messages, form type settings, and HTML attributes.
```php
TextField::new('firstName', 'Name')
// if TRUE, listing can be sorted by this field (default: TRUE)
// unmapped fields cannot be sorted
->setSortable(false)
// help message displayed for this field in the 'detail', 'edit' and 'new' pages
->setHelp('...')
// sets the value of the `empty_data` option in the Symfony form
// see https://symfony.com/doc/current/reference/forms/types/form.html#empty-data
->setEmptyData('Jane Doe')
// the Symfony Form type used to render this field in 'edit'/'new' pages
// (fields have good default values for this option, so you don't usually configure this)
->setFormType(TextType::class)
// an array of parameters passed to the Symfony form type
// (this only overrides the values of the passed form type options;
// it leaves all the other existing type options unchanged)
->setFormTypeOptions(['option_name' => 'option_value'])
// a custom HTML attribute added when rendering the field
// e.g. setHtmlAttribute('data-foo', 'bar') renders a 'data-foo="bar"' attribute in HTML
// On 'index' and 'detail' pages, the attribute is added to the field container:
//
and div.field-group respectively
// On 'new' and 'edit' pages, the attribute is added to the form field;
// it's a shortcut for the equivalent setFormTypeOption('attr.data-foo', 'bar)
->setHtmlAttribute('attribute_name', 'attribute_value')
// a key-value array of attributes to add to the HTML element
->setHtmlAttributes(['data-foo' => 'bar', 'autofocus' => 'autofocus'])
;
```
--------------------------------
### Functional Test Case Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/tests.html
Example of a functional test class for a Category CRUD controller, extending AbstractCrudTestCase.
```php
namespace App\Tests\Admin\Controller;
use App\Controller\Admin\AppDashboardController;
use App\Controller\Admin\CategoryCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Test\AbstractCrudTestCase;
final class CategoryCrudControllerTest extends AbstractCrudTestCase
{
protected function getControllerFqcn(): string
{
return CategoryCrudController::class;
}
protected function getDashboardFqcn(): string
{
return AppDashboardController::class;
}
public function testIndexPage(): void
{
// this examples doesn't use security; in your application you may
// need to ensure that the user is logged before the test
$this->client->request("GET", $this->generateIndexUrl());
static::assertResponseIsSuccessful();
}
}
```
--------------------------------
### Responsive Column Layout
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields.html
This example shows how to use Bootstrap responsive classes to create columns with different sizes based on screen width. Columns below 'lg' breakpoints are not displayed, and the sum of columns doesn't need to be 12.
```php
use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;
public function configureFields(string $pageName): iterable
{
return [
FormField::addColumn('col-lg-8 col-xl-6'),
TextField::new('firstName'),
TextField::new('lastName'),
FormField::addColumn('col-lg-3 col-xl-2'),
TextField::new('phone'),
TextField::new('email')->hideOnIndex(),
];
}
```
--------------------------------
### Dashboard Configuration Method
Source: https://symfony.com/bundles/EasyAdminBundle/current/dashboards.html
The `configureDashboard()` method is where the main dashboard configuration is defined. This snippet shows the structure and some basic configuration options.
```php
namespace App\Controller\Admin;
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminDashboard;
use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;
use EasyCorp\Bundle\EasyAdminBundle\Dto\LocaleDto;
#[AdminDashboard(routePath: '/admin', routeName: 'admin')]
class DashboardController extends AbstractDashboardController
{
// ...
public function configureDashboard(): Dashboard
{
return Dashboard::new()
// the name visible to end users
->setTitle('ACME Corp.')
// you can include HTML contents too (e.g. to link to an image)
->setTitle(' ACME Corp.')
// by default EasyAdmin displays a black square as its default favicon;
// use this method to display a custom favicon: the given path is passed
// "as is" to the Twig asset() function:
//
->setFaviconPath('favicon.svg')
// the domain used by default is 'messages'
->setTranslationDomain('my-custom-domain')
// there's no need to define the "text direction" explicitly because
// its default value is inferred dynamically from the user locale
->setTextDirection('ltr')
// set this option if you prefer the page content to span the entire
// browser width, instead of the default design which sets a max width
->renderContentMaximized()
// set this option if you prefer the sidebar (which contains the main menu)
// to be displayed as a narrow column instead of the default expanded design
->renderSidebarMinimized()
// by default, users can select between a "light" and "dark" mode for the
// backend interface. Call this method if you prefer to disable the "dark"
// mode for any reason (e.g. if your interface customizations are not ready for it)
->disableDarkMode()
// by default, the UI color scheme is 'auto', which means that the backend
// will use the same mode (light/dark) as the operating system and will
// change in sync when the OS mode changes.
// Use this option to set which mode ('light', 'dark' or 'auto') will users see
// by default in the backend (users can change it via the color scheme selector)
->setDefaultColorScheme('dark')
// instead of magic strings, you can use constants as the value of
// this option: EasyCorp\Bundle\EasyAdminBundle\Config\Option\ColorScheme::DARK
// by default, all backend URLs are generated as absolute URLs. If you
// need to generate relative URLs instead, call this method
->generateRelativeUrls()
// set this option if you want to enable locale switching in dashboard.
// IMPORTANT: this feature won't work unless you add the {_locale}
// parameter in the admin dashboard URL (e.g. '/admin/{_locale}').
// the name of each locale will be rendered in that locale
// (in the following example you'll see: "English", "Polski")
->setLocales(['en', 'pl'])
// to customize the labels of locales, pass a key => value array
// (e.g. to display flags; although it's not a recommended practice,
// because many languages/locales are not associated to a single country)
->setLocales([
'en' => '🇬🇧 English',
'pl' => '🇵🇱 Polski'
])
// to further customize the locale option, pass an instance of
// EasyCorp\Bundle\EasyAdminBundle\Config\Locale
->setLocales([
'en', // locale without custom options
Locale::new('pl', 'polski', 'far fa-language') // custom label and icon
])
;
}
}
```
--------------------------------
### setUploadedFileNamePattern with closure and entity data
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/FileField.html
Illustrates using a closure that receives both the UploadedFile and the current entity instance, allowing file names to be based on entity properties like its slug.
```php
yield FileField::new('...')->setUploadedFileNamePattern(
static fn (UploadedFile $file, MyEntity $entity): string => sprintf('%s/[name].[extension]', $entity->getSlug()))
);
```
--------------------------------
### TimezoneFilter Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/filters.html
Example of how to add a TimezoneFilter to filter properties that store timezone identifiers, with an option to filter by country code.
```php
use EasyCorp\Bundle\EasyAdminBundle\Filter\TimezoneFilter;
// show only timezones for a specific country
$filters->add(TimezoneFilter::new('timezone')->forCountryCode('US'));
```
--------------------------------
### Customizing Asset Attributes
Source: https://symfony.com/bundles/EasyAdminBundle/current/design.html
Example demonstrating how to customize HTML attributes and other features of CSS and JavaScript tags by passing an `Asset` object to methods like `addCssFile()`, `addJsFile()`, and `addWebpackEncoreEntry()`. It also shows options like `preload()`, `nopush()`, `defer()`, `onlyOnDetail()`, `onlyWhenCreating()`, and `ignoreOnForm()`.
```php
use EasyCorp\Bundle\EasyAdminBundle\Config\Asset;
// ...
return $assets
->addCssFile(Asset::new('build/admin.css')->preload()->nopush())
->addCssFile(Asset::new('build/admin-print.css')->htmlAttr('media', 'print'))
->addJsFile(Asset::new('build/admin.js')->defer())
->addJsFile(Asset::new('build/admin.js')->preload())
->addJsFile(Asset::new('build/admin.js')->htmlAttr('referrerpolicy', 'strict-origin'))
->addWebpackEncoreEntry(Asset::new('admin-app')->webpackEntrypointName('...'))
// adding full Asset objects for AssetMapper entries work too, but it's
// useless because entries can't define any property, only their name
->addAssetMapperEntry(Asset::new('admin'))
->addCssFile(Asset::new('build/admin-detail.css')->onlyOnDetail())
->addJsFile(Asset::new('build/admin.js')->onlyWhenCreating())
->addWebpackEncoreEntry(Asset::new('admin-app')->ignoreOnForm())
// you can also define the Symfony Asset package which the asset belongs to
->addCssFile(Asset::new('some-path/foo.css')->package('legacy_assets'))
;
```
--------------------------------
### LocaleFilter Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/filters.html
Example of using the LocaleFilter to filter by locale codes, with options to include only specific locales and set preferred choices.
```php
use EasyCorp\Bundle\EasyAdminBundle\Filter\LocaleFilter;
$filters->add(LocaleFilter::new('locale')
->includeOnly(['en_US', 'en_GB', 'es_ES', 'fr_FR', 'de_DE'])
);
```
--------------------------------
### LanguageFilter Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/filters.html
Example of using the LanguageFilter to filter by language codes, with options to include only specific languages and set preferred choices.
```php
use EasyCorp\Bundle\EasyAdminBundle\Filter\LanguageFilter;
$filters->add(LanguageFilter::new('language')
->includeOnly(['en', 'es', 'fr', 'de', 'it', 'pt'])
);
```
--------------------------------
### Manual Configuration - Register Bundles
Source: https://symfony.com/bundles/EasyAdminBundle/current/index.html
Register the EasyAdmin and TwigComponent bundles in your application's `config/bundles.php` file. This is only required for applications not using Symfony Flex.
```php
return [
// ...
EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle::class => ['all' => true],
Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true],
];
```
--------------------------------
### CountryFilter Example
Source: https://symfony.com/bundles/EasyAdminBundle/current/filters.html
Example of how to use the CountryFilter to filter by country codes, with options to include only specific countries and set preferred choices.
```php
use EasyCorp\Bundle\EasyAdminBundle\Filter\CountryFilter;
$filters->add(CountryFilter::new('country')
->includeOnly(['US', 'CA', 'MX'])
->preferredChoices(['US'])
);
```
--------------------------------
### Allowing Multiple Country Choices
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/CountryField.html
Example of how to allow selecting multiple countries using the `allowMultipleChoices()` option. It also includes an example of how to handle array-to-string conversion for Doctrine entities.
```php
yield CountryField::new('...')->allowMultipleChoices();
```
```php
public class MyEntity
{
// ...
public function getCountry(): ?array
{
return '' === $this->country ? null : explode('|', $this->country);
}
public function setCountry(?array $countryCodes): self
{
$this->country = null === $countryCodes ? '' : implode('|', $countryCodes);
return $this;
}
}
```
--------------------------------
### Basic Usage
Source: https://symfony.com/bundles/EasyAdminBundle/current/fields/TimeField.html
Example of how to use the TimeField in a form page.
```php
yield TimeField::new('...')->renderAsChoice();
```
--------------------------------
### Using CSP Nonces in Custom Templates
Source: https://symfony.com/bundles/EasyAdminBundle/current/design.html
Example of using the `{% guard %}` Twig tag to conditionally include a CSP nonce for custom script tags, ensuring compatibility with and without NelmioSecurityBundle.
```twig
{% guard function csp_nonce %}
{% else %}
{% endguard %}
```