Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7da47cf

Browse files
committed
Add FormFlow component
1 parent5276de0 commit7da47cf

File tree

43 files changed

+2745
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2745
-2
lines changed

‎src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
useSymfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
1818
useSymfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
1919
useSymfony\Component\Form\Extension\Core\Type\FormType;
20+
useSymfony\Component\Form\Flow\FormFlowBuilderInterface;
21+
useSymfony\Component\Form\Flow\FormFlowInterface;
22+
useSymfony\Component\Form\Flow\FormFlowTypeInterface;
2023
useSymfony\Component\Form\FormBuilderInterface;
2124
useSymfony\Component\Form\FormFactoryInterface;
2225
useSymfony\Component\Form\FormInterface;
@@ -345,6 +348,8 @@ protected function createAccessDeniedException(string $message = 'Access Denied.
345348

346349
/**
347350
* Creates and returns a Form instance from the type of the form.
351+
*
352+
* @return ($type is class-string<FormFlowTypeInterface> ? FormFlowInterface : FormInterface)
348353
*/
349354
protectedfunctioncreateForm(string$type,mixed$data =null,array$options = []):FormInterface
350355
{
@@ -353,6 +358,8 @@ protected function createForm(string $type, mixed $data = null, array $options =
353358

354359
/**
355360
* Creates and returns a form builder instance.
361+
*
362+
* @return ($type is class-string<FormFlowTypeInterface> ? FormFlowBuilderInterface : FormBuilderInterface)
356363
*/
357364
protectedfunctioncreateFormBuilder(mixed$data =null,array$options = []):FormBuilderInterface
358365
{

‎src/Symfony/Bundle/FrameworkBundle/Resources/config/form.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
useSymfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension;
2525
useSymfony\Component\Form\Extension\HtmlSanitizer\Type\TextTypeHtmlSanitizerExtension;
2626
useSymfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler;
27+
useSymfony\Component\Form\Extension\HttpFoundation\Type\FormFlowTypeSessionDataStorageExtension;
2728
useSymfony\Component\Form\Extension\HttpFoundation\Type\FormTypeHttpFoundationExtension;
2829
useSymfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension;
2930
useSymfony\Component\Form\Extension\Validator\Type\RepeatedTypeValidatorExtension;
@@ -123,6 +124,10 @@
123124
->args([service('form.type_extension.form.request_handler')])
124125
->tag('form.type_extension')
125126

127+
->set('form.type_extension.form.flow.session_data_storage', FormFlowTypeSessionDataStorageExtension::class)
128+
->args([service('request_stack')->ignoreOnInvalid()])
129+
->tag('form.type_extension')
130+
126131
->set('form.type_extension.form.request_handler', HttpFoundationRequestHandler::class)
127132
->args([service('form.server_params')])
128133

‎src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"symfony/dom-crawler":"^6.4|^7.0",
4646
"symfony/dotenv":"^6.4|^7.0",
4747
"symfony/polyfill-intl-icu":"~1.0",
48-
"symfony/form":"^6.4|^7.0",
48+
"symfony/form":"^7.3",
4949
"symfony/expression-language":"^6.4|^7.0",
5050
"symfony/html-sanitizer":"^6.4|^7.0",
5151
"symfony/http-client":"^6.4|^7.0",
@@ -88,7 +88,7 @@
8888
"symfony/dotenv":"<6.4",
8989
"symfony/dom-crawler":"<6.4",
9090
"symfony/http-client":"<6.4",
91-
"symfony/form":"<6.4",
91+
"symfony/form":"<7.3",
9292
"symfony/json-streamer":">=7.4",
9393
"symfony/lock":"<6.4",
9494
"symfony/mailer":"<6.4",

‎src/Symfony/Component/Form/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.4
5+
---
6+
7+
* Add`FormFlow` component for multistep forms management
8+
49
7.3
510
---
611

‎src/Symfony/Component/Form/Extension/Core/CoreExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ protected function loadTypes(): array
7878
newType\TelType(),
7979
newType\ColorType($this->translator),
8080
newType\WeekType(),
81+
newType\FormFlowType(),
8182
];
8283
}
8384

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespaceSymfony\Component\Form\Extension\Core\Type;
13+
14+
useSymfony\Component\Form\AbstractType;
15+
useSymfony\Component\Form\Flow\ActionButtonTypeInterface;
16+
useSymfony\Component\OptionsResolver\Options;
17+
useSymfony\Component\OptionsResolver\OptionsResolver;
18+
19+
/**
20+
* An action-based submit button for a form flow.
21+
*
22+
* @author Yonel Ceruto <open@yceruto.dev>
23+
*/
24+
class FormFlowActionTypeextends AbstractTypeimplements ActionButtonTypeInterface
25+
{
26+
publicfunctionconfigureOptions(OptionsResolver$resolver):void
27+
{
28+
$resolver->define('action')
29+
->info('The action name of the button')
30+
->default('')
31+
->allowedTypes('string');
32+
33+
$resolver->define('handler')
34+
->info('A callable that will be called when this button is clicked')
35+
->default(null)
36+
->allowedTypes('null','callable');
37+
38+
$resolver->define('include_if')
39+
->info('Decide whether to include this button in the current form')
40+
->default(null)
41+
->allowedTypes('null','callable');
42+
43+
$resolver->define('clear_submission')
44+
->info('Whether the submitted data will be cleared when this button is clicked')
45+
->default(function (Options$options) {
46+
return'reset' ===$options['action'] ||'back' ===$options['action'];
47+
})
48+
->allowedTypes('bool');
49+
50+
$resolver->setDefault('validate',function (Options$options) {
51+
return !$options['clear_submission'];
52+
});
53+
54+
$resolver->setDefault('validation_groups',function (Options$options) {
55+
return$options['clear_submission'] ?false :null;
56+
});
57+
}
58+
59+
publicfunctiongetParent():string
60+
{
61+
return SubmitType::class;
62+
}
63+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespaceSymfony\Component\Form\Extension\Core\Type;
13+
14+
useSymfony\Component\Form\AbstractType;
15+
useSymfony\Component\Form\Flow\FormFlowCursor;
16+
useSymfony\Component\Form\FormBuilderInterface;
17+
useSymfony\Component\OptionsResolver\OptionsResolver;
18+
19+
/**
20+
* A navigator type that defines default actions to interact with a form flow.
21+
*
22+
* @author Yonel Ceruto <open@yceruto.dev>
23+
*/
24+
class FormFlowNavigatorTypeextends AbstractType
25+
{
26+
publicfunctionbuildForm(FormBuilderInterface$builder,array$options):void
27+
{
28+
$builder->add('back', FormFlowActionType::class, [
29+
'action' =>'back',
30+
'include_if' =>fn (FormFlowCursor$cursor):bool =>$cursor->canMoveBack(),
31+
]);
32+
33+
$builder->add('next', FormFlowActionType::class, [
34+
'action' =>'next',
35+
'include_if' =>fn (FormFlowCursor$cursor):bool =>$cursor->canMoveNext(),
36+
]);
37+
38+
$builder->add('finish', FormFlowActionType::class, [
39+
'action' =>'finish',
40+
'include_if' =>fn (FormFlowCursor$cursor):bool =>$cursor->isLastStep(),
41+
]);
42+
}
43+
44+
publicfunctionconfigureOptions(OptionsResolver$resolver):void
45+
{
46+
$resolver->setDefaults([
47+
'label' =>false,
48+
'mapped' =>false,
49+
'priority' => -100,
50+
]);
51+
}
52+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespaceSymfony\Component\Form\Extension\Core\Type;
13+
14+
useSymfony\Component\Form\Flow\AbstractFlowType;
15+
useSymfony\Component\Form\Flow\DataAccessor\PropertyPathStepAccessor;
16+
useSymfony\Component\Form\Flow\DataAccessor\StepAccessorInterface;
17+
useSymfony\Component\Form\Flow\DataStorage\DataStorageInterface;
18+
useSymfony\Component\Form\Flow\DataStorage\NullDataStorage;
19+
useSymfony\Component\Form\Flow\FormFlowBuilderInterface;
20+
useSymfony\Component\Form\Flow\FormFlowInterface;
21+
useSymfony\Component\Form\Flow\FormFlowStepView;
22+
useSymfony\Component\Form\FormBuilderInterface;
23+
useSymfony\Component\Form\FormInterface;
24+
useSymfony\Component\Form\FormView;
25+
useSymfony\Component\OptionsResolver\Exception\MissingOptionsException;
26+
useSymfony\Component\OptionsResolver\Options;
27+
useSymfony\Component\OptionsResolver\OptionsResolver;
28+
useSymfony\Component\PropertyAccess\PropertyAccess;
29+
useSymfony\Component\PropertyAccess\PropertyAccessorInterface;
30+
useSymfony\Component\PropertyAccess\PropertyPath;
31+
useSymfony\Component\PropertyAccess\PropertyPathInterface;
32+
33+
/**
34+
* A multistep form.
35+
*
36+
* @author Yonel Ceruto <open@yceruto.dev>
37+
*/
38+
finalclass FormFlowTypeextends AbstractFlowType
39+
{
40+
publicfunction__construct(
41+
private ?PropertyAccessorInterface$propertyAccessor =null,
42+
) {
43+
$this->propertyAccessor ??= PropertyAccess::createPropertyAccessor();
44+
}
45+
46+
/**
47+
* @param FormFlowBuilderInterface $builder
48+
*/
49+
publicfunctionbuildForm(FormBuilderInterface$builder,array$options):void
50+
{
51+
$builder->setDataStorage($options['data_storage']);
52+
$builder->setStepAccessor($options['step_accessor']);
53+
}
54+
55+
/**
56+
* @param FormFlowInterface $form
57+
*/
58+
publicfunctionbuildView(FormView$view,FormInterface$form,array$options):void
59+
{
60+
$view->vars['cursor'] =$form->getCursor();
61+
62+
foreach ($form->getConfig()->getSteps()as$name =>$step) {
63+
$isCurrentStep =$name ===$view->vars['cursor']->getCurrentStep();
64+
$view->vars['steps'][$name] =newFormFlowStepView($name,$isCurrentStep,$step->isSkipped($form->getViewData()));
65+
}
66+
}
67+
68+
publicfunctionconfigureOptions(OptionsResolver$resolver):void
69+
{
70+
$resolver->define('data_storage')
71+
->default(newNullDataStorage())
72+
->allowedTypes(DataStorageInterface::class);
73+
74+
$resolver->define('step_accessor')
75+
->default(function (Options$options) {
76+
if (!isset($options['step_property_path'])) {
77+
thrownewMissingOptionsException('Option "step_property_path" is required.');
78+
}
79+
80+
returnnewPropertyPathStepAccessor($this->propertyAccessor,$options['step_property_path']);
81+
})
82+
->allowedTypes(StepAccessorInterface::class);
83+
84+
$resolver->define('step_property_path')
85+
->info('Required if the default step_accessor is being used')
86+
->allowedTypes('string', PropertyPathInterface::class)
87+
->normalize(function (Options$options,string|PropertyPathInterface$value):PropertyPathInterface {
88+
return\is_string($value) ?newPropertyPath($value) :$value;
89+
});
90+
91+
$resolver->define('auto_reset')
92+
->info('Whether the FormFlow will be reset automatically when it is finished')
93+
->default(true)
94+
->allowedTypes('bool');
95+
96+
$resolver->setDefault('validation_groups',function (FormFlowInterface$flow) {
97+
return ['Default',$flow->getCursor()->getCurrentStep()];
98+
});
99+
}
100+
101+
publicfunctiongetParent():string
102+
{
103+
return FormType::class;
104+
}
105+
}

‎src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ protected function loadTypeExtensions(): array
2424
{
2525
return [
2626
newType\FormTypeHttpFoundationExtension(),
27+
newType\FormFlowTypeSessionDataStorageExtension(),
2728
];
2829
}
2930
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespaceSymfony\Component\Form\Extension\HttpFoundation\Type;
13+
14+
useSymfony\Component\Form\AbstractTypeExtension;
15+
useSymfony\Component\Form\Extension\Core\Type\FormFlowType;
16+
useSymfony\Component\Form\Flow\DataStorage\NullDataStorage;
17+
useSymfony\Component\Form\Flow\DataStorage\SessionDataStorage;
18+
useSymfony\Component\Form\Flow\FormFlowBuilderInterface;
19+
useSymfony\Component\Form\FormBuilderInterface;
20+
useSymfony\Component\HttpFoundation\RequestStack;
21+
22+
class FormFlowTypeSessionDataStorageExtensionextends AbstractTypeExtension
23+
{
24+
publicfunction__construct(
25+
privatereadonly ?RequestStack$requestStack =null,
26+
) {
27+
}
28+
29+
/**
30+
* @param FormFlowBuilderInterface $builder
31+
*/
32+
publicfunctionbuildForm(FormBuilderInterface$builder,array$options):void
33+
{
34+
if (null ===$this->requestStack || !$builder->getDataStorage()instanceof NullDataStorage) {
35+
return;
36+
}
37+
38+
$key =\sprintf('_form_flow.%s_%s',strtolower(str_replace('\\','_',$builder->getType()->getInnerType()::class)),$builder->getName());
39+
$builder->setDataStorage(newSessionDataStorage($key,$this->requestStack));
40+
}
41+
42+
publicstaticfunctiongetExtendedTypes():iterable
43+
{
44+
yield FormFlowType::class;
45+
}
46+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespaceSymfony\Component\Form\Flow;
13+
14+
useSymfony\Component\Form\AbstractType;
15+
useSymfony\Component\Form\Extension\Core\Type\FormFlowType;
16+
17+
/**
18+
* @author Yonel Ceruto <open@yceruto.dev>
19+
*/
20+
abstractclass AbstractFlowTypeextends AbstractTypeimplements FormFlowTypeInterface
21+
{
22+
publicfunctiongetParent():string
23+
{
24+
return FormFlowType::class;
25+
}
26+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp