Skip to content

Commit 3c1bc9d

Browse files
committed
[Form] Implemented support for buttons
1 parent 5525164 commit 3c1bc9d

23 files changed

+1993
-201
lines changed

Button.php

Lines changed: 412 additions & 0 deletions
Large diffs are not rendered by default.

ButtonBuilder.php

Lines changed: 823 additions & 0 deletions
Large diffs are not rendered by default.

ButtonTypeInterface.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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;
13+
14+
/**
15+
* A type that should be converted into a {@link Button} instance.
16+
*
17+
* @author Bernhard Schussek <[email protected]>
18+
*/
19+
interface ButtonTypeInterface extends FormTypeInterface
20+
{
21+
}

ClickableInterface.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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;
13+
14+
/**
15+
* A clickable form element.
16+
*
17+
* @author Bernhard Schussek <[email protected]>
18+
*/
19+
interface ClickableInterface
20+
{
21+
/**
22+
* Returns whether this element was clicked.
23+
*
24+
* @return Boolean Whether this element was clicked.
25+
*/
26+
public function isClicked();
27+
}

Extension/Core/CoreExtension.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ protected function loadTypes()
5050
new Type\TimezoneType(),
5151
new Type\UrlType(),
5252
new Type\FileType(),
53+
new Type\ButtonType(),
54+
new Type\SubmitType(),
55+
new Type\ResetType(),
5356
);
5457
}
5558
}

Extension/Core/Type/BaseType.php

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Core\Type;
13+
14+
use Symfony\Component\Form\AbstractType;
15+
use Symfony\Component\Form\FormBuilderInterface;
16+
use Symfony\Component\Form\FormInterface;
17+
use Symfony\Component\Form\FormView;
18+
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
19+
20+
/**
21+
* Encapsulates common logic of {@link FormType} and {@link ButtonType}.
22+
*
23+
* This type does not appear in the form's type inheritance chain and as such
24+
* cannot be extended (via {@link FormTypeExtension}s) nor themed.
25+
*
26+
* @author Bernhard Schussek <[email protected]>
27+
*/
28+
abstract class BaseType extends AbstractType
29+
{
30+
/**
31+
* {@inheritdoc}
32+
*/
33+
public function buildForm(FormBuilderInterface $builder, array $options)
34+
{
35+
$builder->setDisabled($options['disabled']);
36+
}
37+
38+
/**
39+
* {@inheritdoc}
40+
*/
41+
public function buildView(FormView $view, FormInterface $form, array $options)
42+
{
43+
/* @var \Symfony\Component\Form\ClickableInterface $form */
44+
45+
$name = $form->getName();
46+
$blockName = $options['block_name'] ?: $form->getName();
47+
$translationDomain = $options['translation_domain'];
48+
49+
if ($view->parent) {
50+
if ('' !== ($parentFullName = $view->parent->vars['full_name'])) {
51+
$id = sprintf('%s_%s', $view->parent->vars['id'], $name);
52+
$fullName = sprintf('%s[%s]', $parentFullName, $name);
53+
$uniqueBlockPrefix = sprintf('%s_%s', $view->parent->vars['unique_block_prefix'], $blockName);
54+
} else {
55+
$id = $name;
56+
$fullName = $name;
57+
$uniqueBlockPrefix = '_'.$blockName;
58+
}
59+
60+
if (!$translationDomain) {
61+
$translationDomain = $view->parent->vars['translation_domain'];
62+
}
63+
} else {
64+
$id = $name;
65+
$fullName = $name;
66+
$uniqueBlockPrefix = '_'.$blockName;
67+
68+
// Strip leading underscores and digits. These are allowed in
69+
// form names, but not in HTML4 ID attributes.
70+
// http://www.w3.org/TR/html401/struct/global.html#adef-id
71+
$id = ltrim($id, '_0123456789');
72+
}
73+
74+
$blockPrefixes = array();
75+
for ($type = $form->getConfig()->getType(); null !== $type; $type = $type->getParent()) {
76+
array_unshift($blockPrefixes, $type->getName());
77+
}
78+
$blockPrefixes[] = $uniqueBlockPrefix;
79+
80+
if (!$translationDomain) {
81+
$translationDomain = 'messages';
82+
}
83+
84+
$view->vars = array_replace($view->vars, array(
85+
'form' => $view,
86+
'id' => $id,
87+
'name' => $name,
88+
'full_name' => $fullName,
89+
'disabled' => $form->isDisabled(),
90+
'label' => $options['label'],
91+
'multipart' => false,
92+
'attr' => $options['attr'],
93+
'block_prefixes' => $blockPrefixes,
94+
'unique_block_prefix' => $uniqueBlockPrefix,
95+
'translation_domain' => $translationDomain,
96+
// Using the block name here speeds up performance in collection
97+
// forms, where each entry has the same full block name.
98+
// Including the type is important too, because if rows of a
99+
// collection form have different types (dynamically), they should
100+
// be rendered differently.
101+
// https://github.com/symfony/symfony/issues/5038
102+
'cache_key' => $uniqueBlockPrefix.'_'.$form->getConfig()->getType()->getName(),
103+
));
104+
}
105+
106+
/**
107+
* {@inheritdoc}
108+
*/
109+
public function setDefaultOptions(OptionsResolverInterface $resolver)
110+
{
111+
$resolver->setDefaults(array(
112+
'block_name' => null,
113+
'disabled' => false,
114+
'label' => null,
115+
'attr' => array(),
116+
'translation_domain' => null,
117+
));
118+
119+
$resolver->setAllowedTypes(array(
120+
'attr' => 'array',
121+
));
122+
}
123+
}

Extension/Core/Type/ButtonType.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Core\Type;
13+
14+
use Symfony\Component\Form\ButtonTypeInterface;
15+
16+
/**
17+
* A form button.
18+
*
19+
* @author Bernhard Schussek <[email protected]>
20+
*/
21+
class ButtonType extends BaseType implements ButtonTypeInterface
22+
{
23+
/**
24+
* {@inheritdoc}
25+
*/
26+
public function getParent()
27+
{
28+
return null;
29+
}
30+
31+
/**
32+
* {@inheritdoc}
33+
*/
34+
public function getName()
35+
{
36+
return 'button';
37+
}
38+
}

Extension/Core/Type/FormType.php

Lines changed: 18 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
namespace Symfony\Component\Form\Extension\Core\Type;
1313

14-
use Symfony\Component\Form\AbstractType;
1514
use Symfony\Component\Form\FormBuilderInterface;
1615
use Symfony\Component\Form\FormInterface;
1716
use Symfony\Component\Form\FormView;
@@ -23,7 +22,7 @@
2322
use Symfony\Component\PropertyAccess\PropertyAccess;
2423
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
2524

26-
class FormType extends AbstractType
25+
class FormType extends BaseType
2726
{
2827
/**
2928
* @var PropertyAccessorInterface
@@ -40,9 +39,10 @@ public function __construct(PropertyAccessorInterface $propertyAccessor = null)
4039
*/
4140
public function buildForm(FormBuilderInterface $builder, array $options)
4241
{
42+
parent::buildForm($builder, $options);
43+
4344
$builder
4445
->setRequired($options['required'])
45-
->setDisabled($options['disabled'])
4646
->setErrorBubbling($options['error_bubbling'])
4747
->setEmptyData($options['empty_data'])
4848
->setPropertyPath($options['property_path'])
@@ -65,85 +65,34 @@ public function buildForm(FormBuilderInterface $builder, array $options)
6565
*/
6666
public function buildView(FormView $view, FormInterface $form, array $options)
6767
{
68+
parent::buildView($view, $form, $options);
69+
6870
$name = $form->getName();
69-
$blockName = $options['block_name'] ?: $form->getName();
7071
$readOnly = $options['read_only'];
71-
$translationDomain = $options['translation_domain'];
7272

7373
if ($view->parent) {
7474
if ('' === $name) {
7575
throw new Exception('Form node with empty name can be used only as root form node.');
7676
}
7777

78-
if ('' !== ($parentFullName = $view->parent->vars['full_name'])) {
79-
$id = sprintf('%s_%s', $view->parent->vars['id'], $name);
80-
$fullName = sprintf('%s[%s]', $parentFullName, $name);
81-
$uniqueBlockPrefix = sprintf('%s_%s', $view->parent->vars['unique_block_prefix'], $blockName);
82-
} else {
83-
$id = $name;
84-
$fullName = $name;
85-
$uniqueBlockPrefix = '_'.$blockName;
86-
}
87-
8878
// Complex fields are read-only if they themselves or their parents are.
8979
if (!$readOnly) {
9080
$readOnly = $view->parent->vars['read_only'];
9181
}
92-
93-
if (!$translationDomain) {
94-
$translationDomain = $view->parent->vars['translation_domain'];
95-
}
96-
} else {
97-
$id = $name;
98-
$fullName = $name;
99-
$uniqueBlockPrefix = '_'.$blockName;
100-
101-
// Strip leading underscores and digits. These are allowed in
102-
// form names, but not in HTML4 ID attributes.
103-
// http://www.w3.org/TR/html401/struct/global.html#adef-id
104-
$id = ltrim($id, '_0123456789');
105-
}
106-
107-
$blockPrefixes = array();
108-
for ($type = $form->getConfig()->getType(); null !== $type; $type = $type->getParent()) {
109-
array_unshift($blockPrefixes, $type->getName());
110-
}
111-
$blockPrefixes[] = $uniqueBlockPrefix;
112-
113-
if (!$translationDomain) {
114-
$translationDomain = 'messages';
11582
}
11683

11784
$view->vars = array_replace($view->vars, array(
118-
'form' => $view,
119-
'id' => $id,
120-
'name' => $name,
121-
'full_name' => $fullName,
122-
'read_only' => $readOnly,
123-
'errors' => $form->getErrors(),
124-
'valid' => $form->isBound() ? $form->isValid() : true,
125-
'value' => $form->getViewData(),
126-
'data' => $form->getNormData(),
127-
'disabled' => $form->isDisabled(),
128-
'required' => $form->isRequired(),
129-
'max_length' => $options['max_length'],
130-
'pattern' => $options['pattern'],
131-
'size' => null,
132-
'label' => $options['label'],
133-
'multipart' => false,
134-
'attr' => $options['attr'],
135-
'label_attr' => $options['label_attr'],
136-
'compound' => $form->getConfig()->getCompound(),
137-
'block_prefixes' => $blockPrefixes,
138-
'unique_block_prefix' => $uniqueBlockPrefix,
139-
'translation_domain' => $translationDomain,
140-
// Using the block name here speeds up performance in collection
141-
// forms, where each entry has the same full block name.
142-
// Including the type is important too, because if rows of a
143-
// collection form have different types (dynamically), they should
144-
// be rendered differently.
145-
// https://github.com/symfony/symfony/issues/5038
146-
'cache_key' => $uniqueBlockPrefix.'_'.$form->getConfig()->getType()->getName(),
85+
'read_only' => $readOnly,
86+
'errors' => $form->getErrors(),
87+
'valid' => $form->isBound() ? $form->isValid() : true,
88+
'value' => $form->getViewData(),
89+
'data' => $form->getNormData(),
90+
'required' => $form->isRequired(),
91+
'max_length' => $options['max_length'],
92+
'pattern' => $options['pattern'],
93+
'size' => null,
94+
'label_attr' => $options['label_attr'],
95+
'compound' => $form->getConfig()->getCompound(),
14796
));
14897
}
14998

@@ -169,6 +118,8 @@ public function finishView(FormView $view, FormInterface $form, array $options)
169118
*/
170119
public function setDefaultOptions(OptionsResolverInterface $resolver)
171120
{
121+
parent::setDefaultOptions($resolver);
122+
172123
// Derive "data_class" option from passed "data" object
173124
$dataClass = function (Options $options) {
174125
return isset($options['data']) && is_object($options['data']) ? get_class($options['data']) : null;
@@ -202,29 +153,23 @@ public function setDefaultOptions(OptionsResolverInterface $resolver)
202153
));
203154

204155
$resolver->setDefaults(array(
205-
'block_name' => null,
206156
'data_class' => $dataClass,
207157
'empty_data' => $emptyData,
208158
'trim' => true,
209159
'required' => true,
210160
'read_only' => false,
211-
'disabled' => false,
212161
'max_length' => null,
213162
'pattern' => null,
214163
'property_path' => null,
215164
'mapped' => true,
216165
'by_reference' => true,
217166
'error_bubbling' => $errorBubbling,
218-
'label' => null,
219-
'attr' => array(),
220167
'label_attr' => array(),
221168
'virtual' => false,
222169
'compound' => true,
223-
'translation_domain' => null,
224170
));
225171

226172
$resolver->setAllowedTypes(array(
227-
'attr' => 'array',
228173
'label_attr' => 'array',
229174
));
230175
}

0 commit comments

Comments
 (0)