From aabe78f39beae5126e33847b1c61894e8563404d Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sat, 27 Nov 2021 23:09:08 +0100 Subject: [PATCH] Add support for ArrayAccess --- src/Admin/AdminHelper.php | 9 ++- tests/Admin/AdminHelperTest.php | 117 +++++++++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 8 deletions(-) diff --git a/src/Admin/AdminHelper.php b/src/Admin/AdminHelper.php index 963e0fd6f0..6080226456 100644 --- a/src/Admin/AdminHelper.php +++ b/src/Admin/AdminHelper.php @@ -13,7 +13,6 @@ namespace Sonata\AdminBundle\Admin; -use Doctrine\Common\Collections\Collection; use Sonata\AdminBundle\Exception\NoValueException; use Sonata\AdminBundle\FieldDescription\FieldDescriptionInterface; use Sonata\AdminBundle\Manipulator\ObjectManipulator; @@ -167,10 +166,10 @@ public function appendFormFieldElement(AdminInterface $admin, object $subject, s $collection = $this->propertyAccessor->getValue($subject, $path); - if (!($collection instanceof Collection)) { + if (!$collection instanceof \ArrayAccess && !\is_array($collection)) { throw new \TypeError(sprintf( - 'Collection must be an instance of %s, %s given.', - Collection::class, + 'Collection must be an instance of %s or array, %s given.', + \ArrayAccess::class, \is_object($collection) ? 'instance of "'.\get_class($collection).'"' : '"'.\gettype($collection).'"' )); } @@ -180,7 +179,7 @@ public function appendFormFieldElement(AdminInterface $admin, object $subject, s explode('.', preg_replace('#\[\d*?]#', '', $path) ?? '') ); - $collection->add(new $modelClassName()); + $collection[] = new $modelClassName(); $this->propertyAccessor->setValue($subject, $path, $collection); $fieldDescription = null; diff --git a/tests/Admin/AdminHelperTest.php b/tests/Admin/AdminHelperTest.php index 55aca5cf6a..728fe0db0f 100644 --- a/tests/Admin/AdminHelperTest.php +++ b/tests/Admin/AdminHelperTest.php @@ -245,7 +245,7 @@ public function testAppendFormFieldElement(): void } } - public function testAppendFormFieldElementWithoutFormFieldDescriptionInAdminAndNoCollectionClass(): void + public function testAppendFormFieldElementWithoutFormFieldDescriptionInAdminAndNoArrayAccess(): void { $admin = $this->createMock(AdminInterface::class); $admin @@ -329,7 +329,7 @@ public function testAppendFormFieldElementWithoutFormFieldDescriptionInAdminAndN $admin->method('getFormBuilder')->willReturn($formBuilder); $this->expectException(\TypeError::class); - $this->expectExceptionMessage(sprintf('Collection must be an instance of %s, "%s" given.', Collection::class, \gettype(null))); + $this->expectExceptionMessage(sprintf('Collection must be an instance of %s or array, "%s" given.', \ArrayAccess::class, \gettype(null))); $this->helper->appendFormFieldElement($admin, $foo, 'test_bar'); } @@ -444,6 +444,117 @@ public function setBar(Collection $bar): void } } + public function testAppendFormFieldElementWithArray(): void + { + $admin = $this->createMock(AdminInterface::class); + $admin + ->method('getClass') + ->willReturn(Foo::class); + + $associationAdmin = $this->createMock(AdminInterface::class); + $associationAdmin + ->method('getClass') + ->willReturn(Bar::class); + + $associationMapping = [ + 'fieldName' => 'bar', + 'targetEntity' => Foo::class, + 'sourceEntity' => Foo::class, + 'isOwningSide' => false, + ]; + + $fieldDescription = $this->createStub(FieldDescriptionInterface::class); + $fieldDescription->method('getAssociationAdmin')->willReturn($associationAdmin); + $fieldDescription->method('getAssociationMapping')->willReturn($associationMapping); + $fieldDescription->method('getParentAssociationMappings')->willReturn([]); + + $admin + ->method('getFormFieldDescription') + ->willReturn($fieldDescription); + + $associationAdmin + ->method('getFormFieldDescriptions') + ->willReturn([ + 'bar' => $fieldDescription, + ]); + + $admin + ->method('hasFormFieldDescription') + ->with($associationMapping['fieldName']) + ->willReturn(false); + + $request = new Request([], [ + 'test' => [ + 'bar' => [ + [ + 'baz' => [ + 'baz' => true, + ], + ], + ['_delete' => true], + ], + ], + ]); + + $admin + ->method('getRequest') + ->willReturn($request); + + $foo = new class() { + /** @var Collection */ + private $bar; + + public function __construct() + { + $this->bar = new ArrayCollection(); + } + + /** @return array */ + public function getBar(): array + { + return $this->bar->toArray(); + } + + /** @param array $bar */ + public function setBar(array $bar): void + { + $this->bar = new ArrayCollection($bar); + } + }; + + $admin + ->method('hasSubject') + ->willReturn(true); + $admin + ->method('getSubject') + ->willReturn($foo); + + $dataMapper = $this->createStub(DataMapperInterface::class); + $formFactory = $this->createStub(FormFactoryInterface::class); + $eventDispatcher = $this->createStub(EventDispatcherInterface::class); + $formBuilder = new FormBuilder('test', \get_class($foo), $eventDispatcher, $formFactory); + $formBuilder->setRequestHandler(new HttpFoundationRequestHandler()); + $childFormBuilder = new FormBuilder('bar', \stdClass::class, $eventDispatcher, $formFactory); + $childFormBuilder->setCompound(true); + $childFormBuilder->setDataMapper($dataMapper); + $subChildFormBuilder = new FormBuilder('baz', \stdClass::class, $eventDispatcher, $formFactory); + $subChildFormBuilder->setCompound(true); + $subChildFormBuilder->setDataMapper($dataMapper); + $childFormBuilder->add($subChildFormBuilder); + + $formBuilder->setCompound(true); + $formBuilder->setDataMapper($dataMapper); + $formBuilder->add($childFormBuilder); + + $admin->method('getFormBuilder')->willReturn($formBuilder); + + $finalForm = $this->helper->appendFormFieldElement($admin, $foo, 'test_bar')[1]; + + foreach ($finalForm->get($childFormBuilder->getName()) as $childField) { + static::assertFalse($childField->has('_delete')); + } + } + public function testAppendFormFieldElementNested(): void { $admin = $this->createMock(AdminInterface::class); @@ -498,7 +609,7 @@ public function testAppendFormFieldElementNested(): void $admin->expects(static::once())->method('getFormBuilder')->willReturn($formBuilder); $this->expectException(\TypeError::class); - $this->expectExceptionMessage(sprintf('Collection must be an instance of %s, "string" given.', Collection::class)); + $this->expectExceptionMessage(sprintf('Collection must be an instance of %s or array, "string" given.', \ArrayAccess::class)); $this->helper->appendFormFieldElement($admin, $object, 'uniquePartOfId_sub_object_0_and_more_0_final_data'); }