diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index ffe565b..d96ead8 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: - php-versions: ['8,2', '8.3', '8.4'] + php-versions: ['8.2', '8.3', '8.4'] runs-on: ubuntu-latest @@ -37,7 +37,7 @@ jobs: # https://github.com/marketplace/actions/check-php-syntax-errors - name: Check PHP syntax errors - uses: overtrue/phplint@9 + uses: overtrue/phplint@9.7.2 - name: Coding standards run: ./vendor/bin/phpcs diff --git a/docs/README.md b/docs/README.md index 42c7c52..afe62d5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -34,10 +34,12 @@ To notify a user of an error with their form submission: Symfony Form will automatically display an error message next to the input field. Error messages should have a unique ID and be associated with the form input via the `aria-describedby` attribute. This is provided in the accessible form theme. + ## Docs - [Using accessible forms with Laravel](laravel.md) - [Using accessible forms with Symfony](symfony.md) +- [Form options](form-options.md) - Form helpers to create a form - Accessible form theme - GOV.UK Design System form theme diff --git a/docs/form-options.md b/docs/form-options.md new file mode 100644 index 0000000..64e15c2 --- /dev/null +++ b/docs/form-options.md @@ -0,0 +1,26 @@ +# Form options + +## do_not_render + +If you want to add a form field for validating request data, but you don't want to display this in your HTML form, you can use the 'do_not_render' option. +This accepts an array of element names to not render in the form HTML. This can be useful for things like pagination, that don't appear in the main form HTML but appear as links separately on the page. + +Using this example, when the form is rendered to the view template, the page form element is skipped: + +```php +// Form class +class MySearchForm extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('keywords', TextType::class) + ->add('page', IntegerType::class, ['required' => false]) + ; + } +} +// Controller +$form = $this->createForm(MySearchForm::class, null, ['do_not_render' => ['page']]); +``` + +Please note, an alternative way to achieve this is to use the [allow_extra_fields](https://symfony.com/doc/current/reference/forms/types/form.html#allow-extra-fields) option, which allows additional fields to be passed into a form, but you need to take care of validation yourself. diff --git a/phpunit.xml.dist b/phpunit.xml.dist index b651543..9d0cad9 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,18 +1,18 @@ - - - - src - - - - - - - - - tests - - + + + + + + + + tests + + + + + src + + diff --git a/src/FieldTypes/Html.php b/src/FieldTypes/Html.php index e24e6e2..12ee879 100644 --- a/src/FieldTypes/Html.php +++ b/src/FieldTypes/Html.php @@ -1,5 +1,7 @@ setDefaults([ 'attr' => ['novalidate' => 'novalidate'], + 'do_not_render' => [], ]); + + $resolver->setAllowedTypes('do_not_render', 'array'); + } + + /** + * @return void + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + // Remove elements we do not want to render in HTML form + if (!empty($options['do_not_render'])) { + foreach ($options['do_not_render'] as $element) { + unset($view->children[$element]); + } + } + + parent::finishView($view, $form, $options); } } diff --git a/src/Twig/AccessibleFormsExtension.php b/src/Twig/AccessibleFormsExtension.php index f4aafbf..c5566c3 100644 --- a/src/Twig/AccessibleFormsExtension.php +++ b/src/Twig/AccessibleFormsExtension.php @@ -1,4 +1,5 @@ children as $child) { if (isset($child->vars['errors']) && count($child->vars['errors']) > 0) { - $id = $child->vars['id']; if (!empty($child->vars['choices']) && !empty($child->vars['expanded']) && $child->vars['expanded'] === true) { @@ -114,5 +114,4 @@ public function prefixOnErrors(string $string, FormView $form, string $prefix = } return $string; } - } diff --git a/tests/DoNotRenderTest.php b/tests/DoNotRenderTest.php new file mode 100644 index 0000000..8716e6d --- /dev/null +++ b/tests/DoNotRenderTest.php @@ -0,0 +1,55 @@ +add('keywords', TextType::class) + ->add('filters', ChoiceType::class, [ + 'choices' => ['Yellow' => 'Yellow', 'Orange' => 'Orange', 'White' => 'White'], + 'expanded' => true, + 'multiple' => true, + ]) + ->add('page', IntegerType::class, ['required' => false]) + ; + } +} + +/** + * Checks that fields listed in the `do_not_render` option are excluded from the + * actual rendered HTML output, not just removed from the FormView tree. + * + * @see https://docs.phpunit.de/en/12.4/writing-tests-for-phpunit.html + */ +class DoNotRenderTest extends TypeTestCase +{ + use TwigTrait; + + public function testDoNotRenderExcludesFieldFromHtml() + { + $options = [ + 'do_not_render' => ['page'] + ]; + $form = $this->factory->create(DoNotRenderTestForm::class, null, $options); + $view = $form->createView(); + $html = $this->renderForm($view); + + /** + * Form element HTML is expected to be: + * + */ + $this->assertStringNotContainsString(' '{{ form(view) }}']), + new FilesystemLoader([ + __DIR__ . '/../vendor/symfony/twig-bridge/Resources/views/Form', + __DIR__ . '/../src/Resources/views/Form', + ]), + ]); + + $twig = new Environment($loader); + $twig->addExtension(new AccessibleFormsExtension()); + $twig->addExtension(new FormExtension()); + $twig->addExtension(new TranslationExtension()); + + $renderer = new FormRenderer(new TwigRendererEngine(['accessible-forms.html.twig'], $twig)); + $twig->addRuntimeLoader(new FactoryRuntimeLoader([ + FormRenderer::class => fn () => $renderer, + ])); + + return $twig->render('index.html.twig', ['view' => $view]); + } +}