Skip to content

Commit ab49dc4

Browse files
committed
Add FormFlow for multistep forms management
1 parent 342063d commit ab49dc4

Some content is hidden

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

48 files changed

+3462
-2
lines changed

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
use Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension;
2525
use Symfony\Component\Form\Extension\HtmlSanitizer\Type\TextTypeHtmlSanitizerExtension;
2626
use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler;
27+
use Symfony\Component\Form\Extension\HttpFoundation\Type\FormFlowTypeSessionDataStorageExtension;
2728
use Symfony\Component\Form\Extension\HttpFoundation\Type\FormTypeHttpFoundationExtension;
2829
use Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension;
2930
use Symfony\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
@@ -46,7 +46,7 @@
4646
"symfony/dom-crawler": "^6.4|^7.0|^8.0",
4747
"symfony/dotenv": "^6.4|^7.0|^8.0",
4848
"symfony/polyfill-intl-icu": "~1.0",
49-
"symfony/form": "^6.4|^7.0|^8.0",
49+
"symfony/form": "^7.4|^8.0",
5050
"symfony/expression-language": "^6.4|^7.0|^8.0",
5151
"symfony/html-sanitizer": "^6.4|^7.0|^8.0",
5252
"symfony/http-client": "^6.4|^7.0|^8.0",
@@ -89,7 +89,7 @@
8989
"symfony/dotenv": "<6.4",
9090
"symfony/dom-crawler": "<6.4",
9191
"symfony/http-client": "<6.4",
92-
"symfony/form": "<6.4",
92+
"symfony/form": "<7.4",
9393
"symfony/lock": "<6.4",
9494
"symfony/mailer": "<6.4",
9595
"symfony/messenger": "<6.4",

src/Symfony/Component/Form/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
---
66

77
* Add `input=date_point` to `DateTimeType`, `DateType` and `TimeType`
8+
* Add `FormFlow` for multistep forms management
89

910
7.3
1011
---

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory;
1818
use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator;
1919
use Symfony\Component\Form\Extension\Core\Type\TransformationFailureExtension;
20+
use Symfony\Component\Form\Flow;
2021
use Symfony\Component\PropertyAccess\PropertyAccess;
2122
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
2223
use Symfony\Contracts\Translation\TranslatorInterface;
@@ -78,6 +79,12 @@ protected function loadTypes(): array
7879
new Type\TelType(),
7980
new Type\ColorType($this->translator),
8081
new Type\WeekType(),
82+
new Flow\Type\FlowButtonType(),
83+
new Flow\Type\FlowFinishType(),
84+
new Flow\Type\FlowNavigatorType(),
85+
new Flow\Type\FlowNextType(),
86+
new Flow\Type\FlowPreviousType(),
87+
new Flow\Type\FormFlowType($this->propertyAccessor),
8188
];
8289
}
8390

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
new Type\FormTypeHttpFoundationExtension(),
27+
new Type\FormFlowTypeSessionDataStorageExtension(),
2728
];
2829
}
2930
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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+
namespace Symfony\Component\Form\Extension\HttpFoundation\Type;
13+
14+
use Symfony\Component\Form\AbstractTypeExtension;
15+
use Symfony\Component\Form\Flow\DataStorage\SessionDataStorage;
16+
use Symfony\Component\Form\Flow\FormFlowBuilderInterface;
17+
use Symfony\Component\Form\Flow\Type\FormFlowType;
18+
use Symfony\Component\Form\FormBuilderInterface;
19+
use Symfony\Component\HttpFoundation\RequestStack;
20+
21+
class FormFlowTypeSessionDataStorageExtension extends AbstractTypeExtension
22+
{
23+
public function __construct(
24+
private readonly ?RequestStack $requestStack = null,
25+
) {
26+
}
27+
28+
public function buildForm(FormBuilderInterface $builder, array $options): void
29+
{
30+
\assert($builder instanceof FormFlowBuilderInterface);
31+
32+
if (null === $this->requestStack || null !== $options['data_storage']) {
33+
return;
34+
}
35+
36+
$key = \sprintf('_sf_formflow.%s_%s', strtolower(str_replace('\\', '_', $builder->getType()->getInnerType()::class)), $builder->getName());
37+
$builder->setDataStorage(new SessionDataStorage($key, $this->requestStack));
38+
}
39+
40+
public static function getExtendedTypes(): iterable
41+
{
42+
return [FormFlowType::class];
43+
}
44+
}
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+
namespace Symfony\Component\Form\Flow;
13+
14+
use Symfony\Component\Form\AbstractType;
15+
use Symfony\Component\Form\Flow\Type\FormFlowType;
16+
17+
/**
18+
* @author Yonel Ceruto <open@yceruto.dev>
19+
*/
20+
abstract class AbstractFlowType extends AbstractType implements FormFlowTypeInterface
21+
{
22+
public function getParent(): string
23+
{
24+
return FormFlowType::class;
25+
}
26+
}
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+
namespace Symfony\Component\Form\Flow\DataStorage;
13+
14+
/**
15+
* Handles storing and retrieving form data between steps.
16+
*
17+
* @author Yonel Ceruto <open@yceruto.dev>
18+
*/
19+
interface DataStorageInterface
20+
{
21+
public function save(object|array $data): void;
22+
23+
public function load(object|array|null $default = null): object|array|null;
24+
25+
public function clear(): void;
26+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
namespace Symfony\Component\Form\Flow\DataStorage;
13+
14+
/**
15+
* @author Yonel Ceruto <open@yceruto.dev>
16+
*/
17+
class InMemoryDataStorage implements DataStorageInterface
18+
{
19+
private array $memory = [];
20+
21+
public function __construct(
22+
private readonly string $key,
23+
) {
24+
}
25+
26+
public function save(object|array $data): void
27+
{
28+
$this->memory[$this->key] = $data;
29+
}
30+
31+
public function load(object|array|null $default = null): object|array|null
32+
{
33+
return $this->memory[$this->key] ?? $default;
34+
}
35+
36+
public function clear(): void
37+
{
38+
unset($this->memory[$this->key]);
39+
}
40+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
namespace Symfony\Component\Form\Flow\DataStorage;
13+
14+
/**
15+
* @author Yonel Ceruto <open@yceruto.dev>
16+
*/
17+
final class NullDataStorage implements DataStorageInterface
18+
{
19+
public function save(object|array $data): void
20+
{
21+
// no-op
22+
}
23+
24+
public function load(object|array|null $default = null): object|array|null
25+
{
26+
return $default;
27+
}
28+
29+
public function clear(): void
30+
{
31+
// no-op
32+
}
33+
}

0 commit comments

Comments
 (0)