### 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 %} ```