Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce field description factory #6854

Merged

Conversation

franmomu
Copy link
Member

Subject

For background: #6761 and #6701.

First of all, I had this half done, so I've "finished", I'm fine postponing this for 5.0 if we think this shouldn't be done now (and if this is fine ofc).

The idea of this is basically to move responsibility of creating a FieldDescription from ModelManagerInterface to FielDescriptionFactoryInterface.

And also there is a new Sonata\AdminBundle\FieldDescription\TypeGuesserInterface that will guess the FieldDescription type base on a FieldDescriptionInterface properties instead of rely on ModelManagerInterface.

I've created sonata-project/SonataDoctrineMongoDBAdminBundle#531 (unfinished) as example of implementation.

I am targeting this branch, because these changes are BC.

Closes #6761 and #6701.

Changelog

### Added
- Added `FieldDescriptionFactoryInterface` to create FieldDescription instances.
- Added `TypeGuesserInterface` to guess the proper FieldDescription type based on its properties.
### Deprecated
- Deprecated `ModelManagerInterface::getNewFieldDescriptionInstance`.

@franmomu franmomu added the minor label Feb 14, 2021
src/DependencyInjection/Admin/AbstractTaggedAdmin.php Outdated Show resolved Hide resolved
* @method Pool getConfigurationPool()
* @method void setRouteGenerator(RouteGeneratorInterface $routeGenerator)
* @method RouteGeneratorInterface getRouteGenerator()
* @method void initialize()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these method annotations look weird 🧐

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an idea for all these methods...

Can we add a new NextMajorTaggedAdminInterface with all these methods and mark it as @internal.

We can then use @mixin NextMajorTaggedAdminInterface to include all these methods.

tests/App/FieldDescription/FieldDescriptionFactory.php Outdated Show resolved Hide resolved
Copy link
Member

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can rebase 3.x to fix the phpstan failures

src/Admin/AbstractAdmin.php Outdated Show resolved Hide resolved
src/Admin/AbstractAdmin.php Outdated Show resolved Hide resolved
src/Admin/AbstractAdmin.php Outdated Show resolved Hide resolved
src/DependencyInjection/Admin/AbstractTaggedAdmin.php Outdated Show resolved Hide resolved
@VincentLanglet
Copy link
Member

It could be great to have this in 4.0 since it will solved the following issue too if I understand correctly:
sonata-project/SonataDoctrineORMAdminBundle#1296

@franmomu franmomu force-pushed the introduce_field_description_factory branch 4 times, most recently from f52897b to 8f3dfb1 Compare February 16, 2021 00:55
@@ -3077,7 +3074,6 @@ protected function buildList()
]
);

$fieldDescription->setAdmin($this);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have to keep this, no ?

@@ -3103,7 +3098,6 @@ protected function buildList()
]
);

$fieldDescription->setAdmin($this);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

/**
* @phpstan-param class-string $class
*/
public function create(string $class, string $name, array $options = []): FieldDescriptionInterface;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use a more explicite name than $class ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could use modelClass maybe, but I guess this is "sync" with AdminInterface::getClass

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add some type hints for the $options?


interface TypeGuesserInterface
{
public function guess(FieldDescriptionInterface $fieldDescription): TypeGuess;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we create a TypeGuesserChain too ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure, I guess so, I'll take a look.

@franmomu
Copy link
Member Author

It could be great to have this in 4.0 since it will solved the following issue too if I understand correctly:
sonata-project/SonataDoctrineORMAdminBundle#1296

Yes, that is the idea, we could also remove all the fieldDescription->setAdmin calls and remove setting the doctrine mapping type to field description type. I'll try to continue tomorrow.

@franmomu franmomu marked this pull request as draft February 16, 2021 08:37
@franmomu
Copy link
Member Author

I'll try to advance/finish it at the weekend, I have some doubts about how to enforce calling FieldDescriptionInterface::setAdmin() method always once FieldDescriptionInterface is created, because apart from AbstractAdmin, there are calls from the mappers: https://github.com/sonata-project/SonataAdminBundle/search?q=getNewFieldDescriptionInstance.

I thought about creating a FieldDescriptionCreator, something like:

final class FieldDescriptionCreator implements FieldDescriptionCreatorInterface
{
    public function create(AdminInterface $admin, string $propertyName, array $options = []): FieldDescriptionInterface
    {
        $fieldDescriptionFactory = $admin->getFieldDescriptionFactory();

        if (null === $fieldDescriptionFactory) {
            $fieldDescription = $admin->getModelManager()->getNewFieldDescriptionInstance(
                $this->getClass(),
                $propertyName,
                $options
            );
        } else {
            $fieldDescription = $fieldDescriptionFactory->create($admin->getClass(), $propertyName, $options);
        }

        $fieldDescription->setAdmin($admin);

        return $fieldDescription;
    }
}

This would work fine for Mappers, but maybe it's strange to inject this in Admins too. I'll give it some thought and check this current getNewFieldDescriptionInstance calls from AbstractAdmin.

@franmomu franmomu force-pushed the introduce_field_description_factory branch from 8f3dfb1 to 11fa21f Compare February 21, 2021 00:51
Copy link
Member

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is needed to finish this @franmomu ?

@franmomu franmomu force-pushed the introduce_field_description_factory branch from 11fa21f to 75e03fa Compare February 28, 2021 21:53
@franmomu
Copy link
Member Author

What is needed to finish this @franmomu ?

I think now some reviews to see if it's fine and then I'll create both PR for the persistence bundles.

VincentLanglet
VincentLanglet previously approved these changes Feb 28, 2021
Copy link
Member

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after this PR we should move

  • FieldDescriptionCollection.php
  • FieldDescriptionInterface.php
  • FieldDescriptionRegistryInterface.php

To the new directory FieldDescription and deprecate the old classes

@@ -2892,6 +2890,26 @@ final public function hasTemplateRegistry(): bool
return null !== $this->templateRegistry;
}

public function createFieldDescription(string $propertyName, array $options = []): FieldDescriptionInterface
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final?

/**
* @phpstan-param class-string $class
*/
public function create(string $class, string $name, array $options = []): FieldDescriptionInterface;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add some type hints for the $options?

*/
private $guessers = [];

public function __construct(array $guessers)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type hint missing

@core23
Copy link
Member

core23 commented Mar 6, 2021

Can we use the factory to solve this issue too? sonata-project/SonataBlockBundle#244

@VincentLanglet
Copy link
Member

Friendly ping @franmomu ; do you have some time to finish this ? :)

@franmomu
Copy link
Member Author

franmomu commented Mar 7, 2021

Can we use the factory to solve this issue too? sonata-project/SonataBlockBundle#244

hmmmm I guess that this code:

// simulate an association ...
$fieldDescription = $this->getGalleryAdmin()->getModelManager()->getNewFieldDescriptionInstance($this->getGalleryAdmin()->getClass(), 'media', [
   'translation_domain' => 'SonataMediaBundle',
]);
$fieldDescription->setAssociationAdmin($this->getGalleryAdmin());
$fieldDescription->setAdmin($formMapper->getAdmin());
$fieldDescription->setOption('edit', 'list');
$fieldDescription->setAssociationMapping(['fieldName' => 'gallery', 'type' => ClassMetadataInfo::MANY_TO_ONE]);

Will become:

$fieldDescription = $this->getGalleryAdmin()->createFieldDescription('media', [
   'translation_domain' => 'SonataMediaBundle',
    'edit' => 'list',
]);
// Not sure about this one, I think is not necessary
$fieldDescription->setAssociationAdmin($this->getGalleryAdmin());

But to do this: sonata-project/SonataBlockBundle#244 (comment)

I guess we could create something like:

if ($options['sonata_field_description'] instanceof FieldDescriptionInterface) {
$fieldDescription = $options['sonata_field_description'];
$sonataAdmin['admin'] = $fieldDescription->getAdmin();
$sonataAdmin['field_description'] = $fieldDescription;
$sonataAdmin['name'] = $fieldDescription->getName();
$sonataAdmin['edit'] = $fieldDescription->getOption('edit', 'standard');
$sonataAdmin['inline'] = $fieldDescription->getOption('inline', 'natural');
$sonataAdmin['block_name'] = $fieldDescription->getOption('block_name', false);
$sonataAdmin['class'] = $this->getClass($builder);
$builder->setAttribute('sonata_admin_enabled', true);
}

And I guess we could call $options['admin']->createFieldDescription(), the name I supposed that can be fetched from the builder (all of this theoretically).

I'll address the suggestions later today.

This interface will be responsible of creating FieldDescriptionInterface
instances.
This will guess the field description type from its properties.
This method will use the field description factory and also sets
the current admin to the field description.
Deprecated in favor of Sonata\AdminBundle\FieldDescription\TypeGuesserInterface.
@franmomu franmomu force-pushed the introduce_field_description_factory branch from c1db7b6 to e477c50 Compare March 7, 2021 23:12
@@ -2892,6 +2890,26 @@ final public function hasTemplateRegistry(): bool
return null !== $this->templateRegistry;
}

final public function createFieldDescription(string $propertyName, array $options = []): FieldDescriptionInterface
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you provide type hints for the $options?

* @method Pool getConfigurationPool()
* @method void setRouteGenerator(RouteGeneratorInterface $routeGenerator)
* @method RouteGeneratorInterface getRouteGenerator()
* @method void initialize()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an idea for all these methods...

Can we add a new NextMajorTaggedAdminInterface with all these methods and mark it as @internal.

We can then use @mixin NextMajorTaggedAdminInterface to include all these methods.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Create FieldDescriptionFactoryInterface
4 participants