Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 767847f

Browse files
committed
Merge branch '4.x' into 5.x
* 4.x: Fix more deprecations Bump development version Prepare 4.27.1 release Update CrudAutocompleteSubscriber to also handle array of UUIDs correctly Minor tweaks Fix for EasyCorp#7208 Bump development version Prepare 4.27.0 release Minor reword Set CrudFormType as default entry_type for CollectionFields if the property is an association Fix Symfony 7.4 deprecations Minor fix docs related to admin routes Fix some deprecations in form templates Deprecate remaining Doctrine wrappers in EntityDto and access mapping values by offset (works in orm v2 and v3)
2 parents 21de61b + 74c226b commit 767847f

33 files changed

Lines changed: 284 additions & 301 deletions

doc/actions.rst

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -640,17 +640,39 @@ The following example shows all kinds of actions in practice::
640640
of the shortcuts and utilities available in regular `Symfony controllers`_,
641641
such as ``$this->render()``, ``$this->redirect()``, and others.
642642

643-
Custom actions can define the ``#[AdminRoute]`` attribute to
644-
:ref:`customize their route name, path and methods <crud_routes>`::
643+
It's recommended to apply the ``#[AdminRoute]`` attribute to your custom actions
644+
to :ref:`customize their route name, path and methods <crud_routes>`. This is
645+
recommended even for custom actions defined as methods in the CRUD controllers::
645646

646-
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminRoute;
647-
// ...
647+
namespace App\Controller\Admin;
648648

649+
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminRoute;
650+
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
651+
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
652+
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
649653

650-
#[AdminRoute(path: '/invoice', name: 'view_invoice')]
651-
public function renderInvoice(AdminContext $context)
654+
class OrderCrudController extends AbstractCrudController
652655
{
656+
public function configureActions(Actions $actions): Actions
657+
{
658+
$viewInvoice = Action::new('viewInvoice', 'Invoice', 'fa fa-file-invoice')
659+
->linkToCrudAction('renderInvoice');
660+
661+
// ...
662+
}
663+
653664
// ...
665+
666+
#[AdminRoute(path: '/invoice', name: 'view_invoice')]
667+
public function renderInvoice(AdminContext $context)
668+
{
669+
// if the dashboard uses 'admin' as the main route name, the resulting
670+
// route of this action will be:
671+
// path: /admin/order/invoice
672+
// name: admin_order_view_invoice
673+
674+
// ...
675+
}
654676
}
655677

656678
.. _global-actions:
@@ -823,7 +845,6 @@ for the actions using the ``#[AdminRoute]`` attribute::
823845
$this->businessStatsCalculator = $businessStatsCalculator;
824846
}
825847

826-
#[Route("/admin/business-stats", name: "business_stats_index")]
827848
#[AdminRoute("/", name: "index")]
828849
public function index()
829850
{
@@ -832,7 +853,6 @@ for the actions using the ``#[AdminRoute]`` attribute::
832853
]);
833854
}
834855

835-
#[Route("/admin/business-stats/{id}", name: "business_stats_customer")]
836856
#[AdminRoute("/{id}", name: "customer")]
837857
public function customer(Customer $customer)
838858
{

doc/fields/CollectionField.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ class name of the controller as the first argument::
195195

196196
The ``useEntryCrudForm()`` method requires Symfony 6.1 or newer version.
197197

198+
.. note::
199+
200+
For Doctrine associations, you can omit the ``useEntryCrudForm()`` method.
201+
When no Symfony form option ``entry_type`` is set, ``CollectionField`` for
202+
association properties will use ``CrudFormType`` as the default ``entry_type``.
203+
198204
JavaScript Events
199205
-----------------
200206

src/Controller/AbstractCrudController.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,10 +478,11 @@ public function autocomplete(AdminContext $context): JsonResponse
478478
{
479479
$queryBuilder = $this->createIndexQueryBuilder($context->getSearch(), $context->getEntity(), FieldCollection::new([]), FilterCollection::new());
480480

481-
$autocompleteContext = $context->getRequest()->get(AssociationField::PARAM_AUTOCOMPLETE_CONTEXT);
481+
$autocompleteContext = $context->getRequest()->query->all(AssociationField::PARAM_AUTOCOMPLETE_CONTEXT);
482482

483+
$crudControllerFqcn = $autocompleteContext[EA::CRUD_CONTROLLER_FQCN] ?? $context->getRequest()->attributes->get(EA::CRUD_CONTROLLER_FQCN) ?? $context->getRequest()->query->get(EA::CRUD_CONTROLLER_FQCN);
483484
/** @var CrudControllerInterface $controller */
484-
$controller = $this->container->get(ControllerFactory::class)->getCrudControllerInstance($autocompleteContext[EA::CRUD_CONTROLLER_FQCN] ?? $context->getRequest()->get(EA::CRUD_CONTROLLER_FQCN), Action::INDEX, $context->getRequest());
485+
$controller = $this->container->get(ControllerFactory::class)->getCrudControllerInstance($crudControllerFqcn, Action::INDEX, $context->getRequest());
485486
/** @var FieldDto|null $field */
486487
$field = FieldCollection::new($controller->configureFields($autocompleteContext['originatingPage']))->getByProperty($autocompleteContext['propertyName']);
487488
/** @var \Closure|null $queryBuilderCallable */

src/Controller/AbstractDashboardController.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,19 @@
1515
use EasyCorp\Bundle\EasyAdminBundle\Security\Permission;
1616
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
1717
use Symfony\Component\HttpFoundation\Response;
18-
use Symfony\Component\Routing\Annotation\Route;
1918
use Symfony\Component\Security\Core\User\UserInterface;
2019
use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
2120
use function Symfony\Component\Translation\t;
2221

22+
// needed for Symfony 5.4 - 8.0 compatibility (Atribute doesn't exist in 5.4 and
23+
// Annotation doesn't exist in 8.0; both exist in the other versions)
24+
if (!class_exists(Route::class)) {
25+
// @phpstan-ignore-next-line class.notFound
26+
class_alias(\Symfony\Component\Routing\Annotation\Route::class, Route::class);
27+
}
28+
29+
use Symfony\Component\Routing\Attribute\Route;
30+
2331
/**
2432
* This class is useful to extend your dashboard from it instead of implementing
2533
* the interface.

src/Dto/EntityDto.php

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22

33
namespace EasyCorp\Bundle\EasyAdminBundle\Dto;
44

5-
use Doctrine\ORM\Mapping\AssociationMapping;
65
use Doctrine\ORM\Mapping\ClassMetadata;
7-
use Doctrine\ORM\Mapping\FieldMapping;
86
use EasyCorp\Bundle\EasyAdminBundle\Collection\ActionCollection;
97
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
10-
use EasyCorp\Bundle\EasyAdminBundle\Config\KeyValueStore;
118
use Symfony\Component\ExpressionLanguage\Expression;
129
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
1310
use Symfony\Component\PropertyAccess\PropertyAccess;
@@ -146,63 +143,6 @@ public function getClassMetadata(): ClassMetadata
146143
return $this->metadata;
147144
}
148145

149-
public function getPropertyMetadata(string $propertyName): KeyValueStore
150-
{
151-
if (isset($this->metadata->fieldMappings[$propertyName])) {
152-
/** @var FieldMapping|array $fieldMapping */
153-
/** @phpstan-ignore-next-line */
154-
$fieldMapping = $this->metadata->fieldMappings[$propertyName];
155-
156-
// Doctrine ORM 2.x returns an array and Doctrine ORM 3.x returns a FieldMapping object
157-
if ($fieldMapping instanceof FieldMapping) {
158-
$fieldMapping = (array) $fieldMapping;
159-
}
160-
161-
return KeyValueStore::new($fieldMapping);
162-
}
163-
164-
if ($this->metadata->hasAssociation($propertyName)) {
165-
/** @var AssociationMapping|array $associationMapping */
166-
/** @phpstan-ignore-next-line */
167-
$associationMapping = $this->metadata->associationMappings[$propertyName];
168-
169-
// Doctrine ORM 2.x returns an array and Doctrine ORM 3.x returns an AssociationMapping object
170-
if ($associationMapping instanceof AssociationMapping) {
171-
// Doctrine ORM 3.x doesn't include the 'type' key that tells the type of association
172-
// recreate that key to keep the code compatible with both versions
173-
$associationType = $associationMapping->type();
174-
175-
$associationMapping = (array) $associationMapping;
176-
$associationMapping['type'] = $associationType;
177-
}
178-
179-
return KeyValueStore::new($associationMapping);
180-
}
181-
182-
throw new \InvalidArgumentException(sprintf('The "%s" field does not exist in the "%s" entity.', $propertyName, $this->getFqcn()));
183-
}
184-
185-
public function hasProperty(string $propertyName): bool
186-
{
187-
return isset($this->metadata->fieldMappings[$propertyName])
188-
|| $this->metadata->hasAssociation($propertyName);
189-
}
190-
191-
public function isAssociation(string $propertyName): bool
192-
{
193-
if ($this->metadata->hasAssociation($propertyName)) {
194-
return true;
195-
}
196-
197-
if (!str_contains($propertyName, '.')) {
198-
return false;
199-
}
200-
201-
$propertyNameParts = explode('.', $propertyName, 2);
202-
203-
return !isset($this->metadata->embeddedClasses[$propertyNameParts[0]]);
204-
}
205-
206146
/**
207147
* @param TEntity|null $newEntityInstance
208148
*/

src/Factory/FieldFactory.php

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
namespace EasyCorp\Bundle\EasyAdminBundle\Factory;
44

55
use Doctrine\DBAL\Types\Types;
6-
use Doctrine\ORM\Mapping\AssociationMapping;
7-
use Doctrine\ORM\Mapping\FieldMapping;
86
use EasyCorp\Bundle\EasyAdminBundle\Collection\EntityCollection;
97
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
108
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
@@ -143,19 +141,16 @@ private function replaceGenericFieldsWithSpecificFields(FieldCollection $fields,
143141
}
144142

145143
// this is a virtual field, so we can't autoconfigure it
146-
if (!$entityDto->hasProperty($fieldDto->getProperty())) {
144+
if (!isset($entityDto->getClassMetadata()->fieldMappings[$fieldDto->getProperty()])
145+
&& !$entityDto->getClassMetadata()->hasAssociation($fieldDto->getProperty())) {
147146
continue;
148147
}
149148

150149
if ($fieldDto->getProperty() === $entityDto->getClassMetadata()->getSingleIdentifierFieldName()) {
151150
$guessedFieldFqcn = IdField::class;
152151
} elseif ($entityDto->getClassMetadata()->hasAssociation($fieldDto->getProperty())) {
153-
/** @var AssociationMapping|array $associationMapping */
154-
/** @phpstan-ignore-next-line */
155-
$associationMapping = $entityDto->getClassMetadata()->getAssociationMapping($fieldDto->getProperty());
156-
$orphanRemoval = $associationMapping instanceof AssociationMapping
157-
? $associationMapping->orphanRemoval
158-
: (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']);
152+
/** @var bool $orphanRemoval */
153+
$orphanRemoval = $entityDto->getClassMetadata()->getAssociationMapping($fieldDto->getProperty())['orphanRemoval'];
159154
if ($orphanRemoval && $entityDto->getClassMetadata()->isCollectionValuedAssociation($fieldDto->getProperty())) {
160155
$guessedFieldFqcn = CollectionField::class;
161156
} else {
@@ -164,20 +159,10 @@ private function replaceGenericFieldsWithSpecificFields(FieldCollection $fields,
164159
} elseif (!isset($entityDto->getClassMetadata()->fieldMappings[$fieldDto->getProperty()])) {
165160
throw new \RuntimeException(sprintf('Could not guess a field class for "%s" field. It possibly is an association field or an embedded class field.', $fieldDto->getProperty()));
166161
} else {
167-
// Doctrine ORM 2.x returns an array and Doctrine ORM 3.x returns a FieldMapping object
168-
/** @var FieldMapping|array $fieldMapping */
169-
/** @phpstan-ignore-next-line */
170162
$fieldMapping = $entityDto->getClassMetadata()->getFieldMapping($fieldDto->getProperty());
171-
if (\is_array($fieldMapping)) {
172-
$doctrineFieldMappingType = $fieldMapping['type'];
173-
} else {
174-
$doctrineFieldMappingType = $fieldMapping->type;
175-
}
176-
177-
$guessedFieldFqcn = self::$doctrineTypeToFieldFqcn[$doctrineFieldMappingType] ?? null;
178-
163+
$guessedFieldFqcn = self::$doctrineTypeToFieldFqcn[$fieldMapping['type']] ?? null;
179164
if (null === $guessedFieldFqcn) {
180-
throw new \RuntimeException(sprintf('The Doctrine type of the "%s" field is "%s", which is not supported by EasyAdmin. For Doctrine\'s Custom Mapping Types have a look at EasyAdmin\'s field docs.', $fieldDto->getProperty(), $doctrineFieldMappingType));
165+
throw new \RuntimeException(sprintf('The Doctrine type of the "%s" field is "%s", which is not supported by EasyAdmin. For Doctrine\'s Custom Mapping Types have a look at EasyAdmin\'s field docs.', $fieldDto->getProperty(), $fieldMapping['type']));
181166
}
182167
}
183168

src/Factory/FilterFactory.php

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace EasyCorp\Bundle\EasyAdminBundle\Factory;
44

55
use Doctrine\DBAL\Types\Types;
6-
use Doctrine\ORM\Mapping\FieldMapping;
76
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
87
use EasyCorp\Bundle\EasyAdminBundle\Collection\FilterCollection;
98
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Filter\FilterConfiguratorInterface;
@@ -96,24 +95,16 @@ public function create(FilterConfigDto $filterConfig, FieldCollection $fields, E
9695

9796
private function guessFilterClass(EntityDto $entityDto, string $propertyName): string
9897
{
99-
if ($entityDto->isAssociation($propertyName)) {
98+
if ($entityDto->getClassMetadata()->hasAssociation($propertyName)) {
10099
return EntityFilter::class;
101100
}
102101

103102
if (isset($entityDto->getClassMetadata()->embeddedClasses[$propertyName])) {
104103
return TextFilter::class;
105104
}
106105

107-
// Doctrine ORM 2.x returns an array and Doctrine ORM 3.x returns a FieldMapping object
108-
/** @var FieldMapping|array $fieldMapping */
109-
/** @phpstan-ignore-next-line */
110106
$fieldMapping = $entityDto->getClassMetadata()->getFieldMapping($propertyName);
111-
if (\is_array($fieldMapping)) {
112-
$doctrineFieldMappingType = $fieldMapping['type'];
113-
} else {
114-
$doctrineFieldMappingType = $fieldMapping->type;
115-
}
116107

117-
return self::$doctrineTypeToFilterClass[$doctrineFieldMappingType] ?? TextFilter::class;
108+
return self::$doctrineTypeToFilterClass[$fieldMapping['type']] ?? TextFilter::class;
118109
}
119110
}

src/Field/Configurator/AssociationConfigurator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public function supports(FieldDto $field, EntityDto $entityDto): bool
5151
public function configure(FieldDto $field, EntityDto $entityDto, AdminContext $context): void
5252
{
5353
$propertyName = $field->getProperty();
54-
if (!$entityDto->isAssociation($propertyName)) {
54+
if (!$entityDto->getClassMetadata()->hasAssociation($propertyName)) {
5555
throw new \RuntimeException(sprintf('The "%s" field is not a Doctrine association, so it cannot be used as an association field.', $propertyName));
5656
}
5757

0 commit comments

Comments
 (0)