From e68aecb1c2611cc42163d0cb48259901d02398ec Mon Sep 17 00:00:00 2001 From: Fran Moreno Date: Sun, 21 Feb 2021 10:36:08 +0100 Subject: [PATCH] Add AdminInterface::createNewInstance method Instead of overriding ModelManagerInterface::getModelInstance to custom construct their objects, now the user can do it from the related Admin class. The Instantiator class will be use in 4.0 and its code is copied from the persistence bundles. --- UPGRADE-3.x.md | 7 +++++ src/Admin/AbstractAdmin.php | 15 ++++++++++- src/Model/ModelManagerInterface.php | 2 ++ src/Util/Instantiator.php | 41 +++++++++++++++++++++++++++++ tests/Admin/AdminTest.php | 16 +++++------ tests/App/Admin/FooAdmin.php | 2 +- tests/App/Admin/TranslatedAdmin.php | 6 +++++ tests/App/Model/ModelManager.php | 3 +++ 8 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 src/Util/Instantiator.php diff --git a/UPGRADE-3.x.md b/UPGRADE-3.x.md index 0b5db0d3cd2..bdf3feb00f6 100644 --- a/UPGRADE-3.x.md +++ b/UPGRADE-3.x.md @@ -1,6 +1,13 @@ UPGRADE 3.x =========== +UPGRADE FROM 3.xx to 3.xx +========================= + +### Deprecated `Sonata\AdminBundle\Model\ModelManagerInterface::getModelInstance()` method. + +Use `Sonata\AdminBundle\Admin\AbstractAdmin::createNewInstance()` method instead. + UPGRADE FROM 3.88 to 3.89 ========================= diff --git a/src/Admin/AbstractAdmin.php b/src/Admin/AbstractAdmin.php index c40e55abd5f..0c07de67934 100644 --- a/src/Admin/AbstractAdmin.php +++ b/src/Admin/AbstractAdmin.php @@ -30,6 +30,8 @@ use Sonata\AdminBundle\Security\Handler\AclSecurityHandlerInterface; use Sonata\AdminBundle\Show\ShowMapper; use Sonata\AdminBundle\Templating\MutableTemplateRegistryInterface; +// NEXT_MAJOR: Uncomment next line. +// use Sonata\AdminBundle\Util\Instantiator; use Sonata\Form\Validator\Constraints\InlineConstraint; use Sonata\Form\Validator\ErrorElement; use Symfony\Component\Form\Extension\Core\Type\HiddenType; @@ -1247,7 +1249,7 @@ public function getTemplate($name) */ public function getNewInstance() { - $object = $this->getModelManager()->getModelInstance($this->getClass()); + $object = $this->createNewInstance(); $this->appendParentObject($object); $this->alterNewInstance($object); @@ -2887,6 +2889,17 @@ final public function hasTemplateRegistry(): bool return null !== $this->templateRegistry; } + /** + * @phpstan-return T + */ + protected function createNewInstance(): object + { + // NEXT_MAJOR: Uncomment next line and remove the other one. + // return Instantiator::instantiate($this->getClass()); + /* @phpstan-ignore-next-line */ + return $this->getModelManager()->getModelInstance($this->getClass(), 'sonata_deprecation_mute'); + } + /** * @phpstan-param T $object */ diff --git a/src/Model/ModelManagerInterface.php b/src/Model/ModelManagerInterface.php index df2fd23d5da..5e13a639572 100644 --- a/src/Model/ModelManagerInterface.php +++ b/src/Model/ModelManagerInterface.php @@ -191,6 +191,8 @@ public function getUrlSafeIdentifier($model); /** * Create a new instance of the model of the specified class. * + * @deprecated since sonata-project/admin-bundle 3.x. To be removed in 4.0. Use AdminInterface::createNewInstance instead. + * * @param string $class * * @return object diff --git a/src/Util/Instantiator.php b/src/Util/Instantiator.php new file mode 100644 index 00000000000..6a8a8ac70d2 --- /dev/null +++ b/src/Util/Instantiator.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sonata\AdminBundle\Util; + +/** + * @internal + */ +final class Instantiator +{ + /** + * @template T of object + * @phpstan-param class-string $class + * @phpstan-return T + */ + public static function instantiate(string $class): object + { + $r = new \ReflectionClass($class); + if ($r->isAbstract()) { + throw new \RuntimeException(sprintf('Cannot initialize abstract class: %s', $class)); + } + + $constructor = $r->getConstructor(); + + if (null !== $constructor && (!$constructor->isPublic() || $constructor->getNumberOfRequiredParameters() > 0)) { + return $r->newInstanceWithoutConstructor(); + } + + return new $class(); + } +} diff --git a/tests/Admin/AdminTest.php b/tests/Admin/AdminTest.php index a878f4c5ba9..fb9e32360d3 100644 --- a/tests/Admin/AdminTest.php +++ b/tests/Admin/AdminTest.php @@ -1627,13 +1627,13 @@ public function testGetNewInstanceForChildAdminWithCollectionParentValue(): void $formBuilder = $this->createStub(FormBuilderInterface::class); $formBuilder->method('getForm')->willReturn(null); - $postCategory = new PostCategory(); + $postCategoryAdmin = new PostCategoryAdmin('admin.post_category', PostCategory::class, 'MyBundle\MyController'); + // NEXT_MAJOR: Remove next three lines related to model manager. $modelManager = $this->createStub(ModelManagerInterface::class); - $modelManager->method('getModelInstance')->willReturn($postCategory); - - $postCategoryAdmin = new PostCategoryAdmin('admin.post_category', PostCategoryAdmin::class, 'MyBundle\MyController'); + $modelManager->method('getModelInstance')->willReturn(new PostCategory()); $postCategoryAdmin->setModelManager($modelManager); + $postCategoryAdmin->setParent($postAdmin); $request = $this->createStub(Request::class); @@ -1662,13 +1662,13 @@ public function testGetNewInstanceForEmbededAdminWithParentValue(): void $parentField->method('getParentAssociationMappings')->willReturn([]); $parentField->method('getAssociationMapping')->willReturn(['fieldName' => 'tag', 'mappedBy' => 'post']); - $tag = new Tag(); + $tagAdmin = new TagAdmin('admin.tag', Tag::class, 'MyBundle\MyController'); + // NEXT_MAJOR: Remove next three lines related to model manager. $modelManager = $this->createStub(ModelManagerInterface::class); - $modelManager->method('getModelInstance')->willReturn($tag); - - $tagAdmin = new TagAdmin('admin.tag', Tag::class, 'MyBundle\MyController'); + $modelManager->method('getModelInstance')->willReturn(new Tag()); $tagAdmin->setModelManager($modelManager); + $tagAdmin->setParentFieldDescription($parentField); $request = $this->createStub(Request::class); diff --git a/tests/App/Admin/FooAdmin.php b/tests/App/Admin/FooAdmin.php index 3082d3ef914..d771ab027dc 100644 --- a/tests/App/Admin/FooAdmin.php +++ b/tests/App/Admin/FooAdmin.php @@ -25,7 +25,7 @@ class FooAdmin extends AbstractAdmin { - public function getNewInstance() + public function createNewInstance(): object { return new Foo('test_id', 'foo_name'); } diff --git a/tests/App/Admin/TranslatedAdmin.php b/tests/App/Admin/TranslatedAdmin.php index 6ab3f033c5e..ac7275cbca3 100644 --- a/tests/App/Admin/TranslatedAdmin.php +++ b/tests/App/Admin/TranslatedAdmin.php @@ -18,10 +18,16 @@ use Sonata\AdminBundle\Datagrid\ListMapper; use Sonata\AdminBundle\Form\FormMapper; use Sonata\AdminBundle\Show\ShowMapper; +use Sonata\AdminBundle\Tests\App\Model\Translated; use Symfony\Component\Form\Extension\Core\Type\TextType; final class TranslatedAdmin extends AbstractAdmin { + public function createNewInstance(): object + { + return new Translated(); + } + protected function configureListFields(ListMapper $list): void { $list->add('name_list', FieldDescriptionInterface::TYPE_STRING); diff --git a/tests/App/Model/ModelManager.php b/tests/App/Model/ModelManager.php index 410b47903a4..1014eaedb1d 100644 --- a/tests/App/Model/ModelManager.php +++ b/tests/App/Model/ModelManager.php @@ -110,6 +110,9 @@ public function getUrlSafeIdentifier($model) return $model->getId(); } + /** + * NEXT_MAJOR: Remove this method. + */ public function getModelInstance($class) { switch ($class) {