Skip to content

Commit 6f2f69d

Browse files
committed
feature #3551 Add option to modify query of AssociationField (with autocomplete) (lukasluecke)
This PR was squashed before being merged into the 3.0.x-dev branch. Discussion ---------- Add option to modify query of AssociationField (with autocomplete) Resolves #3476. Depends on #3550. Works the same for "default" and autocomplete fields. ```php AssociationField::new('property') ->autocomplete() // optional ->modifyQuery(fn(QueryBuilder $queryBuilder) => $queryBuilder ->where('entity.id > 20') ->orderBy('entity.field', 'DESC') ); ``` Commits ------- 4716e81 Add option to modify query of AssociationField (with autocomplete)
2 parents f237114 + 4716e81 commit 6f2f69d

File tree

3 files changed

+46
-5
lines changed

3 files changed

+46
-5
lines changed

src/Controller/AbstractCrudController.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
1717
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Controller\CrudControllerInterface;
1818
use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
19+
use EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto;
1920
use EasyCorp\Bundle\EasyAdminBundle\Dto\SearchDto;
2021
use EasyCorp\Bundle\EasyAdminBundle\Event\AfterCrudActionEvent;
2122
use EasyCorp\Bundle\EasyAdminBundle\Event\AfterEntityDeletedEvent;
@@ -29,10 +30,12 @@
2930
use EasyCorp\Bundle\EasyAdminBundle\Exception\ForbiddenActionException;
3031
use EasyCorp\Bundle\EasyAdminBundle\Exception\InsufficientEntityPermissionException;
3132
use EasyCorp\Bundle\EasyAdminBundle\Factory\ActionFactory;
33+
use EasyCorp\Bundle\EasyAdminBundle\Factory\ControllerFactory;
3234
use EasyCorp\Bundle\EasyAdminBundle\Factory\EntityFactory;
3335
use EasyCorp\Bundle\EasyAdminBundle\Factory\FilterFactory;
3436
use EasyCorp\Bundle\EasyAdminBundle\Factory\FormFactory;
3537
use EasyCorp\Bundle\EasyAdminBundle\Factory\PaginatorFactory;
38+
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
3639
use EasyCorp\Bundle\EasyAdminBundle\Form\Type\FiltersFormType;
3740
use EasyCorp\Bundle\EasyAdminBundle\Orm\EntityRepository;
3841
use EasyCorp\Bundle\EasyAdminBundle\Orm\EntityUpdater;
@@ -88,6 +91,7 @@ public static function getSubscribedServices()
8891
'event_dispatcher' => '?'.EventDispatcherInterface::class,
8992
ActionFactory::class => '?'.ActionFactory::class,
9093
AdminContextProvider::class => '?'.AdminContextProvider::class,
94+
ControllerFactory::class => '?'.ControllerFactory::class,
9195
CrudUrlGenerator::class => '?'.CrudUrlGenerator::class,
9296
EntityFactory::class => '?'.EntityFactory::class,
9397
EntityRepository::class => '?'.EntityRepository::class,
@@ -393,6 +397,21 @@ public function delete(AdminContext $context)
393397
public function autocomplete(AdminContext $context): JsonResponse
394398
{
395399
$queryBuilder = $this->createIndexQueryBuilder($context->getSearch(), $context->getEntity(), FieldCollection::new([]), FilterCollection::new());
400+
401+
$autocompleteContext = $context->getRequest()->get(AssociationField::PARAM_AUTOCOMPLETE_CONTEXT);
402+
403+
/** @var CrudControllerInterface $controller */
404+
$controller = $this->get(ControllerFactory::class)->getCrudControllerInstance($autocompleteContext['crudId'], Action::INDEX, $context->getRequest());
405+
/** @var FieldDto $field */
406+
$field = FieldCollection::new($controller->configureFields(Crud::PAGE_INDEX))->get($autocompleteContext['propertyName']);
407+
/** @var \Closure|null $modify */
408+
$modify = $field->getCustomOption(AssociationField::OPTION_MODIFY_QUERY);
409+
410+
if(null !== $modify)
411+
{
412+
$modify($queryBuilder);
413+
}
414+
396415
$paginator = $this->get(PaginatorFactory::class)->create($queryBuilder);
397416

398417
return JsonResponse::fromJsonString($paginator->getResultsAsJson());

src/Field/AssociationField.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ final class AssociationField implements FieldInterface
1515
public const OPTION_AUTOCOMPLETE = 'autocomplete';
1616
public const OPTION_CRUD_CONTROLLER = 'crudControllerFqcn';
1717
public const OPTION_WIDGET = 'widget';
18+
public const OPTION_MODIFY_QUERY = 'modifyQuery';
1819
/** @internal this option is intended for internal use only */
1920
public const OPTION_RELATED_URL = 'relatedUrl';
2021
/** @internal this option is intended for internal use only */
@@ -23,6 +24,9 @@ final class AssociationField implements FieldInterface
2324
public const WIDGET_AUTOCOMPLETE = 'autocomplete';
2425
public const WIDGET_NATIVE = 'native';
2526

27+
/** @internal this option is intended for internal use only */
28+
public const PARAM_AUTOCOMPLETE_CONTEXT = 'autocompleteContext';
29+
2630
public static function new(string $propertyName, ?string $label = null): self
2731
{
2832
return (new self())
@@ -34,6 +38,7 @@ public static function new(string $propertyName, ?string $label = null): self
3438
->setCustomOption(self::OPTION_AUTOCOMPLETE, false)
3539
->setCustomOption(self::OPTION_CRUD_CONTROLLER, null)
3640
->setCustomOption(self::OPTION_WIDGET, self::WIDGET_AUTOCOMPLETE)
41+
->setCustomOption(self::OPTION_MODIFY_QUERY, null)
3742
->setCustomOption(self::OPTION_RELATED_URL, null)
3843
->setCustomOption(self::OPTION_DOCTRINE_ASSOCIATION_TYPE, null);
3944
}
@@ -58,4 +63,11 @@ public function setCrudController(string $crudControllerFqcn): self
5863

5964
return $this;
6065
}
66+
67+
public function modifyQuery(\Closure $modify): self
68+
{
69+
$this->setCustomOption(self::OPTION_MODIFY_QUERY, $modify);
70+
71+
return $this;
72+
}
6173
}

src/Field/Configurator/AssociationConfigurator.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace EasyCorp\Bundle\EasyAdminBundle\Field\Configurator;
44

5+
use Doctrine\ORM\EntityRepository;
56
use Doctrine\ORM\PersistentCollection;
67
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
78
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
@@ -72,14 +73,23 @@ public function configure(FieldDto $field, EntityDto $entityDto, AdminContext $c
7273
->setAction('autocomplete')
7374
->setEntityId(null)
7475
->unset('sort') // Avoid passing the 'sort' param from the current entity to the autocompleted one
76+
->set(AssociationField::PARAM_AUTOCOMPLETE_CONTEXT, [
77+
'crudId' => $context->getRequest()->query->get('crudId'),
78+
'propertyName' => $propertyName,
79+
])
7580
->generateUrl();
7681

7782
$field->setFormTypeOption('attr.data-ea-autocomplete-endpoint-url', $autocompleteEndpointUrl);
78-
79-
// If the field is not required we allow clearing out the selection
80-
if (false === $field->getFormTypeOption('required')) {
81-
$field->setFormTypeOption('attr.data-allow-clear', 'true');
82-
}
83+
} else {
84+
$field->setFormTypeOption('query_builder', static function(EntityRepository $repository) use($field) {
85+
// TODO: should this use `createIndexQueryBuilder` instead, so we get the default ordering etc.?
86+
// it would then be identical to the one used in autocomplete action, but it is a bit complex getting it in here
87+
$queryBuilder = $repository->createQueryBuilder('entity');
88+
if($modify = $field->getCustomOption(AssociationField::OPTION_MODIFY_QUERY)) {
89+
$modify($queryBuilder);
90+
}
91+
return $queryBuilder;
92+
});
8393
}
8494
}
8595

0 commit comments

Comments
 (0)