Skip to content

Commit 40ac080

Browse files
Merge branch '3.2'
* 3.2: (51 commits) [FrameworkBundle] [Workflow] Fix service marking store configuration Fix merge [Validator] add class name to the cache key [Serializer] Remove AbstractObjectNormalizer::isAttributeToNormalize Throw less misleading exception when property access not found [Twig] Fix deprecations with Twig 1.29 [FrameworkBundle] Fix validation cache warmer with failing or missing classes Fixed typo [FrameworkBundle] Removed the kernel.debug parameter from the cache pool namespace seed Fix email address fix the docblock in regard to the role argument [Bridge\Twig] Trigger deprecation when using FormExtension::$renderer Don't use the "app" global variable in the profiler [VarDumper] fix tests when xdebug is enabled Fix merge FIXED NON EXISTING TYPE DECLARATION [Form] Add failing test for data collector bug [Cache] Fix dumping SplDoublyLinkedList iter mode [Form] Fix FormDataCollector Ignore missing 'debug.file_link_formatter' service in Debug and Twig bundles ...
2 parents 13f41fd + be62015 commit 40ac080

File tree

5 files changed

+188
-29
lines changed

5 files changed

+188
-29
lines changed

Extension/Core/Type/FileType.php

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,37 @@
1212
namespace Symfony\Component\Form\Extension\Core\Type;
1313

1414
use Symfony\Component\Form\AbstractType;
15+
use Symfony\Component\Form\FormBuilderInterface;
16+
use Symfony\Component\Form\FormEvent;
17+
use Symfony\Component\Form\FormEvents;
1518
use Symfony\Component\Form\FormInterface;
1619
use Symfony\Component\Form\FormView;
20+
use Symfony\Component\OptionsResolver\Options;
1721
use Symfony\Component\OptionsResolver\OptionsResolver;
1822

1923
class FileType extends AbstractType
2024
{
25+
/**
26+
* {@inheritdoc}
27+
*/
28+
public function buildForm(FormBuilderInterface $builder, array $options)
29+
{
30+
if ($options['multiple']) {
31+
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
32+
$form = $event->getForm();
33+
$data = $event->getData();
34+
35+
// submitted data for an input file (not required) without choosing any file
36+
if (array(null) === $data) {
37+
$emptyData = $form->getConfig()->getEmptyData();
38+
39+
$data = is_callable($emptyData) ? call_user_func($emptyData, $form, $data) : $emptyData;
40+
$event->setData($data);
41+
}
42+
});
43+
}
44+
}
45+
2146
/**
2247
* {@inheritdoc}
2348
*/
@@ -39,20 +64,26 @@ public function buildView(FormView $view, FormInterface $form, array $options)
3964
*/
4065
public function finishView(FormView $view, FormInterface $form, array $options)
4166
{
42-
$view
43-
->vars['multipart'] = true
44-
;
67+
$view->vars['multipart'] = true;
4568
}
4669

4770
/**
4871
* {@inheritdoc}
4972
*/
5073
public function configureOptions(OptionsResolver $resolver)
5174
{
75+
$dataClass = function (Options $options) {
76+
return $options['multiple'] ? null : 'Symfony\Component\HttpFoundation\File\File';
77+
};
78+
79+
$emptyData = function (Options $options) {
80+
return $options['multiple'] ? array() : null;
81+
};
82+
5283
$resolver->setDefaults(array(
5384
'compound' => false,
54-
'data_class' => 'Symfony\Component\HttpFoundation\File\File',
55-
'empty_data' => null,
85+
'data_class' => $dataClass,
86+
'empty_data' => $emptyData,
5687
'multiple' => false,
5788
));
5889
}

Extension/DataCollector/FormDataCollector.php

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
use Symfony\Component\HttpFoundation\Response;
1818
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
1919
use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter;
20+
use Symfony\Component\Validator\ConstraintViolationInterface;
2021
use Symfony\Component\VarDumper\Caster\Caster;
2122
use Symfony\Component\VarDumper\Caster\ClassStub;
23+
use Symfony\Component\VarDumper\Cloner\ClonerInterface;
2224
use Symfony\Component\VarDumper\Cloner\Data;
2325
use Symfony\Component\VarDumper\Cloner\Stub;
2426
use Symfony\Component\VarDumper\Cloner\VarCloner;
@@ -207,19 +209,15 @@ public function collectViewVariables(FormView $view)
207209
*/
208210
public function buildPreliminaryFormTree(FormInterface $form)
209211
{
210-
$this->data['forms'][$form->getName()] = array();
211-
212-
$this->recursiveBuildPreliminaryFormTree($form, $this->data['forms'][$form->getName()], $this->data['forms_by_hash']);
212+
$this->data['forms'][$form->getName()] = &$this->recursiveBuildPreliminaryFormTree($form, $this->data['forms_by_hash']);
213213
}
214214

215215
/**
216216
* {@inheritdoc}
217217
*/
218218
public function buildFinalFormTree(FormInterface $form, FormView $view)
219219
{
220-
$this->data['forms'][$form->getName()] = array();
221-
222-
$this->recursiveBuildFinalFormTree($form, $view, $this->data['forms'][$form->getName()], $this->data['forms_by_hash']);
220+
$this->data['forms'][$form->getName()] = &$this->recursiveBuildFinalFormTree($form, $view, $this->data['forms_by_hash']);
223221
}
224222

225223
/**
@@ -256,7 +254,7 @@ public function serialize()
256254
case 'resolved_options':
257255
case 'default_data':
258256
case 'submitted_data':
259-
if ($v) {
257+
if ($v && is_array($v)) {
260258
$form[$k] = array_map($cloneVar, $v);
261259
}
262260
break;
@@ -352,26 +350,25 @@ protected function cloneVar($var, $isClass = false)
352350
return $cache = $this->cloner->cloneVar($var);
353351
}
354352

355-
private function recursiveBuildPreliminaryFormTree(FormInterface $form, &$output, array &$outputByHash)
353+
private function &recursiveBuildPreliminaryFormTree(FormInterface $form, array &$outputByHash)
356354
{
357355
$hash = spl_object_hash($form);
358356

357+
$output = &$outputByHash[$hash];
359358
$output = isset($this->dataByForm[$hash])
360359
? $this->dataByForm[$hash]
361360
: array();
362361

363-
$outputByHash[$hash] = &$output;
364-
365362
$output['children'] = array();
366363

367364
foreach ($form as $name => $child) {
368-
$output['children'][$name] = array();
369-
370-
$this->recursiveBuildPreliminaryFormTree($child, $output['children'][$name], $outputByHash);
365+
$output['children'][$name] = &$this->recursiveBuildPreliminaryFormTree($child, $outputByHash);
371366
}
367+
368+
return $output;
372369
}
373370

374-
private function recursiveBuildFinalFormTree(FormInterface $form = null, FormView $view, &$output, array &$outputByHash)
371+
private function &recursiveBuildFinalFormTree(FormInterface $form = null, FormView $view, array &$outputByHash)
375372
{
376373
$viewHash = spl_object_hash($view);
377374
$formHash = null;
@@ -384,6 +381,9 @@ private function recursiveBuildFinalFormTree(FormInterface $form = null, FormVie
384381
// corresponding FormInterface instance for its view in a different way
385382
$formHash = $this->formsByView[$viewHash];
386383
}
384+
if (null !== $formHash) {
385+
$output = &$outputByHash[$formHash];
386+
}
387387

388388
$output = isset($this->dataByView[$viewHash])
389389
? $this->dataByView[$viewHash]
@@ -396,8 +396,6 @@ private function recursiveBuildFinalFormTree(FormInterface $form = null, FormVie
396396
? $this->dataByForm[$formHash]
397397
: array()
398398
);
399-
400-
$outputByHash[$formHash] = &$output;
401399
}
402400

403401
$output['children'] = array();
@@ -409,9 +407,9 @@ private function recursiveBuildFinalFormTree(FormInterface $form = null, FormVie
409407
? $form->get($name)
410408
: null;
411409

412-
$output['children'][$name] = array();
413-
414-
$this->recursiveBuildFinalFormTree($childForm, $childView, $output['children'][$name], $outputByHash);
410+
$output['children'][$name] = &$this->recursiveBuildFinalFormTree($childForm, $childView, $outputByHash);
415411
}
412+
413+
return $output;
416414
}
417415
}

Extension/DataCollector/FormDataExtractor.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@
2323
*/
2424
class FormDataExtractor implements FormDataExtractorInterface
2525
{
26-
/**
27-
* @var VarCloner
28-
*/
29-
private $cloner;
30-
3126
/**
3227
* Constructs a new data extractor.
3328
*/

Tests/Extension/Core/Type/FileTypeTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,33 @@ public function testSubmitEmpty()
4444
$this->assertNull($form->getData());
4545
}
4646

47+
public function testSubmitEmptyMultiple()
48+
{
49+
$form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FileType', null, array(
50+
'multiple' => true,
51+
))->getForm();
52+
53+
// submitted data when an input file is uploaded without choosing any file
54+
$form->submit(array(null));
55+
56+
$this->assertSame(array(), $form->getData());
57+
}
58+
59+
public function testSetDataMultiple()
60+
{
61+
$form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FileType', null, array(
62+
'multiple' => true,
63+
))->getForm();
64+
65+
$data = array(
66+
$this->createUploadedFileMock('abcdef', 'first.jpg', true),
67+
$this->createUploadedFileMock('zyxwvu', 'second.jpg', true),
68+
);
69+
70+
$form->setData($data);
71+
$this->assertSame($data, $form->getData());
72+
}
73+
4774
public function testSubmitMultiple()
4875
{
4976
$form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FileType', null, array(

Tests/Extension/DataCollector/FormDataCollectorTest.php

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,114 @@ public function testBuildFinalFormTree()
342342
), $this->dataCollector->getData());
343343
}
344344

345+
public function testSerializeWithFormAddedMultipleTimes()
346+
{
347+
$form1 = $this->createForm('form1');
348+
$form2 = $this->createForm('form2');
349+
$child1 = $this->createForm('child1');
350+
351+
$form1View = new FormView();
352+
$form2View = new FormView();
353+
$child1View = new FormView();
354+
$child1View->vars['is_selected'] = function ($choice, array $values) {
355+
return in_array($choice, $values, true);
356+
};
357+
358+
$form1->add($child1);
359+
$form2->add($child1);
360+
361+
$form1View->children['child1'] = $child1View;
362+
$form2View->children['child1'] = $child1View;
363+
364+
$this->dataExtractor->expects($this->at(0))
365+
->method('extractConfiguration')
366+
->with($form1)
367+
->will($this->returnValue(array('config' => 'foo')));
368+
$this->dataExtractor->expects($this->at(1))
369+
->method('extractConfiguration')
370+
->with($child1)
371+
->will($this->returnValue(array('config' => 'bar')));
372+
373+
$this->dataExtractor->expects($this->at(2))
374+
->method('extractDefaultData')
375+
->with($form1)
376+
->will($this->returnValue(array('default_data' => 'foo')));
377+
$this->dataExtractor->expects($this->at(3))
378+
->method('extractDefaultData')
379+
->with($child1)
380+
->will($this->returnValue(array('default_data' => 'bar')));
381+
382+
$this->dataExtractor->expects($this->at(4))
383+
->method('extractSubmittedData')
384+
->with($form1)
385+
->will($this->returnValue(array('submitted_data' => 'foo')));
386+
$this->dataExtractor->expects($this->at(5))
387+
->method('extractSubmittedData')
388+
->with($child1)
389+
->will($this->returnValue(array('submitted_data' => 'bar')));
390+
391+
$this->dataExtractor->expects($this->at(6))
392+
->method('extractViewVariables')
393+
->with($form1View)
394+
->will($this->returnValue(array('view_vars' => 'foo')));
395+
396+
$this->dataExtractor->expects($this->at(7))
397+
->method('extractViewVariables')
398+
->with($child1View)
399+
->will($this->returnValue(array('view_vars' => $child1View->vars)));
400+
401+
$this->dataExtractor->expects($this->at(8))
402+
->method('extractConfiguration')
403+
->with($form2)
404+
->will($this->returnValue(array('config' => 'foo')));
405+
$this->dataExtractor->expects($this->at(9))
406+
->method('extractConfiguration')
407+
->with($child1)
408+
->will($this->returnValue(array('config' => 'bar')));
409+
410+
$this->dataExtractor->expects($this->at(10))
411+
->method('extractDefaultData')
412+
->with($form2)
413+
->will($this->returnValue(array('default_data' => 'foo')));
414+
$this->dataExtractor->expects($this->at(11))
415+
->method('extractDefaultData')
416+
->with($child1)
417+
->will($this->returnValue(array('default_data' => 'bar')));
418+
419+
$this->dataExtractor->expects($this->at(12))
420+
->method('extractSubmittedData')
421+
->with($form2)
422+
->will($this->returnValue(array('submitted_data' => 'foo')));
423+
$this->dataExtractor->expects($this->at(13))
424+
->method('extractSubmittedData')
425+
->with($child1)
426+
->will($this->returnValue(array('submitted_data' => 'bar')));
427+
428+
$this->dataExtractor->expects($this->at(14))
429+
->method('extractViewVariables')
430+
->with($form2View)
431+
->will($this->returnValue(array('view_vars' => 'foo')));
432+
433+
$this->dataExtractor->expects($this->at(15))
434+
->method('extractViewVariables')
435+
->with($child1View)
436+
->will($this->returnValue(array('view_vars' => $child1View->vars)));
437+
438+
$this->dataCollector->collectConfiguration($form1);
439+
$this->dataCollector->collectDefaultData($form1);
440+
$this->dataCollector->collectSubmittedData($form1);
441+
$this->dataCollector->collectViewVariables($form1View);
442+
$this->dataCollector->buildFinalFormTree($form1, $form1View);
443+
444+
$this->dataCollector->collectConfiguration($form2);
445+
$this->dataCollector->collectDefaultData($form2);
446+
$this->dataCollector->collectSubmittedData($form2);
447+
$this->dataCollector->collectViewVariables($form2View);
448+
$this->dataCollector->buildFinalFormTree($form2, $form2View);
449+
450+
$this->dataCollector->serialize();
451+
}
452+
345453
public function testFinalFormReliesOnFormViewStructure()
346454
{
347455
$this->form->add($child1 = $this->createForm('first'));

0 commit comments

Comments
 (0)