Permalink
Browse files

[Form] Added FormTypeExtensionInterface

With implementations of this interface, existing types can be amended.
The Csrf extension, for example, now contains a class FormTypeCsrfExtension
that adds CSRF capabilities to the "form" type.

To register new type extensions in the DIC, tag them with "form.type_extension"
and the name of the extended type as alias.
  • Loading branch information...
1 parent fc5c541 commit 3e5fc0775d4bbae3e0ca6c07dc16c39480c3d421 @webmozart webmozart committed Apr 22, 2011
View
69 AbstractExtension.php
@@ -25,9 +25,9 @@
private $types;
/**
- * @var Boolean
+ * @var array
*/
- private $typesLoaded = false;
+ private $typeExtensions;
/**
* @var FormTypeGuesserInterface
@@ -39,14 +39,23 @@
*/
private $typeGuesserLoaded = false;
- abstract protected function loadTypes();
+ protected function loadTypes()
+ {
+ return array();
+ }
- abstract protected function loadTypeGuesser();
+ protected function loadTypeExtensions()
+ {
+ return array();
+ }
- private function initTypes()
+ protected function loadTypeGuesser()
{
- $this->typesLoaded = true;
+ return null;
+ }
+ private function initTypes()
+ {
$types = $this->loadTypes();
$typesByName = array();
@@ -61,6 +70,28 @@ private function initTypes()
$this->types = $typesByName;
}
+ private function initTypeExtensions()
+ {
+ $extensions = $this->loadTypeExtensions();
+ $extensionsByType = array();
+
+ foreach ($extensions as $extension) {
+ if (!$extension instanceof FormTypeExtensionInterface) {
+ throw new UnexpectedTypeException($extension, 'Symfony\Component\Form\FormTypeExtensionInterface');
+ }
+
+ $type = $extension->getExtendedType();
+
+ if (!isset($extensionsByType[$type])) {
+ $extensionsByType[$type] = array();
+ }
+
+ $extensionsByType[$type][] = $extension;
+ }
+
+ $this->typeExtensions = $extensionsByType;
+ }
+
private function initTypeGuesser()
{
$this->typeGuesserLoaded = true;
@@ -76,26 +107,46 @@ private function initTypeGuesser()
public function getType($name)
{
- if (!$this->typesLoaded) {
+ if (null === $this->types) {
$this->initTypes();
}
if (!isset($this->types[$name])) {
- throw new FormException(sprintf('The type "%s" can not be typesLoaded by this extension', $name));
+ throw new FormException(sprintf('The type "%s" can not be loaded by this extension', $name));
}
return $this->types[$name];
}
public function hasType($name)
{
- if (!$this->typesLoaded) {
+ if (null === $this->types) {
$this->initTypes();
}
return isset($this->types[$name]);
}
+ function getTypeExtensions($name)
+ {
+ if (null === $this->typeExtensions) {
+ $this->initTypeExtensions();
+ }
+
+ return isset($this->typeExtensions[$name])
+ ? $this->typeExtensions[$name]
+ : array();
+ }
+
+ function hasTypeExtensions($name)
+ {
+ if (null === $this->typeExtensions) {
+ $this->initTypeExtensions();
+ }
+
+ return isset($this->typeExtensions[$name]) && count($this->typeExtensions[$name]) > 0;
+ }
+
public function getTypeGuesser()
{
if (!$this->typeGuesserLoaded) {
View
18 AbstractType.php
@@ -13,6 +13,8 @@
abstract class AbstractType implements FormTypeInterface
{
+ private $extensions = array();
+
public function buildForm(FormBuilder $builder, array $options)
{
}
@@ -46,4 +48,20 @@ public function getName()
return strtolower($matches[1]);
}
+
+ public function setExtensions(array $extensions)
+ {
+ foreach ($extensions as $extension) {
+ if (!$extension instanceof FormTypeExtensionInterface) {
+ throw new UnexpectedTypeException($extension, 'Symfony\Component\Form\FormTypeExtensionInterface');
+ }
+ }
+
+ $this->extensions = $extensions;
+ }
+
+ public function getExtensions()
+ {
+ return $this->extensions;
+ }
}
View
32 AbstractTypeExtension.php
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+abstract class AbstractTypeExtension implements FormTypeExtensionInterface
+{
+ public function buildForm(FormBuilder $builder, array $options)
+ {
+ }
+
+ public function buildView(FormView $view, FormInterface $form)
+ {
+ }
+
+ public function buildViewBottomUp(FormView $view, FormInterface $form)
+ {
+ }
+
+ public function getDefaultOptions(array $options)
+ {
+ return array();
+ }
+}
View
14 Extension/Core/Type/FormType.php
@@ -25,16 +25,6 @@ public function buildForm(FormBuilder $builder, array $options)
{
$builder->setAttribute('virtual', $options['virtual'])
->setDataMapper(new PropertyPathMapper($options['data_class']));
-
- if ($options['csrf_protection']) {
- $csrfOptions = array('page_id' => $options['csrf_page_id']);
-
- if ($options['csrf_provider']) {
- $csrfOptions['csrf_provider'] = $options['csrf_provider'];
- }
-
- $builder->add($options['csrf_field_name'], 'csrf', $csrfOptions);
- }
}
public function buildViewBottomUp(FormView $view, FormInterface $form)
@@ -54,10 +44,6 @@ public function buildViewBottomUp(FormView $view, FormInterface $form)
public function getDefaultOptions(array $options)
{
$defaultOptions = array(
- 'csrf_protection' => true,
- 'csrf_field_name' => '_token',
- 'csrf_provider' => null,
- 'csrf_page_id' => get_class($this),
'virtual' => false,
// Errors in forms bubble by default, so that form errors will
// end up as global errors in the root form
View
5 Extension/Csrf/CsrfExtension.php
@@ -31,7 +31,10 @@ protected function loadTypes()
);
}
- protected function loadTypeGuesser()
+ protected function loadTypeExtensions()
{
+ return array(
+ new Type\FormTypeCsrfExtension(),
+ );
}
}
View
46 Extension/Csrf/Type/FormTypeCsrfExtension.php
@@ -0,0 +1,46 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Csrf\Type;
+
+use Symfony\Component\Form\AbstractTypeExtension;
+use Symfony\Component\Form\FormBuilder;
+
+class FormTypeCsrfExtension extends AbstractTypeExtension
+{
+ public function buildForm(FormBuilder $builder, array $options)
+ {
+ if ($options['csrf_protection']) {
+ $csrfOptions = array('page_id' => $options['csrf_page_id']);
+
+ if ($options['csrf_provider']) {
+ $csrfOptions['csrf_provider'] = $options['csrf_provider'];
+ }
+
+ $builder->add($options['csrf_field_name'], 'csrf', $csrfOptions);
+ }
+ }
+
+ public function getDefaultOptions(array $options)
+ {
+ return array(
+ 'csrf_protection' => true,
+ 'csrf_field_name' => '_token',
+ 'csrf_provider' => null,
+ 'csrf_page_id' => get_class($this),
+ );
+ }
+
+ public function getExtendedType()
+ {
+ return 'form';
+ }
+}
View
34 Extension/DependencyInjection/DependencyInjectionExtension.php
@@ -28,25 +28,45 @@ class DependencyInjectionExtension implements FormExtensionInterface
private $guesserLoaded = false;
public function __construct(ContainerInterface $container,
- array $typeServiceIds, array $guesserServiceIds)
+ array $typeServiceIds, array $typeExtensionServiceIds,
+ array $guesserServiceIds)
{
$this->container = $container;
$this->typeServiceIds = $typeServiceIds;
+ $this->typeExtensionServiceIds = $typeExtensionServiceIds;
$this->guesserServiceIds = $guesserServiceIds;
}
- public function getType($identifier)
+ public function getType($name)
{
- if (!isset($this->typeServiceIds[$identifier])) {
- throw new \InvalidArgumentException(sprintf('The field type "%s" is not registered with the service container.', $identifier));
+ if (!isset($this->typeServiceIds[$name])) {
+ throw new \InvalidArgumentException(sprintf('The field type "%s" is not registered with the service container.', $name));
}
- return $this->container->get($this->typeServiceIds[$identifier]);
+ return $this->container->get($this->typeServiceIds[$name]);
}
- public function hasType($identifier)
+ public function hasType($name)
{
- return isset($this->typeServiceIds[$identifier]);
+ return isset($this->typeServiceIds[$name]);
+ }
+
+ public function getTypeExtensions($name)
+ {
+ $extensions = array();
+
+ if (isset($this->typeExtensionServiceIds[$name])) {
+ foreach ($this->typeExtensionServiceIds[$name] as $serviceId) {
+ $extensions[] = $this->container->get($serviceId);
+ }
+ }
+
+ return $extensions;
+ }
+
+ public function hasTypeExtensions($name)
+ {
+ return isset($this->typeExtensionServiceIds[$name]);
}
public function getTypeGuesser()
View
8 Form.php
@@ -844,6 +844,10 @@ public function createView(FormView $parent = null)
foreach ($types as $type) {
$type->buildView($view, $this);
+
+ foreach ($type->getExtensions() as $typeExtension) {
+ $typeExtension->buildView($view, $this);
+ }
}
foreach ($this->children as $key => $child) {
@@ -854,6 +858,10 @@ public function createView(FormView $parent = null)
foreach ($types as $type) {
$type->buildViewBottomUp($view, $this);
+
+ foreach ($type->getExtensions() as $typeExtension) {
+ $typeExtension->buildViewBottomUp($view, $this);
+ }
}
return $view;
View
4 FormExtensionInterface.php
@@ -17,5 +17,9 @@ function getType($name);
function hasType($name);
+ function getTypeExtensions($name);
+
+ function hasTypeExtensions($name);
+
function getTypeGuesser();
}
View
65 FormFactory.php
@@ -20,6 +20,8 @@ class FormFactory implements FormFactoryInterface
{
private $extensions = array();
+ private $types = array();
+
private $guesser;
public function __construct(array $extensions)
@@ -48,6 +50,46 @@ private function loadGuesser()
$this->guesser = new FormTypeGuesserChain($guessers);
}
+ public function getType($name)
+ {
+ $type = null;
+
+ if ($name instanceof FormTypeInterface) {
+ $type = $name;
+ $name = $type->getName();
+ }
+
+ 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));
+ }
+ }
+
+ $typeExtensions = array();
+
+ foreach ($this->extensions as $extension) {
+ $typeExtensions = array_merge(
+ $typeExtensions,
+ $extension->getTypeExtensions($name)
+ );
+ }
+
+ $type->setExtensions($typeExtensions);
+
+ $this->types[$name] = $type;
+ }
+
+ return $this->types[$name];
+ }
+
public function create($type, $data = null, array $options = array())
{
return $this->createBuilder($type, $data, $options)->getForm();
@@ -77,27 +119,22 @@ public function createNamedBuilder($type, $name, $data = null, array $options =
{
$builder = null;
$types = array();
+ $typeExtensions = array();
$knownOptions = array();
$passedOptions = array_keys($options);
while (null !== $type) {
- if (!$type instanceof FormTypeInterface) {
- foreach ($this->extensions as $extension) {
- if ($extension->hasType($type)) {
- $type = $extension->getType($type);
- break;
- }
- }
+ $type = $this->getType($type);
- if (!$type) {
- throw new FormException(sprintf('Could not load type "%s"', $type));
- }
+ $defaultOptions = $type->getDefaultOptions($options);
+
+ foreach ($type->getExtensions() as $typeExtension) {
+ $defaultOptions = array_merge($defaultOptions, $typeExtension->getDefaultOptions($options));
}
- array_unshift($types, $type);
- $defaultOptions = $type->getDefaultOptions($options);
$options = array_merge($defaultOptions, $options);
$knownOptions = array_merge($knownOptions, array_keys($defaultOptions));
+ array_unshift($types, $type);
$type = $type->getParent($options);
}
@@ -117,6 +154,10 @@ public function createNamedBuilder($type, $name, $data = null, array $options =
foreach ($types as $type) {
$type->buildForm($builder, $options);
+
+ foreach ($type->getExtensions() as $typeExtension) {
+ $typeExtension->buildForm($builder, $options);
+ }
}
if (null !== $data) {
View
25 FormTypeExtensionInterface.php
@@ -0,0 +1,25 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+interface FormTypeExtensionInterface
+{
+ function buildForm(FormBuilder $builder, array $options);
+
+ function buildView(FormView $view, FormInterface $form);
+
+ function buildViewBottomUp(FormView $view, FormInterface $form);
+
+ function getDefaultOptions(array $options);
+
+ function getExtendedType();
+}
View
4 FormTypeInterface.php
@@ -26,4 +26,8 @@ function getDefaultOptions(array $options);
function getParent(array $options);
function getName();
+
+ function setExtensions(array $extensions);
+
+ function getExtensions();
}

0 comments on commit 3e5fc07

Please sign in to comment.