diff --git a/UPGRADE-3.x.md b/UPGRADE-3.x.md index 0b5db0d3cd2..7fdf3d8444e 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\AdminInterface::createNewInstance()` method instead. + UPGRADE FROM 3.88 to 3.89 ========================= diff --git a/src/Admin/AbstractAdmin.php b/src/Admin/AbstractAdmin.php index c40e55abd5f..858a05365e5 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 + */ + public 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/Admin/AdminInterface.php b/src/Admin/AdminInterface.php index 8b5d82b7ee8..fb777b88a5a 100644 --- a/src/Admin/AdminInterface.php +++ b/src/Admin/AdminInterface.php @@ -51,6 +51,7 @@ * @method string|null getParentAssociationMapping() * @method void reorderFormGroup(string $group, array $keys) * @method void defineFormBuilder(FormBuilderInterface $formBuilder) + * @method object createNewInstance() * * @phpstan-template T of object * @phpstan-extends AccessRegistryInterface @@ -789,6 +790,12 @@ public function getListMode(); // * the getFormBuilder is only call by the main admin class. // */ // public function defineFormBuilder(FormBuilderInterface $formBuilder): void; + +// NEXT_MAJOR: uncomment this method in 4.0 +// /** +// * @phpstan-return T +// */ +// public function createNewInstance(): object; } class_exists(\Sonata\Form\Validator\ErrorElement::class); 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) {