Skip to content

Commit

Permalink
feature #24530 [Form] simplify the form type extension registration (…
Browse files Browse the repository at this point in the history
…xabbuh)

This PR was merged into the 4.2-dev branch.

Discussion
----------

[Form] simplify the form type extension registration

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #22833, #27906
| License       | MIT
| Doc PR        |

Commits
-------

6a1d4c5 simplify the form type extension registration
  • Loading branch information
fabpot committed Oct 10, 2018
2 parents c10d2c0 + 6a1d4c5 commit 52b7239
Show file tree
Hide file tree
Showing 32 changed files with 474 additions and 82 deletions.
31 changes: 30 additions & 1 deletion UPGRADE-4.2.md
Expand Up @@ -55,9 +55,38 @@ Finder
Form
----

* The `getExtendedType()` method of the `FormTypeExtensionInterface` is deprecated and will be removed in 5.0. Type
extensions must implement the static `getExtendedTypes()` method instead and return an iterable of extended types.

Before:

```php
class FooTypeExtension extends AbstractTypeExtension
{
public function getExtendedType()
{
return FormType::class;
}

// ...
}
```

After:

```php
class FooTypeExtension extends AbstractTypeExtension
{
public static function getExtendedTypes(): iterable
{
return array(FormType::class);
}

// ...
}
```
* The `scale` option of the `IntegerType` is deprecated.
* The `$scale` argument of the `IntegerToLocalizedStringTransformer` is deprecated.

* Deprecated calling `FormRenderer::searchAndRenderBlock` for fields which were already rendered.
Instead of expecting such calls to return empty strings, check if the field has already been rendered.

Expand Down
30 changes: 30 additions & 0 deletions UPGRADE-5.0.md
Expand Up @@ -72,6 +72,36 @@ Finder
Form
----

* The `getExtendedType()` method was removed from the `FormTypeExtensionInterface`. It is replaced by the the static
`getExtendedTypes()` method which must return an iterable of extended types.

Before:

```php
class FooTypeExtension extends AbstractTypeExtension
{
public function getExtendedType()
{
return FormType::class;
}

// ...
}
```

After:

```php
class FooTypeExtension extends AbstractTypeExtension
{
public static function getExtendedTypes(): iterable
{
return array(FormType::class);
}

// ...
}
```
* The `scale` option was removed from the `IntegerType`.
* The `$scale` argument of the `IntegerToLocalizedStringTransformer` was removed.

Expand Down
Expand Up @@ -52,6 +52,7 @@
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Form\FormTypeExtensionInterface;
use Symfony\Component\Form\FormTypeGuesserInterface;
use Symfony\Component\Form\FormTypeInterface;
use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface;
Expand Down Expand Up @@ -325,6 +326,8 @@ public function load(array $configs, ContainerBuilder $container)
->addTag('form.type');
$container->registerForAutoconfiguration(FormTypeGuesserInterface::class)
->addTag('form.type_guesser');
$container->registerForAutoconfiguration(FormTypeExtensionInterface::class)
->addTag('form.type_extension');
$container->registerForAutoconfiguration(CacheClearerInterface::class)
->addTag('kernel.cache_clearer');
$container->registerForAutoconfiguration(CacheWarmerInterface::class)
Expand Down
6 changes: 3 additions & 3 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml
Expand Up @@ -74,7 +74,7 @@
<!-- FormTypeHttpFoundationExtension -->
<service id="form.type_extension.form.http_foundation" class="Symfony\Component\Form\Extension\HttpFoundation\Type\FormTypeHttpFoundationExtension">
<argument type="service" id="form.type_extension.form.request_handler" />
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\FormType" />
<tag name="form.type_extension" />
</service>

<!-- HttpFoundationRequestHandler -->
Expand All @@ -92,13 +92,13 @@
<argument type="service" id="validator" />
</service>
<service id="form.type_extension.repeated.validator" class="Symfony\Component\Form\Extension\Validator\Type\RepeatedTypeValidatorExtension">
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\RepeatedType" />
<tag name="form.type_extension" />
</service>
<service id="form.type_extension.submit.validator" class="Symfony\Component\Form\Extension\Validator\Type\SubmitTypeValidatorExtension">
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\SubmitType" />
</service>
<service id="form.type_extension.upload.validator" class="Symfony\Component\Form\Extension\Validator\Type\UploadValidatorExtension">
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\FormType" />
<tag name="form.type_extension" />
<argument type="service" id="translator"/>
<argument type="string">%validator.translation_domain%</argument>
</service>
Expand Down
Expand Up @@ -8,7 +8,7 @@
<defaults public="false" />

<service id="form.type_extension.csrf" class="Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension">
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\FormType" />
<tag name="form.type_extension" />
<argument type="service" id="security.csrf.token_manager" />
<argument>%form.type_extension.csrf.enabled%</argument>
<argument>%form.type_extension.csrf.field_name%</argument>
Expand Down
Expand Up @@ -16,7 +16,7 @@

<!-- DataCollectorTypeExtension -->
<service id="form.type_extension.form.data_collector" class="Symfony\Component\Form\Extension\DataCollector\Type\DataCollectorTypeExtension">
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\FormType" />
<tag name="form.type_extension" />
<argument type="service" id="data_collector.form" />
</service>

Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Bundle/FrameworkBundle/composer.json
Expand Up @@ -39,7 +39,7 @@
"symfony/dom-crawler": "~3.4|~4.0",
"symfony/polyfill-intl-icu": "~1.0",
"symfony/security": "~3.4|~4.0",
"symfony/form": "^4.1",
"symfony/form": "^4.2",
"symfony/expression-language": "~3.4|~4.0",
"symfony/messenger": "^4.2",
"symfony/process": "~3.4|~4.0",
Expand All @@ -66,7 +66,7 @@
"phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0",
"symfony/asset": "<3.4",
"symfony/console": "<3.4",
"symfony/form": "<4.1",
"symfony/form": "<4.2",
"symfony/messenger": "<4.2",
"symfony/property-info": "<3.4",
"symfony/serializer": "<4.1",
Expand Down
16 changes: 14 additions & 2 deletions src/Symfony/Component/Form/AbstractExtension.php
Expand Up @@ -175,9 +175,21 @@ private function initTypeExtensions()
throw new UnexpectedTypeException($extension, 'Symfony\Component\Form\FormTypeExtensionInterface');
}

$type = $extension->getExtendedType();
if (method_exists($extension, 'getExtendedTypes')) {
$extendedTypes = array();

$this->typeExtensions[$type][] = $extension;
foreach ($extension::getExtendedTypes() as $extendedType) {
$extendedTypes[] = $extendedType;
}
} else {
@trigger_error(sprintf('Not implementing the static getExtendedTypes() method in %s when implementing the %s is deprecated since Symfony 4.2. The method will be added to the interface in 5.0.', \get_class($extension), FormTypeExtensionInterface::class), E_USER_DEPRECATED);

$extendedTypes = array($extension->getExtendedType());
}

foreach ($extendedTypes as $extendedType) {
$this->typeExtensions[$extendedType][] = $extension;
}
}
}

Expand Down
19 changes: 19 additions & 0 deletions src/Symfony/Component/Form/AbstractTypeExtension.php
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Component\Form;

use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
Expand Down Expand Up @@ -45,4 +46,22 @@ public function finishView(FormView $view, FormInterface $form, array $options)
public function configureOptions(OptionsResolver $resolver)
{
}

/**
* {@inheritdoc}
*
* @deprecated since Symfony 4.2, use getExtendedTypes() instead.
*/
public function getExtendedType()
{
if (!method_exists($this, 'getExtendedTypes')) {
throw new LogicException(sprintf('You need to implement the static getExtendedTypes() method when implementing the %s in %s.', FormTypeExtensionInterface::class, static::class));
}

@trigger_error(sprintf('The %s::getExtendedType() method is deprecated since Symfony 4.2 and will be removed in 5.0. Use getExtendedTypes() instead.', \get_class($this)), E_USER_DEPRECATED);

foreach (static::getExtendedTypes() as $extendedType) {
return $extendedType;
}
}
}
30 changes: 30 additions & 0 deletions src/Symfony/Component/Form/CHANGELOG.md
Expand Up @@ -4,6 +4,36 @@ CHANGELOG
4.2.0
-----

* The `getExtendedType()` method of the `FormTypeExtensionInterface` is deprecated and will be removed in 5.0. Type
extensions must implement the static `getExtendedTypes()` method instead and return an iterable of extended types.

Before:

```php
class FooTypeExtension extends AbstractTypeExtension
{
public function getExtendedType()
{
return FormType::class;
}

// ...
}
```

After:

```php
class FooTypeExtension extends AbstractTypeExtension
{
public static function getExtendedTypes(): iterable
{
return array(FormType::class);
}

// ...
}
```
* deprecated the `$scale` argument of the `IntegerToLocalizedStringTransformer`
* added `Symfony\Component\Form\ClearableErrorsInterface`
* deprecated calling `FormRenderer::searchAndRenderBlock` for fields which were already rendered
Expand Down
25 changes: 20 additions & 5 deletions src/Symfony/Component/Form/DependencyInjection/FormPass.php
Expand Up @@ -18,6 +18,7 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Form\FormTypeExtensionInterface;

/**
* Adds all services with the tags "form.type", "form.type_extension" and
Expand Down Expand Up @@ -92,13 +93,27 @@ private function processFormTypeExtensions(ContainerBuilder $container)

$tag = $serviceDefinition->getTag($this->formTypeExtensionTag);
if (isset($tag[0]['extended_type'])) {
$extendedType = $tag[0]['extended_type'];
if (!method_exists($serviceDefinition->getClass(), 'getExtendedTypes')) {
@trigger_error(sprintf('Not implementing the static getExtendedTypes() method in %s when implementing the %s is deprecated since Symfony 4.2. The method will be added to the interface in 5.0.', $serviceDefinition->getClass(), FormTypeExtensionInterface::class), E_USER_DEPRECATED);
}

$typeExtensions[$tag[0]['extended_type']][] = new Reference($serviceId);
$typeExtensionsClasses[] = $serviceDefinition->getClass();
} elseif (method_exists($serviceDefinition->getClass(), 'getExtendedTypes')) {
$extendsTypes = false;

foreach ($serviceDefinition->getClass()::getExtendedTypes() as $extendedType) {
$typeExtensions[$extendedType][] = new Reference($serviceId);
$typeExtensionsClasses[] = $serviceDefinition->getClass();
$extendsTypes = true;
}

if (!$extendsTypes) {
throw new InvalidArgumentException(sprintf('The getExtendedTypes() method for service "%s" does not return any extended types.', $serviceId));
}
} else {
throw new InvalidArgumentException(sprintf('"%s" tagged services must have the extended type configured using the extended_type/extended-type attribute, none was configured for the "%s" service.', $this->formTypeExtensionTag, $serviceId));
throw new InvalidArgumentException(sprintf('"%s" tagged services have to implement the static getExtendedTypes() method. The class for service "%s" does not implement it.', $this->formTypeExtensionTag, $serviceId));
}

$typeExtensions[$extendedType][] = new Reference($serviceId);
$typeExtensionsClasses[] = $serviceDefinition->getClass();
}

foreach ($typeExtensions as $extendedType => $extensions) {
Expand Down
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Extension\Csrf\Type;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
Expand Down Expand Up @@ -114,8 +115,8 @@ public function configureOptions(OptionsResolver $resolver)
/**
* {@inheritdoc}
*/
public function getExtendedType()
public static function getExtendedTypes(): iterable
{
return 'Symfony\Component\Form\Extension\Core\Type\FormType';
return array(FormType::class);
}
}
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Extension\DataCollector\Type;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\DataCollector\EventListener\DataCollectorListener;
use Symfony\Component\Form\Extension\DataCollector\FormDataCollectorInterface;
use Symfony\Component\Form\FormBuilderInterface;
Expand Down Expand Up @@ -45,8 +46,8 @@ public function buildForm(FormBuilderInterface $builder, array $options)
/**
* {@inheritdoc}
*/
public function getExtendedType()
public static function getExtendedTypes(): iterable
{
return 'Symfony\Component\Form\Extension\Core\Type\FormType';
return array(FormType::class);
}
}
Expand Up @@ -58,9 +58,19 @@ public function getTypeExtensions($name)
foreach ($this->typeExtensionServices[$name] as $serviceId => $extension) {
$extensions[] = $extension;

// validate result of getExtendedType() to ensure it is consistent with the service definition
if ($extension->getExtendedType() !== $name) {
throw new InvalidArgumentException(sprintf('The extended type specified for the service "%s" does not match the actual extended type. Expected "%s", given "%s".', $serviceId, $name, $extension->getExtendedType()));
if (method_exists($extension, 'getExtendedTypes')) {
$extendedTypes = array();

foreach ($extension::getExtendedTypes() as $extendedType) {
$extendedTypes[] = $extendedType;
}
} else {
$extendedTypes = array($extension->getExtendedType());
}

// validate the result of getExtendedTypes()/getExtendedType() to ensure it is consistent with the service definition
if (!\in_array($name, $extendedTypes, true)) {
throw new InvalidArgumentException(sprintf('The extended type specified for the service "%s" does not match the actual extended type. Expected "%s", given "%s".', $serviceId, $name, implode(', ', $extendedTypes)));
}
}
}
Expand Down
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Extension\HttpFoundation\Type;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\RequestHandlerInterface;
Expand Down Expand Up @@ -39,8 +40,8 @@ public function buildForm(FormBuilderInterface $builder, array $options)
/**
* {@inheritdoc}
*/
public function getExtendedType()
public static function getExtendedTypes(): iterable
{
return 'Symfony\Component\Form\Extension\Core\Type\FormType';
return array(FormType::class);
}
}
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Component\Form\Extension\Validator\Type;

use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener;
use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper;
use Symfony\Component\Form\FormBuilderInterface;
Expand Down Expand Up @@ -67,8 +68,8 @@ public function configureOptions(OptionsResolver $resolver)
/**
* {@inheritdoc}
*/
public function getExtendedType()
public static function getExtendedTypes(): iterable
{
return 'Symfony\Component\Form\Extension\Core\Type\FormType';
return array(FormType::class);
}
}

0 comments on commit 52b7239

Please sign in to comment.