mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 18:48:51 +02:00
removed symfonycasts
This commit is contained in:
@@ -33,7 +33,6 @@
|
||||
"symfony/twig-bundle": "~6.4.0",
|
||||
"symfony/var-dumper": "~6.4.0",
|
||||
"symfony/yaml": "~6.4.0",
|
||||
"symfonycasts/dynamic-forms": "^0.1.3",
|
||||
"tecnickcom/tcpdf": "^6.6.0",
|
||||
"thenetworg/oauth2-azure": "^2.0"
|
||||
},
|
||||
|
||||
@@ -3155,9 +3155,6 @@ return array(
|
||||
'Symfony\\Runtime\\Symfony\\Component\\HttpFoundation\\RequestRuntime' => $vendorDir . '/symfony/runtime/Internal/HttpFoundation/RequestRuntime.php',
|
||||
'Symfony\\Runtime\\Symfony\\Component\\HttpFoundation\\ResponseRuntime' => $vendorDir . '/symfony/runtime/Internal/HttpFoundation/ResponseRuntime.php',
|
||||
'Symfony\\Runtime\\Symfony\\Component\\HttpKernel\\HttpKernelInterfaceRuntime' => $vendorDir . '/symfony/runtime/Internal/HttpKernel/HttpKernelInterfaceRuntime.php',
|
||||
'Symfonycasts\\DynamicForms\\DependentField' => $vendorDir . '/symfonycasts/dynamic-forms/src/DependentField.php',
|
||||
'Symfonycasts\\DynamicForms\\DependentFieldConfig' => $vendorDir . '/symfonycasts/dynamic-forms/src/DependentFieldConfig.php',
|
||||
'Symfonycasts\\DynamicForms\\DynamicFormBuilder' => $vendorDir . '/symfonycasts/dynamic-forms/src/DynamicFormBuilder.php',
|
||||
'SynchroExceptionNotStarted' => $baseDir . '/application/exceptions/SynchroExceptionNotStarted.php',
|
||||
'System' => $vendorDir . '/pear/pear-core-minimal/src/System.php',
|
||||
'TCPDF' => $vendorDir . '/tecnickcom/tcpdf/tcpdf.php',
|
||||
|
||||
@@ -10,16 +10,16 @@ return array(
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'662a729f963d39afe703c9d9b7ab4a8c' => $vendorDir . '/symfony/polyfill-php83/bootstrap.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'89efb1254ef2d1c5d80096acd12c4098' => $vendorDir . '/twig/twig/src/Resources/core.php',
|
||||
'ffecb95d45175fd40f75be8a23b34f90' => $vendorDir . '/twig/twig/src/Resources/debug.php',
|
||||
'c7baa00073ee9c61edf148c51917cfb4' => $vendorDir . '/twig/twig/src/Resources/escaper.php',
|
||||
'f844ccf1d25df8663951193c3fc307c8' => $vendorDir . '/twig/twig/src/Resources/string_loader.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'6a47392539ca2329373e0d33e1dba053' => $vendorDir . '/symfony/polyfill-intl-icu/bootstrap.php',
|
||||
'344f11dc3484aaed5cbde58e23513be4' => $vendorDir . '/apereo/phpcas/source/CAS.php',
|
||||
|
||||
@@ -8,7 +8,6 @@ $baseDir = dirname($vendorDir);
|
||||
return array(
|
||||
'Twig\\' => array($vendorDir . '/twig/twig/src'),
|
||||
'TheNetworg\\OAuth2\\Client\\' => array($vendorDir . '/thenetworg/oauth2-azure/src'),
|
||||
'Symfonycasts\\DynamicForms\\' => array($vendorDir . '/symfonycasts/dynamic-forms/src'),
|
||||
'Symfony\\Runtime\\Symfony\\Component\\' => array($vendorDir . '/symfony/runtime/Internal'),
|
||||
'Symfony\\Polyfill\\Php83\\' => array($vendorDir . '/symfony/polyfill-php83'),
|
||||
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
|
||||
|
||||
@@ -11,16 +11,16 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'662a729f963d39afe703c9d9b7ab4a8c' => __DIR__ . '/..' . '/symfony/polyfill-php83/bootstrap.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'89efb1254ef2d1c5d80096acd12c4098' => __DIR__ . '/..' . '/twig/twig/src/Resources/core.php',
|
||||
'ffecb95d45175fd40f75be8a23b34f90' => __DIR__ . '/..' . '/twig/twig/src/Resources/debug.php',
|
||||
'c7baa00073ee9c61edf148c51917cfb4' => __DIR__ . '/..' . '/twig/twig/src/Resources/escaper.php',
|
||||
'f844ccf1d25df8663951193c3fc307c8' => __DIR__ . '/..' . '/twig/twig/src/Resources/string_loader.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'6a47392539ca2329373e0d33e1dba053' => __DIR__ . '/..' . '/symfony/polyfill-intl-icu/bootstrap.php',
|
||||
'344f11dc3484aaed5cbde58e23513be4' => __DIR__ . '/..' . '/apereo/phpcas/source/CAS.php',
|
||||
@@ -35,7 +35,6 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
),
|
||||
'S' =>
|
||||
array (
|
||||
'Symfonycasts\\DynamicForms\\' => 26,
|
||||
'Symfony\\Runtime\\Symfony\\Component\\' => 34,
|
||||
'Symfony\\Polyfill\\Php83\\' => 23,
|
||||
'Symfony\\Polyfill\\Mbstring\\' => 26,
|
||||
@@ -126,10 +125,6 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/thenetworg/oauth2-azure/src',
|
||||
),
|
||||
'Symfonycasts\\DynamicForms\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfonycasts/dynamic-forms/src',
|
||||
),
|
||||
'Symfony\\Runtime\\Symfony\\Component\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/runtime/Internal',
|
||||
@@ -3541,9 +3536,6 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Symfony\\Runtime\\Symfony\\Component\\HttpFoundation\\RequestRuntime' => __DIR__ . '/..' . '/symfony/runtime/Internal/HttpFoundation/RequestRuntime.php',
|
||||
'Symfony\\Runtime\\Symfony\\Component\\HttpFoundation\\ResponseRuntime' => __DIR__ . '/..' . '/symfony/runtime/Internal/HttpFoundation/ResponseRuntime.php',
|
||||
'Symfony\\Runtime\\Symfony\\Component\\HttpKernel\\HttpKernelInterfaceRuntime' => __DIR__ . '/..' . '/symfony/runtime/Internal/HttpKernel/HttpKernelInterfaceRuntime.php',
|
||||
'Symfonycasts\\DynamicForms\\DependentField' => __DIR__ . '/..' . '/symfonycasts/dynamic-forms/src/DependentField.php',
|
||||
'Symfonycasts\\DynamicForms\\DependentFieldConfig' => __DIR__ . '/..' . '/symfonycasts/dynamic-forms/src/DependentFieldConfig.php',
|
||||
'Symfonycasts\\DynamicForms\\DynamicFormBuilder' => __DIR__ . '/..' . '/symfonycasts/dynamic-forms/src/DynamicFormBuilder.php',
|
||||
'SynchroExceptionNotStarted' => __DIR__ . '/../..' . '/application/exceptions/SynchroExceptionNotStarted.php',
|
||||
'System' => __DIR__ . '/..' . '/pear/pear-core-minimal/src/System.php',
|
||||
'TCPDF' => __DIR__ . '/..' . '/tecnickcom/tcpdf/tcpdf.php',
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
Copyright (c) SymfonyCasts <https://symfonycasts.com/>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -1,275 +0,0 @@
|
||||
# Dynamic / Dependent Symfony Form Fields
|
||||
|
||||
[](https://github.com/SymfonyCasts/dynamic-forms/actions/workflows/ci.yaml)
|
||||
|
||||
**NOTE**: This package is currently experimental. It seems to work great - but
|
||||
forms are complex! If you find a bug, please open an issue!
|
||||
|
||||
Ever have a form field that depends on another?
|
||||
|
||||
You can find a [Demo with LiveComponent on Symfony UX](https://ux.symfony.com/demos/live-component/dependent-form-fields).
|
||||
|
||||
* Show a field only if another field is set to a specific value;
|
||||
* Change the options of a field based on the value of another field;
|
||||
* Have multiple-level dependencies (e.g. field A depends on field B
|
||||
which depends on field C).
|
||||
|
||||
```php
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder = new DynamicFormBuilder($builder);
|
||||
|
||||
$builder->add('meal', ChoiceType::class, [
|
||||
'choices' => [
|
||||
'Breakfast' => 'breakfast',
|
||||
'Lunch' => 'lunch',
|
||||
'Dinner' => 'dinner',
|
||||
],
|
||||
]);
|
||||
|
||||
$builder->addDependent('mainFood', ['meal'], function(DependentField $field, string $meal) {
|
||||
// dynamically add choices based on the meal!
|
||||
$choices = ['...'];
|
||||
|
||||
$field->add(ChoiceType::class, [
|
||||
'placeholder' => null === $meal ? 'Select a meal first' : sprintf('What is for %s?', $meal->getReadable()),
|
||||
'choices' => $choices,
|
||||
'disabled' => null === $meal,
|
||||
]);
|
||||
});
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
Install the package with:
|
||||
|
||||
```bash
|
||||
composer require symfonycasts/dynamic-forms
|
||||
```
|
||||
|
||||
Done - you're ready to build dynamic forms!
|
||||
|
||||
## Usage
|
||||
|
||||
Setting up a dependent field is two parts:
|
||||
|
||||
1. [Usage in PHP](#usage-in-php) - set up your Symfony form to handle
|
||||
the dynamic fields;
|
||||
2. [Updating the Frontend](#updating-the-frontend) - adding code to your
|
||||
frontend so that when one field changes, part of the form is re-rendered.
|
||||
|
||||
## Usage in PHP
|
||||
|
||||
Start by wrapping your `FormBuilderInterface` with a `DynamicFormBuilder`:
|
||||
|
||||
```php
|
||||
use Symfonycasts\DynamicForms\DynamicFormBuilder;
|
||||
// ...
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder = new DynamicFormBuilder($builder);
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
`DynamicFormBuilder` has all the same methods as `FormBuilderInterface` plus
|
||||
one extra: `addDependent()`. If a field depends on another, use this method
|
||||
instead of `add()`
|
||||
|
||||
```php
|
||||
// src/Form/FeedbackForm.php
|
||||
|
||||
// ...
|
||||
use Symfonycasts\DynamicForms\DependentField;
|
||||
use Symfonycasts\DynamicForms\DynamicFormBuilder;
|
||||
|
||||
class FeedbackForm extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder = new DynamicFormBuilder($builder);
|
||||
|
||||
$builder->add('rating', ChoiceType::class, [
|
||||
'choices' => [
|
||||
'Select a rating' => null,
|
||||
'Great' => 5,
|
||||
'Good' => 4,
|
||||
'Okay' => 3,
|
||||
'Bad' => 2,
|
||||
'Terrible' => 1
|
||||
],
|
||||
]);
|
||||
|
||||
$builder->addDependent('badRatingNotes', 'rating', function(DependentField $field, ?int $rating) {
|
||||
if (null === $rating || $rating >= 3) {
|
||||
return; // field not needed
|
||||
}
|
||||
|
||||
$field->add(TextareaType::class, [
|
||||
'label' => 'What went wrong?',
|
||||
'attr' => ['rows' => 3],
|
||||
'help' => sprintf('Because you gave a %d rating, we\'d love to know what went wrong.', $rating),
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `addDependent()` method takes 3 arguments:
|
||||
|
||||
1. The name of the field to add;
|
||||
2. The name (or names) of the field that this field depends on;
|
||||
3. A callback that will be called when the form is submitted. This callback
|
||||
receives a `DependentField` object as the first argument then the
|
||||
value of each dependent field as the next arguments.
|
||||
|
||||
Behind the scenes, this works by registering several form event listeners.
|
||||
The callback be executed when the form is first created (using the initial
|
||||
data) and then again when the form is submitted. This means that the callback
|
||||
may be called multiple times.
|
||||
|
||||
Rendering the field is the same - just be sure to make sure the field exists
|
||||
if it's conditionally added:
|
||||
|
||||
```twig
|
||||
{{ form_start(form) }}
|
||||
{{ form_row(form.rating) }}
|
||||
|
||||
{% if form.badRatingNotes is defined %}
|
||||
{{ form_row(form.badRatingNotes) }}
|
||||
{% endif %}
|
||||
|
||||
<button>Send Feedback</button>
|
||||
{{ form_end(form) }}
|
||||
```
|
||||
|
||||
## Updating the Frontend
|
||||
|
||||
In the previous example, when the `rating` field changes, the form (or part of
|
||||
the form) needs to be re-rendered so the `badRatingNotes` field can be added.
|
||||
|
||||
This library doesn't handle this for you, but here are the 2 main options:
|
||||
|
||||
### A) Use [Live Components](https://symfony.com/bundles/ux-live-component/current/index.html)
|
||||
|
||||
This is the easiest method: by rendering your form inside a live component,
|
||||
it will automatically re-render when the form changes.
|
||||
|
||||
### B) Use [Symfony UX Turbo](https://symfony.com/bundles/ux-turbo/current/index.html#decomposing-complex-pages-with-turbo-frames)
|
||||
|
||||
If you are already using Symfony UX Turbo on your website, you can have a dynamic form running quickly without any JavaScript.
|
||||
|
||||
Or you may want to install Symfony UX Turbo, [check out the documentation](https://symfony.com/bundles/ux-turbo/current/index.html#installation).
|
||||
|
||||
> [!NOTE]
|
||||
> You only need to have Turbo Frame, you can disable Turbo Drive if you do not use it, or do not want to use it.
|
||||
> ie: `Turbo.session.drive = false;`
|
||||
|
||||
Simply add a `<turbo-frame>` around your form:
|
||||
|
||||
```twig
|
||||
<turbo-frame id="rating-form">
|
||||
{{ form(form) }}
|
||||
</turbo-frame>
|
||||
```
|
||||
|
||||
From here you need two small changes:
|
||||
|
||||
First, in your form type:
|
||||
- You need to add an attribute on the choice field, so it auto-submits the form when changed (may need to be adapted to your own form if more complex)
|
||||
- Add a submit button, so in the controller you can differenciate from an auto-submit versus a user action
|
||||
|
||||
|
||||
```diff
|
||||
// src/Form/FeedbackForm.php
|
||||
|
||||
// ...
|
||||
|
||||
class FeedbackForm extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder = new DynamicFormBuilder($builder);
|
||||
|
||||
$builder->add('rating', ChoiceType::class, [
|
||||
'choices' => [
|
||||
'Select a rating' => null,
|
||||
'Great' => 5,
|
||||
'Good' => 4,
|
||||
'Okay' => 3,
|
||||
'Bad' => 2,
|
||||
'Terrible' => 1
|
||||
],
|
||||
+ // This will allow the form to auto-submit on value change
|
||||
+ 'attr' => ['onchange' => 'this.form.requestSubmit()'],
|
||||
]);
|
||||
+ // This will allow to differenciate between a user submition and an auto-submit
|
||||
+ $builder->add('submit', SubmitType::class, [
|
||||
+ 'attr' => ['value' => 'submit'], // Needed for Turbo
|
||||
+ ]);
|
||||
|
||||
$builder->addDependent('badRatingNotes', 'rating', function(DependentField $field, ?int $rating) {
|
||||
if (null === $rating || $rating >= 3) {
|
||||
return; // field not needed
|
||||
}
|
||||
|
||||
$field->add(TextareaType::class, [
|
||||
'label' => 'What went wrong?',
|
||||
'attr' => ['rows' => 3],
|
||||
'help' => sprintf('Because you gave a %d rating, we\'d love to know what went wrong.', $rating),
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Second, in your controller:
|
||||
- Specify the action on your form, [this is needed for Turbo Frame](https://symfony.com/bundles/ux-turbo/current/index.html#3-form-response-code-changes)
|
||||
- Handle the auto-submit by checking if the button has been clicked
|
||||
|
||||
```diff
|
||||
// src/Controller/FeedbackController.php
|
||||
|
||||
#[Route('/feedback', name: 'feedback')]
|
||||
public function feedback(Request $request): Response
|
||||
{
|
||||
//...
|
||||
|
||||
- $feedbackForm = $this->createForm(FeedbackForm::class);
|
||||
+ $feedbackForm = $this->createForm(FeedbackForm::class, options: [
|
||||
+ // This is needed by Turbo Frame, it is not specific to Dependent Symfony Form Fields
|
||||
+ 'action' => $this->generateUrl('feedback'),
|
||||
+ ]);
|
||||
$feedbackForm->handleRequest($request);
|
||||
if ($feedbackForm->isSubmitted() && $feedbackForm->isValid()) {
|
||||
|
||||
+ /** @var SubmitButton $submitButton */
|
||||
+ $submitButton = $feedbackForm->get('submit');
|
||||
+ if (!$submitButton->isClicked()) {
|
||||
+ return $this->render('feedback.html.twig', ['feedbackForm' => $feedbackForm]);
|
||||
+ }
|
||||
|
||||
// Your code here
|
||||
// ...
|
||||
|
||||
return $this->redirectToRoute('home');
|
||||
}
|
||||
|
||||
return $this->render('feedback.html.twig', ['feedbackForm' => $feedbackForm]);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### C) Write custom JavaScript
|
||||
|
||||
If you're not using Live Components, nor Turbo Frames, you'll need to write some custom
|
||||
JavaScript to listen to the `change` event on the `rating` field and then
|
||||
make an AJAX call to re-render the form. The AJAX call should submit the
|
||||
form to its usual endpoint (or any endpoint that will submit the form), take
|
||||
the HTML response, extract the parts that need to be re-rendered and then replace
|
||||
the HTML on the page.
|
||||
|
||||
This is a non-trivial task and there may be room for improvement in this
|
||||
library to make this easier. If you have ideas, please open an issue!
|
||||
@@ -1,51 +0,0 @@
|
||||
{
|
||||
"name": "symfonycasts/dynamic-forms",
|
||||
"description": "Add dynamic/dependent fields to Symfony forms",
|
||||
"license": "MIT",
|
||||
"type": "library",
|
||||
"keywords": ["symfony", "forms"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ryan Weaver",
|
||||
"homepage": "https://symfonycasts.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"symfony/form": "^5.4|^6.3|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/framework-bundle": "^6.3|^7.0",
|
||||
"symfony/phpunit-bridge": "^5.4.32|^6.3.9|^7.0",
|
||||
"zenstruck/browser": "^1.4",
|
||||
"symfony/twig-bundle": "^5.4|^6.3|^7.0",
|
||||
"twig/twig": "^2.15|^3.0",
|
||||
"symfony/options-resolver": "^5.4|^6.3|^7.0",
|
||||
"phpunit/phpunit": "^9.6"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfonycasts\\DynamicForms\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Symfonycasts\\DynamicForms\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"tools:upgrade": [
|
||||
"@tools:upgrade:php-cs-fixer",
|
||||
"@tools:upgrade:phpstan"
|
||||
],
|
||||
"tools:upgrade:php-cs-fixer": "composer upgrade -W -d tools/php-cs-fixer",
|
||||
"tools:upgrade:phpstan": "composer upgrade -W -d tools/phpstan",
|
||||
"tools:run": [
|
||||
"@tools:run:php-cs-fixer",
|
||||
"@tools:run:phpstan"
|
||||
],
|
||||
"tools:run:php-cs-fixer": "tools/php-cs-fixer/vendor/bin/php-cs-fixer fix",
|
||||
"tools:run:phpstan": "tools/phpstan/vendor/bin/phpstan --memory-limit=1G"
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the SymfonyCasts DynamicForms package.
|
||||
* Copyright (c) SymfonyCasts <https://symfonycasts.com/>
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfonycasts\DynamicForms;
|
||||
|
||||
/**
|
||||
* Used to configure a dependent/dynamic field.
|
||||
*
|
||||
* If ->add() is not called, the field won't be included.
|
||||
*/
|
||||
class DependentField
|
||||
{
|
||||
private ?string $type = null;
|
||||
private array $options = [];
|
||||
private bool $shouldBeAdded = false;
|
||||
|
||||
public function add(?string $type = null, array $options = []): static
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->options = $options;
|
||||
$this->shouldBeAdded = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getType(): ?string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function getOptions(): array
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
public function shouldBeAdded(): bool
|
||||
{
|
||||
return $this->shouldBeAdded;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the SymfonyCasts DynamicForms package.
|
||||
* Copyright (c) SymfonyCasts <https://symfonycasts.com/>
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfonycasts\DynamicForms;
|
||||
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
/**
|
||||
* Holds the configuration for a dynamic field & what listeners have been executed.
|
||||
*/
|
||||
class DependentFieldConfig
|
||||
{
|
||||
public array $callbackExecuted = [
|
||||
FormEvents::PRE_SET_DATA => false,
|
||||
FormEvents::POST_SUBMIT => false,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
public string $name,
|
||||
public array $dependencies,
|
||||
public \Closure $callback,
|
||||
) {
|
||||
}
|
||||
|
||||
public function isReady(array $availableDependencyData, string $eventName): bool
|
||||
{
|
||||
if (!\array_key_exists($eventName, $this->callbackExecuted)) {
|
||||
throw new \InvalidArgumentException(\sprintf('Invalid event name "%s"', $eventName));
|
||||
}
|
||||
|
||||
if ($this->callbackExecuted[$eventName]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->dependencies as $dependency) {
|
||||
if (!\array_key_exists($dependency, $availableDependencyData)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function execute(array $availableDependencyData, string $eventName): DependentField
|
||||
{
|
||||
$configurableFormBuilder = new DependentField();
|
||||
|
||||
$this->callbackExecuted[$eventName] = true;
|
||||
$dependencyData = array_map(fn (string $dependency) => $availableDependencyData[$dependency], $this->dependencies);
|
||||
$this->callback->__invoke($configurableFormBuilder, ...$dependencyData);
|
||||
|
||||
return $configurableFormBuilder;
|
||||
}
|
||||
}
|
||||
@@ -1,587 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the SymfonyCasts DynamicForms package.
|
||||
* Copyright (c) SymfonyCasts <https://symfonycasts.com/>
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfonycasts\DynamicForms;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\ClearableErrorsInterface;
|
||||
use Symfony\Component\Form\DataMapperInterface;
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormConfigInterface;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\FormFactoryInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\RequestHandlerInterface;
|
||||
use Symfony\Component\Form\ResolvedFormTypeInterface;
|
||||
use Symfony\Component\PropertyAccess\PropertyPathInterface;
|
||||
|
||||
/**
|
||||
* Wraps the normal form builder & to add addDynamic() to it.
|
||||
*
|
||||
* @author Ryan Weaver
|
||||
*/
|
||||
class DynamicFormBuilder implements FormBuilderInterface, \IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* @var DependentFieldConfig[]
|
||||
*/
|
||||
private array $dependentFieldConfigs = [];
|
||||
|
||||
/**
|
||||
* The actual form that this builder is turned into.
|
||||
*/
|
||||
private FormInterface $form;
|
||||
|
||||
private array $preSetDataDependencyData = [];
|
||||
private array $postSubmitDependencyData = [];
|
||||
|
||||
public function __construct(private FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
|
||||
$this->form = $event->getForm();
|
||||
$this->preSetDataDependencyData = [];
|
||||
$this->initializeListeners();
|
||||
|
||||
// A fake hidden field where we can "store" an error if a dependent form
|
||||
// field is suddenly invalid because its previous data was invalid
|
||||
// and a field it depends on just changed (e.g. user selected "Michigan"
|
||||
// as a state, then the user changed "Country" from "USA" to "Mexico"
|
||||
// and so now "Michigan" is invalid). In this case, we clear the error
|
||||
// on the actual field, but store a "fake" error here, which won't be
|
||||
// rendered, but will prevent the form from being valid.
|
||||
if (!$this->form->has('__dynamic_error')) {
|
||||
$this->form->add('__dynamic_error', HiddenType::class, [
|
||||
'mapped' => false,
|
||||
'error_bubbling' => false,
|
||||
]);
|
||||
}
|
||||
}, 100);
|
||||
|
||||
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
|
||||
$this->postSubmitDependencyData = [];
|
||||
});
|
||||
// guarantee later than core ValidationListener
|
||||
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
|
||||
$this->clearDataOnTransformationError($event);
|
||||
}, -1);
|
||||
}
|
||||
|
||||
public function addDependent(string $name, string|array $dependencies, callable $callback): self
|
||||
{
|
||||
$dependencies = (array) $dependencies;
|
||||
|
||||
$this->dependentFieldConfigs[] = new DependentFieldConfig($name, $dependencies, $callback);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function storePreSetDataDependencyData(FormEvent $event): void
|
||||
{
|
||||
$dependency = $event->getForm()->getName();
|
||||
$this->preSetDataDependencyData[$dependency] = $event->getData();
|
||||
|
||||
$this->executeReadyCallbacks($this->preSetDataDependencyData, FormEvents::PRE_SET_DATA);
|
||||
}
|
||||
|
||||
public function storePostSubmitDependencyData(FormEvent $event): void
|
||||
{
|
||||
$dependency = $event->getForm()->getName();
|
||||
$this->postSubmitDependencyData[$dependency] = $event->getForm()->getData();
|
||||
|
||||
$this->executeReadyCallbacks($this->postSubmitDependencyData, FormEvents::POST_SUBMIT);
|
||||
}
|
||||
|
||||
public function clearDataOnTransformationError(FormEvent $event): void
|
||||
{
|
||||
$form = $event->getForm();
|
||||
$transformationErrorsCleared = false;
|
||||
foreach ($this->dependentFieldConfigs as $dependentFieldConfig) {
|
||||
if (!$form->has($dependentFieldConfig->name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$subForm = $form->get($dependentFieldConfig->name);
|
||||
if ($subForm->getTransformationFailure() && $subForm instanceof ClearableErrorsInterface) {
|
||||
$subForm->clearErrors();
|
||||
$transformationErrorsCleared = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($transformationErrorsCleared) {
|
||||
// We've cleared the error, but the bad data remains on the field.
|
||||
// We need to make sure that the form doesn't submit successfully,
|
||||
// but we also don't want to render a validation error on any field.
|
||||
// So, we jam the error into a hidden field, which doesn't render errors.
|
||||
if ($form->get('__dynamic_error')->isValid()) {
|
||||
$form->get('__dynamic_error')->addError(new FormError('Some dynamic fields have errors.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function executeReadyCallbacks(array $availableDependencyData, string $eventName): void
|
||||
{
|
||||
foreach ($this->dependentFieldConfigs as $dependentFieldConfig) {
|
||||
if ($dependentFieldConfig->isReady($availableDependencyData, $eventName)) {
|
||||
$dynamicField = $dependentFieldConfig->execute($availableDependencyData, $eventName);
|
||||
$name = $dependentFieldConfig->name;
|
||||
|
||||
if (!$dynamicField->shouldBeAdded()) {
|
||||
$this->form->remove($name);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->builder->add($name, $dynamicField->getType(), $dynamicField->getOptions());
|
||||
|
||||
$this->initializeListeners([$name]);
|
||||
// auto initialize mimics FormBuilder::getForm() behavior
|
||||
$field = $this->builder->get($name)->setAutoInitialize(false)->getForm();
|
||||
$this->form->add($field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function initializeListeners(?array $fieldsToConsider = null): void
|
||||
{
|
||||
$registeredFields = [];
|
||||
foreach ($this->dependentFieldConfigs as $dynamicField) {
|
||||
foreach ($dynamicField->dependencies as $dependency) {
|
||||
if ($fieldsToConsider && !\in_array($dependency, $fieldsToConsider)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip dependencies that are possibly not *yet* part of the form
|
||||
if (!$this->builder->has($dependency)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (\in_array($dependency, $registeredFields)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$registeredFields[] = $dependency;
|
||||
|
||||
$this->builder->get($dependency)->addEventListener(FormEvents::PRE_SET_DATA, [$this, 'storePreSetDataDependencyData']);
|
||||
$this->builder->get($dependency)->addEventListener(FormEvents::POST_SUBMIT, [$this, 'storePostSubmitDependencyData']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ----------------------------------------
|
||||
*
|
||||
* Pure decoration methods below.
|
||||
*
|
||||
* ----------------------------------------
|
||||
*/
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return $this->builder->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|FormBuilderInterface $child
|
||||
*/
|
||||
public function add($child, ?string $type = null, array $options = []): static
|
||||
{
|
||||
$this->builder->add($child, $type, $options);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function create(string $name, ?string $type = null, array $options = []): FormBuilderInterface
|
||||
{
|
||||
return $this->builder->create($name, $type, $options);
|
||||
}
|
||||
|
||||
public function get(string $name): FormBuilderInterface
|
||||
{
|
||||
return $this->builder->get($name);
|
||||
}
|
||||
|
||||
public function remove(string $name): static
|
||||
{
|
||||
$this->builder->remove($name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return $this->builder->has($name);
|
||||
}
|
||||
|
||||
public function all(): array
|
||||
{
|
||||
return $this->builder->all();
|
||||
}
|
||||
|
||||
public function getForm(): FormInterface
|
||||
{
|
||||
return $this->builder->getForm();
|
||||
}
|
||||
|
||||
public function addEventListener(string $eventName, callable $listener, int $priority = 0): static
|
||||
{
|
||||
$this->builder->addEventListener($eventName, $listener, $priority);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addEventSubscriber(EventSubscriberInterface $subscriber): static
|
||||
{
|
||||
$this->builder->addEventSubscriber($subscriber);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addViewTransformer(DataTransformerInterface $viewTransformer, bool $forcePrepend = false): static
|
||||
{
|
||||
$this->builder->addViewTransformer($viewTransformer, $forcePrepend);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function resetViewTransformers(): static
|
||||
{
|
||||
$this->builder->resetViewTransformers();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addModelTransformer(DataTransformerInterface $modelTransformer, bool $forceAppend = false): static
|
||||
{
|
||||
$this->builder->addModelTransformer($modelTransformer, $forceAppend);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function resetModelTransformers(): static
|
||||
{
|
||||
$this->builder->resetModelTransformers();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setAttribute(string $name, mixed $value): static
|
||||
{
|
||||
$this->builder->setAttribute($name, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setAttributes(array $attributes): static
|
||||
{
|
||||
$this->builder->setAttributes($attributes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataMapper(?DataMapperInterface $dataMapper = null): static
|
||||
{
|
||||
$this->builder->setDataMapper($dataMapper);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDisabled(bool $disabled): static
|
||||
{
|
||||
$this->builder->setDisabled($disabled);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setEmptyData(mixed $emptyData): static
|
||||
{
|
||||
$this->builder->setEmptyData($emptyData);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setErrorBubbling(bool $errorBubbling): static
|
||||
{
|
||||
$this->builder->setErrorBubbling($errorBubbling);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setInheritData(bool $inheritData): static
|
||||
{
|
||||
$this->builder->setInheritData($inheritData);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMapped(bool $mapped): static
|
||||
{
|
||||
$this->builder->setMapped($mapped);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMethod(string $method): static
|
||||
{
|
||||
$this->builder->setMethod($method);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|PropertyPathInterface|null $propertyPath
|
||||
*/
|
||||
public function setPropertyPath($propertyPath): static
|
||||
{
|
||||
$this->builder->setPropertyPath($propertyPath);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRequired(bool $required): static
|
||||
{
|
||||
$this->builder->setRequired($required);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setAction(?string $action): static
|
||||
{
|
||||
$this->builder->setAction($action);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setCompound(bool $compound): static
|
||||
{
|
||||
$this->builder->setCompound($compound);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDataLocked(bool $locked): static
|
||||
{
|
||||
$this->builder->setDataLocked($locked);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setFormFactory(FormFactoryInterface $formFactory): static
|
||||
{
|
||||
$this->builder->setFormFactory($formFactory);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setType(?ResolvedFormTypeInterface $type): static
|
||||
{
|
||||
$this->builder->setType($type);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRequestHandler(?RequestHandlerInterface $requestHandler): static
|
||||
{
|
||||
$this->builder->setRequestHandler($requestHandler);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAttribute(string $name, mixed $default = null): mixed
|
||||
{
|
||||
return $this->builder->getAttribute($name, $default);
|
||||
}
|
||||
|
||||
public function hasAttribute(string $name): bool
|
||||
{
|
||||
return $this->builder->hasAttribute($name);
|
||||
}
|
||||
|
||||
public function getAttributes(): array
|
||||
{
|
||||
return $this->builder->getAttributes();
|
||||
}
|
||||
|
||||
public function getDataMapper(): ?DataMapperInterface
|
||||
{
|
||||
return $this->builder->getDataMapper();
|
||||
}
|
||||
|
||||
public function getEventDispatcher(): EventDispatcherInterface
|
||||
{
|
||||
return $this->builder->getEventDispatcher();
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->builder->getName();
|
||||
}
|
||||
|
||||
public function getPropertyPath(): ?PropertyPathInterface
|
||||
{
|
||||
return $this->builder->getPropertyPath();
|
||||
}
|
||||
|
||||
public function getRequestHandler(): RequestHandlerInterface
|
||||
{
|
||||
return $this->builder->getRequestHandler();
|
||||
}
|
||||
|
||||
public function getType(): ResolvedFormTypeInterface
|
||||
{
|
||||
return $this->builder->getType();
|
||||
}
|
||||
|
||||
public function setByReference(bool $byReference): static
|
||||
{
|
||||
$this->builder->setByReference($byReference);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setData(mixed $data): static
|
||||
{
|
||||
$this->builder->setData($data);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setAutoInitialize(bool $initialize): static
|
||||
{
|
||||
$this->builder->setAutoInitialize($initialize);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFormConfig(): FormConfigInterface
|
||||
{
|
||||
return $this->builder->getFormConfig();
|
||||
}
|
||||
|
||||
public function setIsEmptyCallback(?callable $isEmptyCallback): static
|
||||
{
|
||||
$this->builder->setIsEmptyCallback($isEmptyCallback);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMapped(): bool
|
||||
{
|
||||
return $this->builder->getMapped();
|
||||
}
|
||||
|
||||
public function getByReference(): bool
|
||||
{
|
||||
return $this->builder->getByReference();
|
||||
}
|
||||
|
||||
public function getInheritData(): bool
|
||||
{
|
||||
return $this->builder->getInheritData();
|
||||
}
|
||||
|
||||
public function getCompound(): bool
|
||||
{
|
||||
return $this->builder->getCompound();
|
||||
}
|
||||
|
||||
public function getViewTransformers(): array
|
||||
{
|
||||
return $this->builder->getViewTransformers();
|
||||
}
|
||||
|
||||
public function getModelTransformers(): array
|
||||
{
|
||||
return $this->builder->getModelTransformers();
|
||||
}
|
||||
|
||||
public function getRequired(): bool
|
||||
{
|
||||
return $this->builder->getRequired();
|
||||
}
|
||||
|
||||
public function getDisabled(): bool
|
||||
{
|
||||
return $this->builder->getDisabled();
|
||||
}
|
||||
|
||||
public function getErrorBubbling(): bool
|
||||
{
|
||||
return $this->builder->getErrorBubbling();
|
||||
}
|
||||
|
||||
public function getEmptyData(): mixed
|
||||
{
|
||||
return $this->builder->getEmptyData();
|
||||
}
|
||||
|
||||
public function getData(): mixed
|
||||
{
|
||||
return $this->builder->getData();
|
||||
}
|
||||
|
||||
public function getDataClass(): ?string
|
||||
{
|
||||
return $this->builder->getDataClass();
|
||||
}
|
||||
|
||||
public function getDataLocked(): bool
|
||||
{
|
||||
return $this->builder->getDataLocked();
|
||||
}
|
||||
|
||||
public function getFormFactory(): FormFactoryInterface
|
||||
{
|
||||
return $this->builder->getFormFactory();
|
||||
}
|
||||
|
||||
public function getAction(): string
|
||||
{
|
||||
return $this->builder->getAction();
|
||||
}
|
||||
|
||||
public function getMethod(): string
|
||||
{
|
||||
return $this->builder->getMethod();
|
||||
}
|
||||
|
||||
public function getAutoInitialize(): bool
|
||||
{
|
||||
return $this->builder->getAutoInitialize();
|
||||
}
|
||||
|
||||
public function getOptions(): array
|
||||
{
|
||||
return $this->builder->getOptions();
|
||||
}
|
||||
|
||||
public function hasOption(string $name): bool
|
||||
{
|
||||
return $this->builder->hasOption($name);
|
||||
}
|
||||
|
||||
public function getOption(string $name, mixed $default = null): mixed
|
||||
{
|
||||
return $this->builder->getOption($name, $default);
|
||||
}
|
||||
|
||||
public function getIsEmptyCallback(): ?callable
|
||||
{
|
||||
return $this->builder->getIsEmptyCallback();
|
||||
}
|
||||
|
||||
public function getIterator(): \Traversable
|
||||
{
|
||||
return $this->builder;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user