Skip to content

Commit

Permalink
[Form] Improved FormType::getDefaultOptions() to see default options …
Browse files Browse the repository at this point in the history
…defined in parent types

In turn, FormType::getParent() does not see default options anymore.
  • Loading branch information
webmozart committed Feb 7, 2012
1 parent b9facfc commit 88ef52d
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 19 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG-2.1.md
Expand Up @@ -202,6 +202,8 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c
* added options "add_method" and "remove_method" to collection and choice type
* forms now don't create an empty object anymore if they are completely
empty and not required. The empty value for such forms is null.
* FormType::getDefaultOptions() now sees default options defined by parent types
* [BC BREAK] FormType::getParent() does not see default options anymore

### HttpFoundation

Expand Down
19 changes: 19 additions & 0 deletions UPGRADE-2.1.md
Expand Up @@ -229,3 +229,22 @@ UPGRADE FROM 2.0 to 2.1
return false;
}
}

* The options passed to `getParent` of the form types don't contain default
options anymore

You should check with `isset` if options exist before checking their value.

Before:

public function getParent()
{
return 'single_text' === $options['widget'] ? 'text' : 'choice';
}

After:

public function getParent()
{
return isset($options['widget']) && 'single_text' === $options['widget'] ? 'text' : 'choice';
}
1 change: 0 additions & 1 deletion src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php
Expand Up @@ -50,7 +50,6 @@ public function getDefaultOptions(array $options)
'property' => null,
'query_builder' => null,
'loader' => null,
'choices' => null,
'group_by' => null,
);

Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
Expand Up @@ -149,7 +149,7 @@ public function getDefaultOptions(array $options)
'multiple' => false,
'expanded' => false,
'choice_list' => null,
'choices' => array(),
'choices' => null,
'preferred_choices' => array(),
'value_strategy' => ChoiceList::GENERATE,
'index_strategy' => ChoiceList::GENERATE,
Expand All @@ -166,7 +166,7 @@ public function getDefaultOptions(array $options)
*/
public function getParent(array $options)
{
return $options['expanded'] ? 'form' : 'field';
return isset($options['expanded']) && $options['expanded'] ? 'form' : 'field';
}

/**
Expand Down
Expand Up @@ -200,7 +200,7 @@ public function getAllowedOptionValues(array $options)
*/
public function getParent(array $options)
{
return 'single_text' === $options['widget'] ? 'field' : 'form';
return isset($options['widget']) && 'single_text' === $options['widget'] ? 'field' : 'form';
}

/**
Expand Down
Expand Up @@ -211,7 +211,7 @@ public function getAllowedOptionValues(array $options)
*/
public function getParent(array $options)
{
return 'single_text' === $options['widget'] ? 'field' : 'form';
return isset($options['widget']) && 'single_text' === $options['widget'] ? 'field' : 'form';
}

/**
Expand Down
Expand Up @@ -184,7 +184,7 @@ public function getAllowedOptionValues(array $options)
*/
public function getParent(array $options)
{
return 'single_text' === $options['widget'] ? 'field' : 'form';
return isset($options['widget']) && 'single_text' === $options['widget'] ? 'field' : 'form';
}

/**
Expand Down
Expand Up @@ -31,7 +31,7 @@ public function getDefaultOptions(array $options)
'value_strategy' => ChoiceList::COPY_CHOICE,
);

if (!isset($options['choice_list']) && !isset($options['choices'])) {
if (empty($options['choice_list']) && empty($options['choices'])) {
$defaultOptions['choices'] = self::getTimezones();
}

Expand Down
47 changes: 35 additions & 12 deletions src/Symfony/Component/Form/FormFactory.php
Expand Up @@ -213,16 +213,20 @@ public function createBuilder($type, $data = null, array $options = array(), For
*/
public function createNamedBuilder($type, $name, $data = null, array $options = array(), FormBuilder $parent = null)
{
$builder = null;
$types = array();
$knownOptions = array();
$passedOptions = array_keys($options);
$optionValues = array();

if (!array_key_exists('data', $options)) {
$options['data'] = $data;
}

$builder = null;
$types = array();
$defaultOptions = array();
$optionValues = array();
$passedOptions = $options;

// Bottom-up determination of the type hierarchy
// Start with the actual type and look for the parent type
// The complete hierarchy is saved in $types, the first entry being
// the root and the last entry being the leaf (the concrete type)
while (null !== $type) {
if ($type instanceof FormTypeInterface) {
if ($type->getName() == $type->getParent($options)) {
Expand All @@ -236,28 +240,47 @@ public function createNamedBuilder($type, $name, $data = null, array $options =
throw new UnexpectedTypeException($type, 'string or Symfony\Component\Form\FormTypeInterface');
}

$defaultOptions = $type->getDefaultOptions($options);
array_unshift($types, $type);

// getParent() cannot see default options set by this type nor
// default options set by parent types
// As a result, the options always have to be checked for
// existence with isset() before using them in this method.
$type = $type->getParent($options);
}

// Top-down determination of the options and default options
foreach ($types as $type) {
// Merge the default options of all types to an array of default
// options. Default options of children override default options
// of parents.
// Default options of ancestors are already visible in the $options
// array passed to the following methods.
$defaultOptions = array_replace($defaultOptions, $type->getDefaultOptions($options));
$optionValues = array_merge_recursive($optionValues, $type->getAllowedOptionValues($options));

foreach ($type->getExtensions() as $typeExtension) {
$defaultOptions = array_replace($defaultOptions, $typeExtension->getDefaultOptions($options));
$optionValues = array_merge_recursive($optionValues, $typeExtension->getAllowedOptionValues($options));
}

$options = array_replace($defaultOptions, $options);
$knownOptions = array_merge($knownOptions, array_keys($defaultOptions));
array_unshift($types, $type);
$type = $type->getParent($options);
// In each turn, the options are replaced by the combination of
// the currently known default options and the passed options.
// It is important to merge with $passedOptions and not with
// $options, otherwise default options of parents would override
// default options of child types.
$options = array_replace($defaultOptions, $passedOptions);
}

$type = end($types);
$knownOptions = array_keys($defaultOptions);
$diff = array_diff(self::$requiredOptions, $knownOptions);

if (count($diff) > 0) {
throw new TypeDefinitionException(sprintf('Type "%s" should support the option(s) "%s"', $type->getName(), implode('", "', $diff)));
}

$diff = array_diff($passedOptions, $knownOptions);
$diff = array_diff(array_keys($passedOptions), $knownOptions);

if (count($diff) > 1) {
throw new CreationException(sprintf('The options "%s" do not exist. Known options are: "%s"', implode('", "', $diff), implode('", "', $knownOptions)));
Expand Down

0 comments on commit 88ef52d

Please sign in to comment.