From bea33c9647983e9ffcf720e8c5f9f9eb69283054 Mon Sep 17 00:00:00 2001 From: fran Date: Tue, 21 Jul 2020 17:44:00 +0200 Subject: [PATCH] Deprecate help option in FieldDescription --- .phpstan/phpstan-baseline.neon | 6 ++ UPGRADE-3.x.md | 21 ++++ docs/cookbook/recipe_image_previews.rst | 24 +++-- docs/reference/form_help_message.rst | 101 +----------------- src/Admin/BaseFieldDescription.php | 28 ++++- src/Admin/FieldDescriptionInterface.php | 7 -- src/Form/FormMapper.php | 40 ++++++- .../edit_one_to_many_inline_table.html.twig | 6 ++ .../CRUD/base_standard_edit_field.html.twig | 6 ++ .../views/Form/form_admin_fields.html.twig | 6 +- tests/Admin/BaseFieldDescriptionTest.php | 22 +++- tests/App/AppKernel.php | 1 + .../Controller/CRUDControllerTest.php | 4 + 13 files changed, 146 insertions(+), 126 deletions(-) diff --git a/.phpstan/phpstan-baseline.neon b/.phpstan/phpstan-baseline.neon index e8b61ae9adc..e74f96444b6 100644 --- a/.phpstan/phpstan-baseline.neon +++ b/.phpstan/phpstan-baseline.neon @@ -90,6 +90,12 @@ parameters: count: 1 path: ../src/Translator/Extractor/JMSTranslatorBundle/AdminExtractor.php + - + # NEXT_MAJOR: Remove this error + message: "#^Call to an undefined method Sonata\\\\AdminBundle\\\\Admin\\\\FieldDescriptionInterface\\:\\:setHelp\\(\\)\\.$#" + count: 1 + path: ../src/Form/FormMapper.php + # next 6 errors are due to not installed Doctrine ORM\ODM - message: "#^Class Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection not found\\.$#" diff --git a/UPGRADE-3.x.md b/UPGRADE-3.x.md index cd6eca2f9c2..696ffe2880e 100644 --- a/UPGRADE-3.x.md +++ b/UPGRADE-3.x.md @@ -1,6 +1,27 @@ UPGRADE 3.x =========== +## Deprecated `help` option in field description + +You MUST use Symfony's [`help`](https://symfony.com/doc/4.4/reference/forms/types/form.html#help) option instead. + +Before: +```php +$formMapper + ->add('field', null, [ + 'help' => 'Help text Please!', + ]); +``` + +After: +```php +$formMapper + ->add('field', null, [ + 'help' => 'Help text Please!', + 'help_html' => true, + ]); +``` + UPGRADE FROM 3.71 to 3.72 ========================= diff --git a/docs/cookbook/recipe_image_previews.rst b/docs/cookbook/recipe_image_previews.rst index 423e594a976..708873b5462 100644 --- a/docs/cookbook/recipe_image_previews.rst +++ b/docs/cookbook/recipe_image_previews.rst @@ -22,14 +22,14 @@ Pre-requisites The recipe ---------- -SonataAdmin lets us put raw HTML into the 'help' option for any given form field. +You can use Symfony 'help' and 'help_html' options to put raw HTML into any given form field. We are going to use this functionality to embed an image tag when an image exists. To do this we need to: - get access to the ``Image`` instance from within ``ImageAdmin`` - create an image tag based on the Image's URL -- add a 'help' option to a field on the Image form to display the image tag +- add a 'help' and 'help_html' option to a field on the Image form to display the image tag For the sake of this example we will use some basic CSS to restrict the size of the preview image (we are not going to generate and save special thumbnails). @@ -48,19 +48,20 @@ we are manipulating form fields we do this from within ``ImageAdmin::configureFo // get the current Image instance $image = $this->getSubject(); - // use $fileFieldOptions so we can add other options to the field - $fileFieldOptions = ['required' => false]; + // use $fileFormOptions so we can add other options to the field + $fileFormOptions = ['required' => false]; if ($image && ($webPath = $image->getWebPath())) { // get the container so the full path to the image can be set $container = $this->getConfigurationPool()->getContainer(); $fullPath = $container->get('request_stack')->getCurrentRequest()->getBasePath().'/'.$webPath; // add a 'help' option containing the preview's img tag - $fileFieldOptions['help'] = ''; + $fileFormOptions['help'] = ''; + $fileFormOptions['help_html'] = true; } $formMapper - ->add('file', 'file', $fileFieldOptions) + ->add('file', 'file', $fileFormOptions) ; } } @@ -77,7 +78,7 @@ We then use CSS to restrict the max size of the image: And that is all there is to it! However, this method does not work when the ``ImageAdmin`` can be embedded in other -Admins using the ``sonata_type_admin`` field type. For that we need... +Admins using the ``Sonata\\AdminBundle\\Form\\Type\\AdminType`` field type. For that we need... Advanced example - works with embedded Admins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -105,15 +106,16 @@ Admin class is embedded and use a different method:: $image = $this->getSubject(); } - // use $fileFieldOptions so we can add other options to the field - $fileFieldOptions = ['required' => false]; + // use $fileFormOptions so we can add other options to the field + $fileFormOptions = ['required' => false]; if ($image && ($webPath = $image->getWebPath())) { // add a 'help' option containing the preview's img tag - $fileFieldOptions['help'] = ''; + $fileFormOptions['help'] = ''; + $fileFormOptions['help_html'] = true; } $formMapper - ->add('file', 'file', $fileFieldOptions) + ->add('file', 'file', $fileFormOptions) ; } } diff --git a/docs/reference/form_help_message.rst b/docs/reference/form_help_message.rst index 04127925377..e72cd514bc3 100644 --- a/docs/reference/form_help_message.rst +++ b/docs/reference/form_help_message.rst @@ -4,10 +4,9 @@ Form Help Messages and Descriptions Help Messages ------------- -Help messages are short notes that are rendered together with form fields. +You can use `Symfony 'help' option`_ to add help messages that are rendered together with form fields. They are generally used to show additional information so the user can complete -the form element faster and more accurately. The text is not escaped, -so HTML can be used. +the form element faster and more accurately. Example ^^^^^^^ @@ -37,100 +36,6 @@ Example :align: center :alt: Example of the two form fields with help messages. -Alternative Ways To Define Help Messages -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -All at once:: - - // src/Admin/PostAdmin.php - - final class PostAdmin extends AbstractAdmin - { - protected function configureFormFields(FormMapper $formMapper) - { - $formMapper - ->with('General') - ->add('title') - ->add('keywords') - ->setHelps([ - 'title' => 'Set the title of a web page', - 'keywords' => 'Set the keywords of a web page', - ]) - ->end() - ; - } - } - -or step by step:: - - // src/Admin/PostAdmin.php - - final class PostAdmin extends AbstractAdmin - { - protected function configureFormFields(FormMapper $formMapper) - { - $formMapper - ->with('General') - ->add('title') - ->add('keywords') - ->setHelp('title', 'Set the title of a web page') - ->setHelp('keywords', 'Set the keywords of a web page') - ->end() - ; - } - } - -This can be very useful if you want to apply general help messages via an ``AdminExtension``. -This Extension for example adds a note field to some entities which use a custom trait:: - - namespace App\Admin\Extension; - - use Sonata\AdminBundle\Admin\AbstractAdminExtension; - use Sonata\AdminBundle\Datagrid\DatagridMapper; - use Sonata\AdminBundle\Form\FormMapper; - use Sonata\AdminBundle\Show\ShowMapper; - - final class NoteAdminExtension extends AbstractAdminExtension - { - // add this field to the datagrid every time its available - /** - * @param DatagridMapper $datagridMapper - */ - protected function configureDatagridFilters(DatagridMapper $datagridMapper) - { - $datagridMapper - ->add('note') - ; - } - - // here we don't add the field, because we would like to define - // the place manually in the admin. But if the filed is available, - // we want to add the following help message to the field. - /** - * @param FormMapper $formMapper - */ - protected function configureFormFields(FormMapper $formMapper) - { - $formMapper - ->addHelp('note', 'Use this field for an internal note.') - ; - } - - // if the field exists, add it in a special tab on the show view. - /** - * @param ShowMapper $showMapper - */ - protected function configureShowFields(ShowMapper $showMapper) - { - $showMapper - ->with('Internal') - ->add('note') - ->end() - ; - } - } - - Advanced usage ^^^^^^^^^^^^^^ @@ -170,3 +75,5 @@ Example ; } } + +.. _`Symfony 'help' option`: https://symfony.com/doc/4.4/reference/forms/types/form.html#help diff --git a/src/Admin/BaseFieldDescription.php b/src/Admin/BaseFieldDescription.php index 77ec3f72355..bfe9bf48a91 100644 --- a/src/Admin/BaseFieldDescription.php +++ b/src/Admin/BaseFieldDescription.php @@ -180,9 +180,17 @@ public function setOptions(array $options) unset($options['template']); } + // NEXT_MAJOR: Remove this block. // set help if provided if (isset($options['help'])) { - $this->setHelp($options['help']); + @trigger_error(sprintf( + 'Passing "help" option to "%s()" is deprecated since sonata-project/admin-bundle 3.x' + .' and the option will be removed in 4.0.' + .' Use Symfony Form "help" option instead.', + __METHOD__ + ), E_USER_DEPRECATED); + + $this->setHelp($options['help'], 'sonata_deprecation_mute'); unset($options['help']); } @@ -448,15 +456,33 @@ public static function camelize($property) /** * Defines the help message. * + * NEXT_MAJOR: Remove this method. + * + * @deprecated since sonata-project/admin-bundle 3.x and will be removed in version 4.0. Use Symfony Form "help" option instead. + * * @param string $help */ public function setHelp($help) { + if ('sonata_deprecation_mute' !== (\func_get_args()[1] ?? null)) { + @trigger_error(sprintf( + 'The "%s()" method is deprecated since sonata-project/admin-bundle 3.x and will be removed in version 4.0.' + .' Use Symfony Form "help" option instead.', + __METHOD__ + ), E_USER_DEPRECATED); + } + $this->help = $help; } public function getHelp() { + @trigger_error(sprintf( + 'The "%s()" method is deprecated since sonata-project/admin-bundle 3.x and will be removed in version 4.0.' + .' Use Symfony Form "help" option instead.', + __METHOD__ + ), E_USER_DEPRECATED); + return $this->help; } diff --git a/src/Admin/FieldDescriptionInterface.php b/src/Admin/FieldDescriptionInterface.php index d9424344b57..dd9f9a72325 100644 --- a/src/Admin/FieldDescriptionInterface.php +++ b/src/Admin/FieldDescriptionInterface.php @@ -20,7 +20,6 @@ * @method bool hasAdmin() * @method bool hasParent() * @method bool hasAssociationAdmin() - * @method void setHelp(string $help) */ interface FieldDescriptionInterface { @@ -300,10 +299,4 @@ public function getSortParentAssociationMapping(); * @return mixed */ public function getFieldValue($object, $fieldName); - -// NEXT_MAJOR: uncomment this method in 4.0 -// /** -// * Defines the help message. -// */ -// public function setHelp(string $help): void; } diff --git a/src/Form/FormMapper.php b/src/Form/FormMapper.php index 49d3b32be87..e7d8ed675b7 100644 --- a/src/Form/FormMapper.php +++ b/src/Form/FormMapper.php @@ -136,9 +136,19 @@ public function add($name, $type = null, array $options = [], array $fieldDescri $options['label'] = $this->admin->getLabelTranslatorStrategy()->getLabel($name, 'form', 'label'); } + // NEXT_MAJOR: Remove this block. if (isset($options['help'])) { - $fieldDescription->setHelp($options['help']); - unset($options['help']); + $containsHtml = $options['help'] !== strip_tags($options['help']); + + if (!isset($options['help_html']) && $containsHtml) { + @trigger_error( + 'Using HTML syntax within the "help" option and not setting the "help_html" option to "true" is deprecated' + .' since sonata-project/admin-bundle 3.x and it will not work in version 4.0.', + E_USER_DEPRECATED + ); + + $options['help_html'] = true; + } } } @@ -241,24 +251,46 @@ public function create($name, $type = null, array $options = []) } /** + * NEXT_MAJOR: Remove this method. + * + * @deprecated since sonata-project/admin-bundle 3.x and will be removed in version 4.0. Use Symfony Form "help" option instead. + * * @return FormMapper */ public function setHelps(array $helps = []) { + @trigger_error(sprintf( + 'The "%s()" method is deprecated since sonata-project/admin-bundle 3.x and will be removed in version 4.0.' + .' Use Symfony Form "help" option instead.', + __METHOD__ + ), E_USER_DEPRECATED); + foreach ($helps as $name => $help) { - $this->addHelp($name, $help); + $this->addHelp($name, $help, 'sonata_deprecation_mute'); } return $this; } /** + * NEXT_MAJOR: Remove this method. + * + * @deprecated since sonata-project/admin-bundle 3.x and will be removed in version 4.0. Use Symfony Form "help" option instead. + * * @return FormMapper */ public function addHelp($name, $help) { + if ('sonata_deprecation_mute' !== (\func_get_args()[2] ?? null)) { + @trigger_error(sprintf( + 'The "%s()" method is deprecated since sonata-project/admin-bundle 3.x and will be removed in version 4.0.' + .' Use Symfony Form "help" option instead.', + __METHOD__ + ), E_USER_DEPRECATED); + } + if ($this->admin->hasFormFieldDescription($name)) { - $this->admin->getFormFieldDescription($name)->setHelp($help); + $this->admin->getFormFieldDescription($name)->setHelp($help, 'sonata_deprecation_mute'); } return $this; diff --git a/src/Resources/views/CRUD/Association/edit_one_to_many_inline_table.html.twig b/src/Resources/views/CRUD/Association/edit_one_to_many_inline_table.html.twig index 64f5806fbab..931a3d7ba90 100644 --- a/src/Resources/views/CRUD/Association/edit_one_to_many_inline_table.html.twig +++ b/src/Resources/views/CRUD/Association/edit_one_to_many_inline_table.html.twig @@ -48,9 +48,13 @@ file that was distributed with this source code. {% if sonata_admin.field_description.associationadmin.formfielddescriptions[field_name] is defined %} {{ form_widget(nested_field) }} + {# NEXT_MAJOR: Remove this block #} {% if sonata_admin.field_description.associationadmin.formfielddescriptions[field_name].help %} {%- block help %} + {% if nested_field.help is empty %} + {% deprecated 'The "help" option is deprecated in field description since sonata-project/admin-bundle 3.x, to be removed in 4.0. Use Symfony Form "help" option instead.' %} + {% endif %} {{- sonata_admin.field_description.associationadmin.formfielddescriptions[field_name].help|trans( {}, sonata_admin.field_description.associationadmin.formfielddescriptions[field_name].translationDomain @@ -59,6 +63,8 @@ file that was distributed with this source code. {% endif %} + {{ form_help(nested_field) }} + {% set dummy = nested_group_field.setrendered %} {% else %} {% if field_name == '_delete' %} diff --git a/src/Resources/views/CRUD/base_standard_edit_field.html.twig b/src/Resources/views/CRUD/base_standard_edit_field.html.twig index bf2b7f0769d..a1ba3e8c088 100644 --- a/src/Resources/views/CRUD/base_standard_edit_field.html.twig +++ b/src/Resources/views/CRUD/base_standard_edit_field.html.twig @@ -22,10 +22,16 @@ file that was distributed with this source code. {% block field %}{{ form_widget(field_element) }}{% endblock %} + {# NEXT_MAJOR: Remove this block #} {% if field_description.help %} + {% if field_element.help is empty %} + {% deprecated 'The "help" option is deprecated in field description since sonata-project/admin-bundle 3.x, to be removed in 4.0. Use Symfony Form "help" option instead.' %} + {% endif %} {% block help %}{{ field_description.help|raw }}{% endblock %} {% endif %} + {{ form_help(field_element) }} +
{% block errors %}{{ form_errors(field_element) }}{% endblock %}
diff --git a/src/Resources/views/Form/form_admin_fields.html.twig b/src/Resources/views/Form/form_admin_fields.html.twig index c09d6b0802c..f5d53fa64ad 100644 --- a/src/Resources/views/Form/form_admin_fields.html.twig +++ b/src/Resources/views/Form/form_admin_fields.html.twig @@ -35,7 +35,7 @@ file that was distributed with this source code. {% block form_help %} {% apply spaceless %} - {{ parent() }} + {{ parent() }} {% endapply %} {% endblock %} @@ -400,7 +400,11 @@ file that was distributed with this source code. {% endif %} + {# NEXT_MAJOR: Remove this block #} {% if sonata_admin is defined and sonata_admin_enabled and sonata_admin.field_description.help|default(false) %} + {% if form.help is empty %} + {% deprecated 'The "help" option is deprecated in field description since sonata-project/admin-bundle 3.x, to be removed in 4.0. Use Symfony Form "help" option instead.' %} + {% endif %} {{ sonata_admin.field_description.help|trans(help_translation_parameters, sonata_admin.field_description.translationDomain ?: admin.translationDomain)|raw }} {% endif %} diff --git a/tests/Admin/BaseFieldDescriptionTest.php b/tests/Admin/BaseFieldDescriptionTest.php index daa40f00960..f9a824ebad4 100644 --- a/tests/Admin/BaseFieldDescriptionTest.php +++ b/tests/Admin/BaseFieldDescriptionTest.php @@ -56,17 +56,13 @@ public function testOptions(): void $description->setOption('label', 'trucmuche'); $this->assertSame('trucmuche', $description->getLabel()); $this->assertNull($description->getTemplate()); - $description->setOptions(['type' => 'integer', 'template' => 'foo.twig.html', 'help' => 'fooHelp']); + $description->setOptions(['type' => 'integer', 'template' => 'foo.twig.html']); $this->assertSame('integer', $description->getType()); $this->assertSame('foo.twig.html', $description->getTemplate()); - $this->assertSame('fooHelp', $description->getHelp()); $this->assertCount(2, $description->getOptions()); - $description->setHelp('Please enter an integer'); - $this->assertSame('Please enter an integer', $description->getHelp()); - $description->setMappingType('int'); $this->assertSame('int', $description->getMappingType()); @@ -81,6 +77,22 @@ public function testOptions(): void $this->assertTrue($description->isSortable()); } + /** + * NEXT_MAJOR: Remove this test. + * + * @group legacy + */ + public function testHelpOptions(): void + { + $description = new FieldDescription(); + + $description->setHelp('Please enter an integer'); + $this->assertSame('Please enter an integer', $description->getHelp()); + + $description->setOptions(['help' => 'fooHelp']); + $this->assertSame('fooHelp', $description->getHelp()); + } + public function testAdmin(): void { $description = new FieldDescription(); diff --git a/tests/App/AppKernel.php b/tests/App/AppKernel.php index 50fb4ddadbb..05356bcbd9e 100644 --- a/tests/App/AppKernel.php +++ b/tests/App/AppKernel.php @@ -98,6 +98,7 @@ protected function configureContainer(ContainerBuilder $containerBuilder, Loader $containerBuilder->loadFromExtension('twig', [ 'strict_variables' => '%kernel.debug%', 'exception_controller' => null, + 'form_themes' => ['@SonataAdmin/Form/form_admin_fields.html.twig'], ]); $loader->load(sprintf('%s/config/services.yml', $this->getProjectDir())); diff --git a/tests/Functional/Controller/CRUDControllerTest.php b/tests/Functional/Controller/CRUDControllerTest.php index f8d7c807867..126a7b417be 100644 --- a/tests/Functional/Controller/CRUDControllerTest.php +++ b/tests/Functional/Controller/CRUDControllerTest.php @@ -50,6 +50,10 @@ public function testCreate(): void 1, $crawler->filter('.sonata-ba-collapsed-fields label:contains("Name")')->count() ); + $this->assertCount( + 1, + $crawler->filter('.sonata-ba-field-help:contains("Help me!")') + ); } public function testEmptyCreate(): void