symfony easyadmin3 cheat sheet
https://symfony.com/bundles/EasyAdminBundle/3.x/index.html
Font-Awesome Icons: https://fontawesome.com/v5/search?q=check&o=r&m=free&f=classic
Easyadmin3 uses https://tom-select.js.org/ for select widgets. See below for an example.
Dashboard configuration
We had problems with a wrong base url via varnish proxy. Enabling relative urls helped:
-
// src/Controller/Admin/DashboardController.php public function configureDashboard(): Dashboard { return Dashboard::new() ->generateRelativeUrls() ; }
CRUD
Generate new crud:
-
php bin/console make:admin:crud
Actions
-
public function configureActions(Actions $actions): Actions { $viewAction = Action::new('viewView', 'View')->linkToUrl(function (Foo $entity) { return '/foo/' . $entity->getUrl(); }); $secondAction = ...; // These are displayed in reverse order! return $actions ->add(Crud::PAGE_INDEX, $secondAction) ->add(Crud::PAGE_INDEX, $viewAction) ->add(Crud::PAGE_EDIT, $viewAction) ; }
Fields
Association
Add an empty option to a required AssociationField:
-
yield AssociationField::new('foo') ->setRequired(true) ->setFormTypeOptions([ 'placeholder' => 'Please select...', // Force empty option when creating ]); ;
ArrayField
-
ArrayField::new('myRelation', 'Energy Communities') ->setTemplatePath('admin/field/my_field.html.twig') ->onlyOnIndex(), ];
-
{% if field.value is not empty %} {% for entity in field.value %} {{ entity.name }}{% if not loop.last %}, {% endif %} {% endfor %} {% else %} <span class="badge badge-secondary">None</span> {% endif %}
Filters
Choice Filter
-
public function configureFilters(Filters $filters): Filters { $exampleRepository = $this->getDoctrine()->getManager()->getRepository(Example::class); $filters ->add('name') ->add(ChoiceFilter::new('interests') ->setChoices(array_combine( $exampleRepository->findDistinctInterests(), $exampleRepository->findDistinctInterests() )) ) ; return $filters; }
Expanded
- ->add(BooleanFilter::new('enabled')->setFormTypeOption('expanded', false));
We can also create our own custom filter class. That's as easy as creating a custom class, making it implement FilterInterface, and using this FilterTrait. Then all you need to do is implement the new() method where you set the form type and then the apply() method where you modify the query.
Set a default value / set default from filter value
-
// class Example CrudController public function createEntity(string $entityFqcn) { $entity = new MyEntity(); $entity->setFoo(true); $barRepository = $this->getDoctrine()->getManager()->getRepository(Bar::class); $filters = $this->getContext()->getSearch()->getAppliedFilters(); if (isset($filters['bar'])) { $bar = $barRepository->find($filters['bar']['value']); $entity->setBar($bar); } return $entity; }
Use Association
And add custom order
-
public function configureFields(string $pageName): iterable { $fields = parent::configureFields($pageName); $fields[] = AssociationField::new('categories') ->setQueryBuilder(function (QueryBuilder $qb) { $qb->orderBy('entity.name'); }); return $fields; }
CKEditor
https://symfony.com/bundles/FOSCKEditorBundle/current/installation.html
- composer require friendsofsymfony/ckeditor-bundle
- yarn run encore dev
- ExampleCrudController.php
-
public function configureCrud(Crud $crud): Crud { $crud ... ->addFormTheme('@FOSCKEditor/Form/ckeditor_widget.html.twig') ; return parent::configureCrud($crud); // TODO: Change the autogenerated stub }
-
// configureFields() yield TextareaField::new('myTextField') ->onlyOnForms() ->setFormType(CKEditorType::class) ;
-
Custom Field for Easyadmin:https://github.com/EasyCorp/EasyAdminBundle/issues/3412#issuecomment-651315841
Configuration / Styles
Global config options for CKEditor can be configured in
- config/packages/fos_ckeditor.yaml
-
# Read the documentation: https://symfony.com/doc/current/bundles/FOSCKEditorBundle/index.html twig: form_themes: - '@FOSCKEditor/Form/ckeditor_widget.html.twig' fos_ck_editor: # @see class CKEditorConfiguration configs: my_config: toolbar: [ [ "Undo", "Redo", "-", "Styles", "Bold", "Italic", "Underline", "JustifyLeft", "JustifyCenter", "JustifyRight","JustifyBlock", "-", "BulletedList", "NumberedList", "-", "Link", "Image", "Source", "Maximize" ] ] removePlugins: contentsCss: [ "/custom.css" ] stylesSet: 'my_styles' # https://symfony.com/bundles/FOSCKEditorBundle/current/usage/file-browse-upload.html#static-routing filebrowserImageUploadUrl: "/infrastructure/CkEditorUpload" # Without config.filebrowserUploadMethod = 'form' we don' get any form POST data... Why? filebrowserUploadMethod: 'form' # https://symfony.com/bundles/FOSCKEditorBundle/current/usage/style.html styles: "my_styles": - { name: "h2", element: "h2" } - { name: "h3", element: "h3" } - { name: "Image left", element: "img", styles: { float: "left", margin: "0 16px 0 0" }} - { name: "Yellow", element: "p", attributes: { class: "blog-yellow" }}
-
The global config above can be adjusted on a crud level:
- ExampleCrudController.php
-
// configureFields() yield TextareaField::new('myTextField') ->onlyOnForms() ->setFormType(CKEditorType::class) // Customize CKEditor config ->setFormTypeOption('config', [ 'toolbar' => 'full', ]) ;
-
https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_stylesSet.html
https://symfony.com/bundles/FOSCKEditorBundle/current/usage/style.html
A custom field: HtmlField / Unmapped Entity Getter
-
<?php // src/Admin/Field/HtmlField.php namespace App\Admin\Field; use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface; use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait; use Symfony\Component\Form\Extension\Core\Type\TextType; /** * Field for index/list view to display html generated by unmapped entity getters */ class HtmlField implements FieldInterface { use FieldTrait; /** * @param string|false|null $label */ public static function new(string $propertyName, $label = null): self { return (new self()) ->setProperty($propertyName) ->setLabel($label) ->setTemplatePath('admin/field/html_field.html.twig') ->setFormType(TextType::class) ; } }
-
// src/Entity/Foo.php // Example for an unmapped getter for EasyAdmin public function getViewLink(): string { return '<a href="/foo/' . $this->getUrl() . '" target="_blank">View</a>'; }
-
// src/Controller/Admin/FooCrudController.php public function configureFields(string $pageName): iterable { yield HtmlField::new('viewLink')->onlyOnIndex(); ...
Note: it seems like it is not possible to create completely artificial/virtual columns. You need to have at least a dummy getter in the entity.
Override a specific form type template
- https://symfony.com/bundles/EasyAdminBundle/current/design.html
- https://symfony.com/doc/current/form/form_themes.html#creating-your-own-form-theme
Example: add image preview to "ImageField".
First let's investigate:
- class ImageField
-
// Define which form field to use ->setFormType(FileUploadType::class)
-
- class FileUploadType
-
// This is the connection between the form type and the twig template block. // It defined the prefix for the form field public function getBlockPrefix(): string { return 'ea_fileupload'; } // buildView() defines what data is injected into the template public function buildView(FormView $view, FormInterface $form, array $options): void { ... $view->vars['currentFiles'] = $currentFiles; $view->vars['multiple'] = $options['multiple']; $view->vars['allow_add'] = $options['allow_add']; $view->vars['allow_delete'] = $options['allow_delete']; $view->vars['download_path'] = $options['download_path']; }
-
- vendor/easycorp/easyadmin-bundle/src/Resources/views/crud/form_theme.html.twig
- This is the default form theme for Easyadmin.
Here we find the corresponding block which we want to override.-
{% block ea_fileupload_widget %} <div class="ea-fileupload"> <div class="input-group"> {% set placeholder = '' %} {% set title = '' %} ...
-
- This is the default form theme for Easyadmin.
Now let's override this block globally for all easyadmin cruds:
- class DashboardController
-
public function configureCrud(): Crud { return Crud::new() // add a custom form theme ->setFormThemes([ '@EasyAdmin/crud/form_theme.html.twig', // the original base form theme 'admin/form_theme.html.twig', // custom theme to override specific blocks, ]) ; }
-
- templates/admin/form_theme.html.twig
-
{# Copied block from vendor/easycorp/easyadmin-bundle/src/Resources/views/crud/form_theme.html.twig #} {# with added image preview html code #} {% block ea_fileupload_widget %} <div class="ea-fileupload"> ... <label class="btn" for="{{ form.file.vars.id }}"> <i class="fa fa-folder-open-o"></i> </label> </div> {# Added image preview #} {% if currentFiles is iterable and currentFiles|length > 0 %} <img style="margin-top: 2rem; max-height: 100px;" src="{{ '/' ~ download_path ~ (currentFiles|first).filename }}" alt="" /> {% endif %} </div> {% if multiple and currentFiles %} ...
-
Use another field as edit link
E.g. link "name" to the edit action instead of separate "Edit" link
-
// src/Controller/Admin/FooCrudController.php public function configureFields(string $pageName): iterable { // Add the trigger css class name: yield TextField::new('name')->setCssClass('use-as-edit-link'); ... } public function configureAssets(): Assets { $assets = parent::configureAssets(); $assets->addJsFile('js/easyadmin/move-edit-link.js'); return $assets; }
-
// /public/js/easyadmin/move-edit-link.js /* jshint esversion: 6 */ document.addEventListener("DOMContentLoaded", function(event) { // Remove the "edit" link and link the field with css class 'use-as-edit-link' instead // Get all elements with class "action-edit" const editLinks = document.querySelectorAll('.action-edit'); // Loop through each edit link editLinks.forEach(editLink => { // Get the href attribute of the current edit link const href = editLink.getAttribute('href'); // Hide the original edit link cell by setting its style to "display: none;" editLink.style.display = 'none'; // Find the closest parent row of the edit link const row = editLink.closest('tr'); // Find the element with class "use-as-edit-link" within the same row const useAsEditLink = row.querySelector('.use-as-edit-link'); // Update the innerHTML of the use-as-edit-link element to create a link useAsEditLink.innerHTML = `<a href="${href}">${useAsEditLink.innerHTML}</a>`; }); });
How to link to another CRUD entity/action
(Unfinished)
-
// src/Controller/Admin/FooCrudController.php public function configureFields(string $pageName): iterable { $adminUrl = $this->container->get(AdminUrlGenerator::class) ->setController(BarCrudController::class) ->setAction(Action::EDIT) ->setEntityId('__id__') ->generateUrl(); dump($adminUrl);
Duplicate / Clone
-
// src/Controller/Admin/ExampleCrudController.php public function clone(AdminContext $context, AdminUrlGenerator $adminUrlGenerator, KernelInterface $kernel) { $id = $context->getRequest()->query->get('entityId'); $entity = $this->getDoctrine()->getRepository(Studiengang::class)->find($id); $clone = clone $entity; $clone->setId(null); $clone->setSlug(null); $clone->setName($clone->getName() . ' COPY'); $now = new DateTime(); $clone->setCreatedAt($now); $clone->setUpdatedAt($now); $this->persistEntity($this->get('doctrine')->getManagerForClass($context->getEntity()->getFqcn()), $clone); $this->addFlash('success', 'Entity duplicated!'); // Use adminUrlGenerator, the original referer is properly retained $url = $adminUrlGenerator->setAll($context->getRequest()->query->all()) // Override params we want to modify ->setAction('edit') ->setEntityId($clone->getId()) ->generateUrl() ; return $this->redirect($url); }
Tom Select
Easyadmin3 uses https://tom-select.js.org/ for select widgets.
If I select an option in a select, add an item to a multiselect:
-
// src/Controller/Admin/ExampleCrudController.php ... public function configureAssets(Assets $assets): Assets { // public/js/easyadmin/example.js $assets->addJsFile('js/easyadmin/example.js'); return $assets; }
-
// public/js/easyadmin/example.js document.addEventListener("DOMContentLoaded", function(event) { const mainTownSelect = document.getElementById("mainTown"); const townsSelect = document.getElementById("towns"); providerTownSelect.addEventListener("change", function () { const selectedValue = mainTownSelect.value; const tomSelectInstance = townsSelect.tomselect; tomSelectInstance.addItem(selectedValue); }); });
-
// Add an empty option const tomSelectInstance = myElement.tomselect; tomSelectInstance.addOption({ value: '', text: '' }); tomSelectInstance.addItem(""); tomSelectInstance.refreshOptions(false);
Custom Action
-
// DasboardController.php public function configureMenuItems(): iterable { // https://fontawesome.com/v5/search?q=check&o=r&m=free&f=classic return[ MenuItem::linkToCrud('Stats', 'fas fa-chart-bar', Example::class) ->setController(ExampleCrudController::class) ->setAction('stats'), ]; }
-
// ExampleCrudController.php public function stats() { ... return $this->render('admin/example/stats.html.twig', [ 'rows' => $rows, ]); }
-
// admin/example/stats.html.twig {% extends '@EasyAdmin/page/content.html.twig' %} {% block main %} <h1>{{ 'Stats' }}</h1> <table class="table table-bordered table-striped"> <thead> <tr> {% for key, _ in rows[0] %} <th>{{ key }}</th> {% endfor %} </tr> </thead> <tbody> {% for row in rows %} <tr> {% for _, value in row %} <td>{{ value }}</td> {% endfor %} </tr> {% endfor %} </tbody> </table> {% endblock %}
Action without a template, but capture output and display it within Easyadmin
-
// ExampleCrudController.php public function import() { // Start output buffering ob_start(); echo "fooo\n"; $content = ob_get_clean(); return $this->render('admin/content.html.twig', [ 'headline' => 'My Import', 'pre' => true, 'content' => $content, ]); }
-
// templates/admin/content.html.twig {% extends '@EasyAdmin/page/content.html.twig' %} {% block main %} <h1>{{ headline }}</h1> {% if (pre|default(null)) %} <pre>{{- content|raw -}}</pre> {% else %} {{- content|raw -}} {% endif %} {% endblock %}
Modify an action
- ->update(Crud::PAGE_DETAIL, Action::EDIT, static function (Action $action) {
return $action->setIcon('fa fa-edit');
})
Action: use the entity / discover arguments:
- $viewAction = Action::new('view')
->linkToUrl(function() {
dd(func_get_args());
}); - $viewAction = Action::new('view')
->linkToUrl(function(Question $question) {
return $this->generateUrl('app_question_show', [
'slug' => $question->getSlug(),
]);
});
global action on list view:
- ->createAsGlobalAction();
custom action with redirect
- action:
- public function approve(AdminContext $adminContext, EntityManagerInterface $entityManager, AdminUrlGenerator $adminUrlGenerator)
{
$question = $adminContext->getEntity()->getInstance();
if (!$question instanceof Question) {
throw new \LogicException('Entity is missing or not a Question');
}
$question->setIsApproved(true);
$entityManager->flush();
$targetUrl = $adminUrlGenerator
->setController(self::class)
->setAction(Crud::PAGE_DETAIL)
->setEntityId($question->getId())
->generateUrl();
return $this->redirect($targetUrl);
}
- public function approve(AdminContext $adminContext, EntityManagerInterface $entityManager, AdminUrlGenerator $adminUrlGenerator)
- template
- templates/admin/approve_action.html.twig
- {# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# @var action \EasyCorp\Bundle\EasyAdminBundle\Dto\ActionDto #}
{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}
<form action="{{ action.linkUrl }}" method="POST" class="me-2">
{{ include('@EasyAdmin/crud/action.html.twig') }}
</form>
order actions
- ->reorder(Crud::PAGE_DETAIL, [
'approve',
'view',
Action::EDIT,
Action::INDEX,
Action::DELETE,
]);
Customization / Structure
- One route in DashboardController: /admin
- crudController and crudAction are in every URL.
- /admin?
- crudAction=index&
- crudControllerFqcn=App%5CController%5CAdmin%5CStudiengangCrudController&
- menuIndex=3&
- submenuIndex=-1
- Fields
- Pseudo properties: Entity::getFullName()
- No ArrayField in easyadmin3
- $field->setFormType(ChoiceType::class)->setFormTypOptions(['choices' => [])
- Field Configurators: vendor/easycorp/easyadmin-bundle/src/Field/Configurator
- Formatted Value:
- yield AvatarField::new('avatar')
->formatValue(static function ($value, User $user) {
return $user->getAvatarUrl();
});
- yield AvatarField::new('avatar')
- Injection
- public function index(ChartBuilderInterface $chartBuilder = null): Response
{
assert(null !== $chartBuilder);
- public function index(ChartBuilderInterface $chartBuilder = null): Response
- QueryBuilder:
- public function createIndexQueryBuilder(SearchDto $searchDto, EntityDto $entityDto, FieldCollection $fields, FilterCollection $filters): QueryBuilder
{
return parent::createIndexQueryBuilder($searchDto, $entityDto, $fields, $filters)
->andWhere('entity.isApproved = :approved')
->setParameter('approved', false);
}
- public function createIndexQueryBuilder(SearchDto $searchDto, EntityDto $entityDto, FieldCollection $fields, FilterCollection $filters): QueryBuilder
- Configure Crud, inject entity
- ->setPageTitle(Crud::PAGE_DETAIL, static function (Question $question) {
return sprintf('#%s %s', $question->getId(), $question->getName());
});
- ->setPageTitle(Crud::PAGE_DETAIL, static function (Question $question) {
- Events, good for enhancments for multiple entities
- e.g. BeforeEntityUpdatedEvent
- symfony console make:subscriber
- Controller override methods, e.g. updateEntity()
- public function updateEntity(EntityManagerInterface $entityManager, $entityInstance): void
{
$user = $this->getUser();
if (!$user instanceof User) {
throw new \LogicException('Currently logged in user is not an instance of User?!');
}
$entityInstance->setUpdatedBy($user);
parent::updateEntity($entityManager, $entityInstance);
} - public function deleteEntity(EntityManagerInterface $entityManager, $entityInstance): void
{
if ($entityInstance->getIsApproved()) {
throw new \Exception('Deleting approved questions is forbidden!');
}
parent::deleteEntity($entityManager, $entityInstance);
}
- public function updateEntity(EntityManagerInterface $entityManager, $entityInstance): void
- Tweak actions with update() e.g displayIf()
- public function configureActions(Actions $actions): Actions
{
return parent::configureActions($actions)
->update(Crud::PAGE_INDEX, Action::DELETE, static function(Action $action) {
$action->displayIf(static function (Question $question) {
return !$question->getIsApproved();
});
})
- public function configureActions(Actions $actions): Actions
- Tamper with action buttons: Events / AdminContext / crudDto:
- https://symfonycasts.com/screencast/easyadminbundle/dynamic-disable-action#disabling-the-action
-
<?php
// src/EventSubscriber/HideActionSubscriber.phpnamespace App\EventSubscriber;
use App\Entity\Question;
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Dto\ActionDto;
use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeCrudActionEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;class HideActionSubscriber implements EventSubscriberInterface
{
public function onBeforeCrudActionEvent(BeforeCrudActionEvent $event)
{
if (!$adminContext = $event->getAdminContext()) {
return;
}
if (!$crudDto = $adminContext->getCrud()) {
return;
}
if ($crudDto->getEntityFqcn() !== Question::class) {
return;
}// disable action entirely delete, detail, edit
$question = $adminContext->getEntity()->getInstance();
if ($question instanceof Question && $question->getIsApproved()) {
$crudDto->getActionsConfig()->disableActions([Action::DELETE]);
}// This gives you the "configuration for all the actions".
// Calling ->getActions() returns the array of actual actions that will be
// enabled for the current page... so then we can modify the one for "delete"
$actions = $crudDto->getActionsConfig()->getActions();
if (!$deleteAction = $actions[Action::DELETE] ?? null) {
return;
}
$deleteAction->setDisplayCallable(function(Question $question) {
return !$question->getIsApproved();
});
}public static function getSubscribedEvents()
{
return [
BeforeCrudActionEvent::class => 'onBeforeCrudActionEvent',
];
}
}
- public function onBeforeCrudActionEvent(BeforeCrudActionEvent $event)
{
if (!$adminContext = $event->getAdminContext()) {
return;
}
if (!$crudDto = $adminContext->getCrud()) {
return;
}
Tamper with filter query
- public function export(AdminContext $context)
{
$fields = FieldCollection::new($this->configureFields(Crud::PAGE_INDEX));
$filters = $this->container->get(FilterFactory::class)->create($context->getCrud()->getFiltersConfig(), $fields, $context->getEntity());
$queryBuilder = $this->createIndexQueryBuilder($context->getSearch(), $context->getEntity(), $fields, $filters);
}
Custom URL with query params
- class QuestionCrudController extends AbstractCrudController
{
private AdminUrlGenerator $adminUrlGenerator;
private RequestStack $requestStack;
public function __construct(AdminUrlGenerator $adminUrlGenerator, RequestStack $requestStack)
{
$this->adminUrlGenerator = $adminUrlGenerator;
$this->requestStack = $requestStack;
} - // Generate the same URL that I have now... but change the action to point to export.
$exportAction = Action::new('export')
->linkToUrl(function () {
$request = $this->requestStack->getCurrentRequest();
return $this->adminUrlGenerator->setAll($request->query->all())
->setAction('export')
->generateUrl();
})
twig link to easyadmin
- <a class="text-white" href="{{ ea_url()
.setController('App\\Controller\\Admin\\QuestionCrudController')
.setAction('edit')
.setEntityId(question.id)
}}">
Custom elements in template / Pass custom data
src/Controller/admin/ExampleCrudController:
-
public function configureCrud(Crud $crud): Crud { return $crud ... ->overrideTemplate('crud/index', 'admin/crud/example/index.html.twig'); } public function configureResponseParameters(KeyValueStore $responseParameters): KeyValueStore { $responseParameters->set('foo', 'bar'); return $responseParameters; }
templates/admin/crud/example/index.html.twig:
-
{% extends '@EasyAdmin/crud/index.html.twig' %} {% block content_title %} {{ parent() }} {{ foo }} {% endblock %} {% block main %} {{ parent() }} hello <script src="https://example.com/example.js"></script> {% endblock %}
Filters
https://symfony.com/bundles/EasyAdminBundle/current/filters.html#unmapped-filters
Autocomplete Filter https://gist.github.com/wizhippo/9043a6676ce2920676b730ba2f507655
Sidebar filters prototype: https://github.com/klemens-u/easyadminSidebarFilters
Add a custom filter
- https://stackoverflow.com/questions/68722242/easyadmin-3-3-create-custom-filter
-
<?php //src/Admin/Form/HasContentFilterType.php namespace App\Admin\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\OptionsResolver\OptionsResolver; class HasContentFilterType extends AbstractType { public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'choices' => [ 'Has Content' => 'not_empty', 'Is Empty' => 'empty', ], ]); } public function getParent() { return ChoiceType::class; } }
-
<?php // src/Admin/Filter/HasContentFilter.php namespace App\Admin\Filter; use App\Admin\Form\HasContentFilterType; use Doctrine\ORM\QueryBuilder; use EasyCorp\Bundle\EasyAdminBundle\Contracts\Filter\FilterInterface; use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto; use EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto; use EasyCorp\Bundle\EasyAdminBundle\Dto\FilterDataDto; use EasyCorp\Bundle\EasyAdminBundle\Filter\FilterTrait; class HasContentFilter implements FilterInterface { use FilterTrait; public static function new(string $propertyName, $label = null): self { return (new self()) ->setFilterFqcn(__CLASS__) ->setProperty($propertyName) ->setLabel($label) ->setFormType(HasContentFilterType::class); } public function apply(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void { if ('not_empty' === $filterDataDto->getValue()) { $queryBuilder->andWhere(sprintf('%s.%s IS NOT NULL AND %s.%s != \'\'', $filterDataDto->getEntityAlias(), $filterDataDto->getProperty(), $filterDataDto->getEntityAlias(), $filterDataDto->getProperty())); } elseif ('empty' === $filterDataDto->getValue()) { $queryBuilder->andWhere(sprintf('%s.%s IS NULL OR %s.%s = \'\'', $filterDataDto->getEntityAlias(), $filterDataDto->getProperty(), $filterDataDto->getEntityAlias(), $filterDataDto->getProperty())); } } }
-
// FooCrudController.php public function configureFilters(Filters $filters): Filters { $filters ->add('name') ->add(HasContentFilter::new('logo')) ; return $filters; }
Internals
Internal actions
Easyadmin actions are in AbstractCrudController, eg. edit(). Entities are fetched via $context->getEntity()->getInstance()
This resolves finally to EntityFactory::getEntityInstance() where the actual fetch code resides.