Skip to content

Commit

Permalink
[Form] Improved test coverage of FormFactory and improved error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
webmozart committed May 13, 2011
1 parent fdd1825 commit 21013b9
Show file tree
Hide file tree
Showing 10 changed files with 527 additions and 68 deletions.
21 changes: 21 additions & 0 deletions src/Symfony/Component/Form/Exception/CreationException.php
@@ -0,0 +1,21 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Form\Exception;

/**
* Thrown when a form could not be constructed by a FormFactory
*
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
*/
class CreationException extends FormException
{
}
21 changes: 21 additions & 0 deletions src/Symfony/Component/Form/Exception/TypeDefinitionException.php
@@ -0,0 +1,21 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Form\Exception;

/**
* Thrown when a form type is configured incorrectly
*
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
*/
class TypeDefinitionException extends FormException
{
}
114 changes: 84 additions & 30 deletions src/Symfony/Component/Form/FormFactory.php
Expand Up @@ -13,9 +13,17 @@

use Symfony\Component\Form\Exception\FormException;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Exception\TypeDefinitionException;
use Symfony\Component\Form\Exception\CreationException;

class FormFactory implements FormFactoryInterface
{
private static $requiredOptions = array(
'data',
'required',
'max_length',
);

private $extensions = array();

private $types = array();
Expand All @@ -40,41 +48,36 @@ public function __construct(array $extensions)
$this->extensions = $extensions;
}

public function getType($name)
public function hasType($name)
{
$type = null;
if (isset($this->types[$name])) {
return true;
}

if ($name instanceof FormTypeInterface) {
$type = $name;
$name = $type->getName();
try {
$this->loadType($name);
} catch (FormException $e) {
return false;
}

if (!isset($this->types[$name])) {
if (!$type) {
foreach ($this->extensions as $extension) {
if ($extension->hasType($name)) {
$type = $extension->getType($name);
break;
}
}

if (!$type) {
throw new FormException(sprintf('Could not load type "%s"', $name));
}
}
return true;
}

$typeExtensions = array();
public function addType(FormTypeInterface $type)
{
$this->loadTypeExtensions($type);

foreach ($this->extensions as $extension) {
$typeExtensions = array_merge(
$typeExtensions,
$extension->getTypeExtensions($name)
);
}
$this->types[$type->getName()] = $type;
}

$type->setExtensions($typeExtensions);
public function getType($name)
{
if (!is_string($name)) {
throw new UnexpectedTypeException($name, 'string');
}

$this->types[$name] = $type;
if (!isset($this->types[$name])) {
$this->loadType($name);
}

return $this->types[$name];
Expand Down Expand Up @@ -117,7 +120,11 @@ public function createNamedBuilder($type, $name, $data = null, array $options =
}

while (null !== $type) {
$type = $this->getType($type);
if ($type instanceof FormTypeInterface) {
$this->addType($type);
} else {
$type = $this->getType($type);
}

$defaultOptions = $type->getDefaultOptions($options);

Expand All @@ -131,17 +138,30 @@ public function createNamedBuilder($type, $name, $data = null, array $options =
$type = $type->getParent($options);
}

$type = end($types);
$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);

if (count($diff) > 1) {
throw new CreationException(sprintf('The options "%s" do not exist', implode('", "', $diff)));
}

if (count($diff) > 0) {
throw new FormException(sprintf('The options "%s" do not exist', implode('", "', $diff)));
throw new CreationException(sprintf('The option "%s" does not exist', $diff[0]));
}

for ($i = 0, $l = count($types); $i < $l && !$builder; ++$i) {
$builder = $types[$i]->createBuilder($name, $this, $options);
}

// TODO check if instance exists
if (!$builder) {
throw new TypeDefinitionException(sprintf('Type "%s" or any of its parents should return a FormBuilder instance from createBuilder()', $type->getName()));
}

$builder->setTypes($types);

Expand Down Expand Up @@ -198,4 +218,38 @@ private function loadGuesser()

$this->guesser = new FormTypeGuesserChain($guessers);
}

private function loadType($name)
{
$type = null;

foreach ($this->extensions as $extension) {
if ($extension->hasType($name)) {
$type = $extension->getType($name);
break;
}
}

if (!$type) {
throw new FormException(sprintf('Could not load type "%s"', $name));
}

$this->loadTypeExtensions($type);

$this->types[$name] = $type;
}

private function loadTypeExtensions(FormTypeInterface $type)
{
$typeExtensions = array();

foreach ($this->extensions as $extension) {
$typeExtensions = array_merge(
$typeExtensions,
$extension->getTypeExtensions($type->getName())
);
}

$type->setExtensions($typeExtensions);
}
}
4 changes: 4 additions & 0 deletions src/Symfony/Component/Form/FormFactoryInterface.php
Expand Up @@ -26,4 +26,8 @@ function createNamedBuilder($type, $name, $data = null, array $options = array()
function createBuilderForProperty($class, $property, $data = null, array $options = array());

function getType($name);

function hasType($name);

function addType(FormTypeInterface $type);
}
36 changes: 6 additions & 30 deletions tests/Symfony/Tests/Component/Form/AbstractExtensionTest.php
Expand Up @@ -17,53 +17,29 @@
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormBuilder;
use Symfony\Tests\Component\Form\Fixtures\FooType;

class AbstractExtensionTest extends \PHPUnit_Framework_TestCase
{
public function testHasType()
{
$loader = new TestExtension();
$loader = new ConcreteExtension();
$this->assertTrue($loader->hasType('foo'));
$this->assertFalse($loader->hasType('bar'));
}

public function testGetType()
{
$loader = new TestExtension(array($type));
$this->assertInstanceOf(__NAMESPACE__.'\TestType', $loader->getType('foo'));
$this->assertSame($loader->getType('foo'), $loader->getType('foo'));
$loader = new ConcreteExtension();
$this->assertTrue($loader->getType('foo') instanceof FooType);
}
}

class TestType implements FormTypeInterface
{
public function getName()
{
return 'foo';
}

function buildForm(FormBuilder $builder, array $options) {}

function buildView(FormView $view, FormInterface $form) {}

function buildViewBottomUp(FormView $view, FormInterface $form) {}

function createBuilder($name, FormFactoryInterface $factory, array $options) {}

function getDefaultOptions(array $options) {}

function getParent(array $options) {}

function setExtensions(array $extensions) {}

function getExtensions() {}
}

class TestExtension extends AbstractExtension
class ConcreteExtension extends AbstractExtension
{
protected function loadTypes()
{
return array(new TestType());
return array(new FooType());
}

protected function loadTypeGuesser()
Expand Down
41 changes: 41 additions & 0 deletions tests/Symfony/Tests/Component/Form/Fixtures/FooType.php
@@ -0,0 +1,41 @@
<?php

namespace Symfony\Tests\Component\Form\Fixtures;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;

class FooType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->setAttribute('foo', 'x');
$builder->setAttribute('data_option', $options['data']);
}

public function getName()
{
return 'foo';
}

public function createBuilder($name, FormFactoryInterface $factory, array $options)
{
return new FormBuilder($name, $factory, new EventDispatcher());
}

public function getDefaultOptions(array $options)
{
return array(
'data' => null,
'required' => false,
'max_length' => null,
);
}

public function getParent(array $options)
{
return null;
}
}
@@ -0,0 +1,19 @@
<?php

namespace Symfony\Tests\Component\Form\Fixtures;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilder;

class FooTypeBarExtension extends AbstractTypeExtension
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->setAttribute('bar', 'x');
}

public function getExtendedType()
{
return 'foo';
}
}
@@ -0,0 +1,19 @@
<?php

namespace Symfony\Tests\Component\Form\Fixtures;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilder;

class FooTypeBazExtension extends AbstractTypeExtension
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->setAttribute('baz', 'x');
}

public function getExtendedType()
{
return 'foo';
}
}

0 comments on commit 21013b9

Please sign in to comment.