Skip to content

Commit

Permalink
[Form] Added FormTypeExtensionInterface
Browse files Browse the repository at this point in the history
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
webmozart committed Apr 22, 2011
1 parent 54e66c5 commit 1ce2db8
Show file tree
Hide file tree
Showing 22 changed files with 431 additions and 81 deletions.
Expand Up @@ -42,9 +42,25 @@ public function process(ContainerBuilder $container)

$container->getDefinition('form.extension')->replaceArgument(1, $types);

$typeExtensions = array();

foreach ($container->findTaggedServiceIds('form.type_extension') as $serviceId => $tag) {
$alias = isset($tag[0]['alias'])
? $tag[0]['alias']
: $serviceId;

if (!isset($typeExtensions[$alias])) {
$typeExtensions[$alias] = array();
}

$typeExtensions[$alias][] = $serviceId;
}

$container->getDefinition('form.extension')->replaceArgument(2, $typeExtensions);

// Find all services annotated with "form.type_guesser"
$guessers = array_keys($container->findTaggedServiceIds('form.type_guesser'));

$container->getDefinition('form.extension')->replaceArgument(2, $guessers);
$container->getDefinition('form.extension')->replaceArgument(3, $guessers);
}
}
20 changes: 15 additions & 5 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml
Expand Up @@ -42,6 +42,11 @@
-->
<argument type="collection" />
<!--
All services with tag "form.type_extension" are inserted here by
InitFormsPass
-->
<argument type="collection" />
<!--
All services with tag "form.type_guesser" are inserted here by
InitFormsPass
-->
Expand All @@ -68,7 +73,7 @@
<argument>%file.temporary_storage.directory%</argument>
</service>

<!-- FieldTypes -->
<!-- CoreExtension -->
<service id="form.type.field" class="Symfony\Component\Form\Extension\Core\Type\FieldType">
<tag name="form.type" alias="field" />
<argument type="service" id="validator" />
Expand All @@ -91,10 +96,6 @@
<service id="form.type.country" class="Symfony\Component\Form\Extension\Core\Type\CountryType">
<tag name="form.type" alias="country" />
</service>
<service id="form.type.csrf" class="Symfony\Component\Form\Extension\Csrf\Type\CsrfType">
<tag name="form.type" alias="csrf" />
<argument type="service" id="form.csrf_provider" />
</service>
<service id="form.type.date" class="Symfony\Component\Form\Extension\Core\Type\DateType">
<tag name="form.type" alias="date" />
</service>
Expand Down Expand Up @@ -153,6 +154,15 @@
<service id="form.type.url" class="Symfony\Component\Form\Extension\Core\Type\UrlType">
<tag name="form.type" alias="url" />
</service>

<!-- CsrfExtension -->
<service id="form.type.csrf" class="Symfony\Component\Form\Extension\Csrf\Type\CsrfType">
<tag name="form.type" alias="csrf" />
<argument type="service" id="form.csrf_provider" />
</service>
<service id="form.type_extension.csrf" class="Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension">
<tag name="form.type_extension" alias="form" />
</service>

</services>
</container>
69 changes: 60 additions & 9 deletions src/Symfony/Component/Form/AbstractExtension.php
Expand Up @@ -25,9 +25,9 @@ abstract class AbstractExtension implements FormExtensionInterface
private $types;

/**
* @var Boolean
* @var array
*/
private $typesLoaded = false;
private $typeExtensions;

/**
* @var FormTypeGuesserInterface
Expand All @@ -39,14 +39,23 @@ abstract class AbstractExtension implements FormExtensionInterface
*/
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();

Expand All @@ -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;
Expand All @@ -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) {
Expand Down
18 changes: 18 additions & 0 deletions src/Symfony/Component/Form/AbstractType.php
Expand Up @@ -13,6 +13,8 @@

abstract class AbstractType implements FormTypeInterface
{
private $extensions = array();

public function buildForm(FormBuilder $builder, array $options)
{
}
Expand Down Expand Up @@ -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;
}
}
32 changes: 32 additions & 0 deletions src/Symfony/Component/Form/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();
}
}
14 changes: 0 additions & 14 deletions src/Symfony/Component/Form/Extension/Core/Type/FormType.php
Expand Up @@ -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)
Expand All @@ -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
Expand Down
5 changes: 4 additions & 1 deletion src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php
Expand Up @@ -31,7 +31,10 @@ protected function loadTypes()
);
}

protected function loadTypeGuesser()
protected function loadTypeExtensions()
{
return array(
new Type\FormTypeCsrfExtension(),
);
}
}
@@ -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';
}
}
Expand Up @@ -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()
Expand Down

0 comments on commit 1ce2db8

Please sign in to comment.