Merge branch 'form'
* form: (291 commits) [FrameworkBundle] updated method call [Form] Removing excess option in the TimezoneType [FrameworkBundle] Adding check for invalid form type for better exception message [TwigBundle] Removing dbug text in form template [Form] Removed obsolete code in TextType [Form] fixed translations escaping [Form] Adding a check that the choice_list option on the ChoiceType implements the ChoiceListInterface. [Form] added support for groups in form validation (when using array data) [Form] fixed error bubbling for choices when expanded is true [Form] added a unit test [Form] Removed obsolete view variables [Form] Renamed ChoiceUtil to FormUtil and gave its methods more general names [Form] Changed separator for Twig blocks from double underscore to single underscore to match the PHP template separator [Form] Removed StripTagsListenerTest [Form] Removed StripTagsListener. Its implementation is insufficient and needs to be replaced by a better one. [Form] added a way to specify the form constraint when building the form (useful if you work with arrays instead of objects) [Form] Added test for 'email' type and fixed a few bugs [Form] Removed obsolete constraints from validation.xml Revert "[Form] removed validation.xml file (not used anymore)" Added html5 email input to the forms ...
This commit is contained in:
commit
0069a70e42
275
src/Symfony/Bridge/Doctrine/Form/ChoiceList/EntityChoiceList.php
Normal file
275
src/Symfony/Bridge/Doctrine/Form/ChoiceList/EntityChoiceList.php
Normal file
@ -0,0 +1,275 @@
|
||||
<?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\Bridge\Doctrine\Form\ChoiceList;
|
||||
|
||||
use Symfony\Component\Form\Util\PropertyPath;
|
||||
use Symfony\Component\Form\Exception\FormException;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\ORM\NoResultException;
|
||||
|
||||
class EntityChoiceList extends ArrayChoiceList
|
||||
{
|
||||
/**
|
||||
* @var Doctrine\ORM\EntityManager
|
||||
*/
|
||||
private $em;
|
||||
|
||||
/**
|
||||
* @var Doctrine\ORM\Mapping\ClassMetadata
|
||||
*/
|
||||
private $class;
|
||||
|
||||
/**
|
||||
* The entities from which the user can choose
|
||||
*
|
||||
* This array is either indexed by ID (if the ID is a single field)
|
||||
* or by key in the choices array (if the ID consists of multiple fields)
|
||||
*
|
||||
* This property is initialized by initializeChoices(). It should only
|
||||
* be accessed through getEntity() and getEntities().
|
||||
*
|
||||
* @var Collection
|
||||
*/
|
||||
private $entities = array();
|
||||
|
||||
/**
|
||||
* Contains the query builder that builds the query for fetching the
|
||||
* entities
|
||||
*
|
||||
* This property should only be accessed through queryBuilder.
|
||||
*
|
||||
* @var Doctrine\ORM\QueryBuilder
|
||||
*/
|
||||
private $queryBuilder;
|
||||
|
||||
/**
|
||||
* The fields of which the identifier of the underlying class consists
|
||||
*
|
||||
* This property should only be accessed through identifier.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $identifier = array();
|
||||
|
||||
/**
|
||||
* A cache for \ReflectionProperty instances for the underlying class
|
||||
*
|
||||
* This property should only be accessed through getReflProperty().
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $reflProperties = array();
|
||||
|
||||
/**
|
||||
* A cache for the UnitOfWork instance of Doctrine
|
||||
*
|
||||
* @var Doctrine\ORM\UnitOfWork
|
||||
*/
|
||||
private $unitOfWork;
|
||||
|
||||
private $propertyPath;
|
||||
|
||||
public function __construct(EntityManager $em, $class, $property = null, $queryBuilder = null, $choices = array())
|
||||
{
|
||||
// If a query builder was passed, it must be a closure or QueryBuilder
|
||||
// instance
|
||||
if (!(null === $queryBuilder || $queryBuilder instanceof QueryBuilder || $queryBuilder instanceof \Closure)) {
|
||||
throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder or \Closure');
|
||||
}
|
||||
|
||||
if ($queryBuilder instanceof \Closure) {
|
||||
$queryBuilder = $queryBuilder($em->getRepository($class));
|
||||
|
||||
if (!$queryBuilder instanceof QueryBuilder) {
|
||||
throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
|
||||
}
|
||||
}
|
||||
|
||||
$this->em = $em;
|
||||
$this->class = $class;
|
||||
$this->queryBuilder = $queryBuilder;
|
||||
$this->unitOfWork = $em->getUnitOfWork();
|
||||
$this->identifier = $em->getClassMetadata($class)->getIdentifierFieldNames();
|
||||
|
||||
// The propery option defines, which property (path) is used for
|
||||
// displaying entities as strings
|
||||
if ($property) {
|
||||
$this->propertyPath = new PropertyPath($property);
|
||||
}
|
||||
|
||||
parent::__construct($choices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the choices and returns them
|
||||
*
|
||||
* The choices are generated from the entities. If the entities have a
|
||||
* composite identifier, the choices are indexed using ascending integers.
|
||||
* Otherwise the identifiers are used as indices.
|
||||
*
|
||||
* If the entities were passed in the "choices" option, this method
|
||||
* does not have any significant overhead. Otherwise, if a query builder
|
||||
* was passed in the "query_builder" option, this builder is now used
|
||||
* to construct a query which is executed. In the last case, all entities
|
||||
* for the underlying class are fetched from the repository.
|
||||
*
|
||||
* If the option "property" was passed, the property path in that option
|
||||
* is used as option values. Otherwise this method tries to convert
|
||||
* objects to strings using __toString().
|
||||
*
|
||||
* @return array An array of choices
|
||||
*/
|
||||
protected function load()
|
||||
{
|
||||
parent::load();
|
||||
|
||||
if ($this->choices) {
|
||||
$entities = $this->choices;
|
||||
} else if ($qb = $this->queryBuilder) {
|
||||
$entities = $qb->getQuery()->execute();
|
||||
} else {
|
||||
$entities = $this->em->getRepository($this->class)->findAll();
|
||||
}
|
||||
|
||||
$propertyPath = null;
|
||||
$this->choices = array();
|
||||
$this->entities = array();
|
||||
|
||||
foreach ($entities as $key => $entity) {
|
||||
if ($this->propertyPath) {
|
||||
// If the property option was given, use it
|
||||
$value = $this->propertyPath->getValue($entity);
|
||||
} else {
|
||||
// Otherwise expect a __toString() method in the entity
|
||||
$value = (string)$entity;
|
||||
}
|
||||
|
||||
if (count($this->identifier) > 1) {
|
||||
// When the identifier consists of multiple field, use
|
||||
// naturally ordered keys to refer to the choices
|
||||
$this->choices[$key] = $value;
|
||||
$this->entities[$key] = $entity;
|
||||
} else {
|
||||
// When the identifier is a single field, index choices by
|
||||
// entity ID for performance reasons
|
||||
$id = current($this->getIdentifierValues($entity));
|
||||
$this->choices[$id] = $value;
|
||||
$this->entities[$id] = $entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getIdentifier()
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the according entities for the choices
|
||||
*
|
||||
* If the choices were not initialized, they are initialized now. This
|
||||
* is an expensive operation, except if the entities were passed in the
|
||||
* "choices" option.
|
||||
*
|
||||
* @return array An array of entities
|
||||
*/
|
||||
public function getEntities()
|
||||
{
|
||||
if (!$this->loaded) {
|
||||
$this->load();
|
||||
}
|
||||
|
||||
return $this->entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity for the given key
|
||||
*
|
||||
* If the underlying entities have composite identifiers, the choices
|
||||
* are intialized. The key is expected to be the index in the choices
|
||||
* array in this case.
|
||||
*
|
||||
* If they have single identifiers, they are either fetched from the
|
||||
* internal entity cache (if filled) or loaded from the database.
|
||||
*
|
||||
* @param string $key The choice key (for entities with composite
|
||||
* identifiers) or entity ID (for entities with single
|
||||
* identifiers)
|
||||
* @return object The matching entity
|
||||
*/
|
||||
public function getEntity($key)
|
||||
{
|
||||
if (!$this->loaded) {
|
||||
$this->load();
|
||||
}
|
||||
|
||||
try {
|
||||
if (count($this->identifier) > 1) {
|
||||
// $key is a collection index
|
||||
$entities = $this->getEntities();
|
||||
return isset($entities[$key]) ? $entities[$key] : null;
|
||||
} else if ($this->entities) {
|
||||
return isset($this->entities[$key]) ? $this->entities[$key] : null;
|
||||
} else if ($qb = $this->queryBuilder) {
|
||||
// should we clone the builder?
|
||||
$alias = $qb->getRootAlias();
|
||||
$where = $qb->expr()->eq($alias.'.'.current($this->identifier), $key);
|
||||
|
||||
return $qb->andWhere($where)->getQuery()->getSingleResult();
|
||||
}
|
||||
|
||||
return $this->em->find($this->class, $key);
|
||||
} catch (NoResultException $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the \ReflectionProperty instance for a property of the
|
||||
* underlying class
|
||||
*
|
||||
* @param string $property The name of the property
|
||||
* @return \ReflectionProperty The reflection instsance
|
||||
*/
|
||||
private function getReflProperty($property)
|
||||
{
|
||||
if (!isset($this->reflProperties[$property])) {
|
||||
$this->reflProperties[$property] = new \ReflectionProperty($this->class, $property);
|
||||
$this->reflProperties[$property]->setAccessible(true);
|
||||
}
|
||||
|
||||
return $this->reflProperties[$property];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values of the identifier fields of an entity
|
||||
*
|
||||
* Doctrine must know about this entity, that is, the entity must already
|
||||
* be persisted or added to the identity map before. Otherwise an
|
||||
* exception is thrown.
|
||||
*
|
||||
* @param object $entity The entity for which to get the identifier
|
||||
* @throws FormException If the entity does not exist in Doctrine's
|
||||
* identity map
|
||||
*/
|
||||
public function getIdentifierValues($entity)
|
||||
{
|
||||
if (!$this->unitOfWork->isInIdentityMap($entity)) {
|
||||
throw new FormException('Entities passed to the choice field must be managed');
|
||||
}
|
||||
|
||||
return $this->unitOfWork->getEntityIdentifier($entity);
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
<?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\Bridge\Doctrine\Form\DataTransformer;
|
||||
|
||||
use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Form\DataTransformer\TransformationFailedException;
|
||||
use Symfony\Component\Form\DataTransformer\DataTransformerInterface;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
class EntitiesToArrayTransformer implements DataTransformerInterface
|
||||
{
|
||||
private $choiceList;
|
||||
|
||||
public function __construct(EntityChoiceList $choiceList)
|
||||
{
|
||||
$this->choiceList = $choiceList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms entities into choice keys
|
||||
*
|
||||
* @param Collection|object A collection of entities, a single entity or
|
||||
* NULL
|
||||
* @return mixed An array of choice keys, a single key or
|
||||
* NULL
|
||||
*/
|
||||
public function transform($collection)
|
||||
{
|
||||
if (null === $collection) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if (!($collection instanceof Collection)) {
|
||||
throw new UnexpectedTypeException($collection, 'Doctrine\Common\Collection\Collection');
|
||||
}
|
||||
|
||||
$array = array();
|
||||
|
||||
if (count($this->choiceList->getIdentifier()) > 1) {
|
||||
// load all choices
|
||||
$availableEntities = $this->choiceList->getEntities();
|
||||
|
||||
foreach ($collection as $entity) {
|
||||
// identify choices by their collection key
|
||||
$key = array_search($entity, $availableEntities);
|
||||
$array[] = $key;
|
||||
}
|
||||
} else {
|
||||
foreach ($collection as $entity) {
|
||||
$array[] = current($this->choiceList->getIdentifierValues($entity));
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms choice keys into entities
|
||||
*
|
||||
* @param mixed $keys An array of keys, a single key or NULL
|
||||
* @return Collection|object A collection of entities, a single entity
|
||||
* or NULL
|
||||
*/
|
||||
public function reverseTransform($keys)
|
||||
{
|
||||
$collection = new ArrayCollection();
|
||||
|
||||
if ('' === $keys || null === $keys) {
|
||||
return $collection;
|
||||
}
|
||||
|
||||
if (!is_array($keys)) {
|
||||
throw new UnexpectedTypeException($keys, 'array');
|
||||
}
|
||||
|
||||
$notFound = array();
|
||||
|
||||
// optimize this into a SELECT WHERE IN query
|
||||
foreach ($keys as $key) {
|
||||
if ($entity = $this->choiceList->getEntity($key)) {
|
||||
$collection->add($entity);
|
||||
} else {
|
||||
$notFound[] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($notFound) > 0) {
|
||||
throw new TransformationFailedException(sprintf('The entities with keys "%s" could not be found', implode('", "', $notFound)));
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
<?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\Bridge\Doctrine\Form\DataTransformer;
|
||||
|
||||
use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Form\DataTransformer\DataTransformerInterface;
|
||||
use Symfony\Component\Form\DataTransformer\TransformationFailedException;
|
||||
|
||||
class EntityToIdTransformer implements DataTransformerInterface
|
||||
{
|
||||
private $choiceList;
|
||||
|
||||
public function __construct(EntityChoiceList $choiceList)
|
||||
{
|
||||
$this->choiceList = $choiceList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms entities into choice keys
|
||||
*
|
||||
* @param Collection|object A collection of entities, a single entity or
|
||||
* NULL
|
||||
* @return mixed An array of choice keys, a single key or
|
||||
* NULL
|
||||
*/
|
||||
public function transform($entity)
|
||||
{
|
||||
if (null === $entity || '' === $entity) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!is_object($entity)) {
|
||||
throw new UnexpectedTypeException($entity, 'object');
|
||||
}
|
||||
|
||||
if (count($this->choiceList->getIdentifier()) > 1) {
|
||||
// load all choices
|
||||
$availableEntities = $this->choiceList->getEntities();
|
||||
|
||||
return array_search($entity, $availableEntities);
|
||||
}
|
||||
|
||||
return current($this->choiceList->getIdentifierValues($entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms choice keys into entities
|
||||
*
|
||||
* @param mixed $key An array of keys, a single key or NULL
|
||||
* @return Collection|object A collection of entities, a single entity
|
||||
* or NULL
|
||||
*/
|
||||
public function reverseTransform($key)
|
||||
{
|
||||
if ('' === $key || null === $key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!is_numeric($key)) {
|
||||
throw new UnexpectedTypeException($key, 'numeric');
|
||||
}
|
||||
|
||||
if (!($entity = $this->choiceList->getEntity($key))) {
|
||||
throw new TransformationFailedException('The entity with key "%s" could not be found', $key);
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
}
|
38
src/Symfony/Bridge/Doctrine/Form/DoctrineTypeLoader.php
Normal file
38
src/Symfony/Bridge/Doctrine/Form/DoctrineTypeLoader.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?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\Bridge\Doctrine\Form;
|
||||
|
||||
use Symfony\Component\Form\Type\Loader\TypeLoaderInterface;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
class DoctrineTypeLoader implements TypeLoaderInterface
|
||||
{
|
||||
private $types;
|
||||
|
||||
public function __construct(EntityManager $em)
|
||||
{
|
||||
$this->types['entity'] = new EntityType($em);
|
||||
}
|
||||
|
||||
public function getType($name)
|
||||
{
|
||||
return $this->types[$name];
|
||||
}
|
||||
|
||||
public function hasType($name)
|
||||
{
|
||||
return isset($this->types[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
82
src/Symfony/Bridge/Doctrine/Form/EntityType.php
Normal file
82
src/Symfony/Bridge/Doctrine/Form/EntityType.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?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\Bridge\Doctrine\Form;
|
||||
|
||||
use Symfony\Component\Form\FormBuilder;
|
||||
use Symfony\Component\Form\FormFactoryInterface;
|
||||
use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList;
|
||||
use Symfony\Bridge\Doctrine\Form\EventListener\MergeCollectionListener;
|
||||
use Symfony\Bridge\Doctrine\Form\DataTransformer\EntitiesToArrayTransformer;
|
||||
use Symfony\Bridge\Doctrine\Form\DataTransformer\EntityToIdTransformer;
|
||||
use Symfony\Component\Form\Type\AbstractType;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
class EntityType extends AbstractType
|
||||
{
|
||||
private $em;
|
||||
|
||||
public function __construct(EntityManager $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilder $builder, array $options)
|
||||
{
|
||||
if ($options['multiple']) {
|
||||
$builder->addEventSubscriber(new MergeCollectionListener())
|
||||
->prependClientTransformer(new EntitiesToArrayTransformer($options['choice_list']));
|
||||
} else {
|
||||
$builder->prependClientTransformer(new EntityToIdTransformer($options['choice_list']));
|
||||
}
|
||||
}
|
||||
|
||||
public function getDefaultOptions(array $options)
|
||||
{
|
||||
$defaultOptions = array(
|
||||
'template' => 'choice',
|
||||
'multiple' => false,
|
||||
'expanded' => false,
|
||||
'em' => $this->em,
|
||||
'class' => null,
|
||||
'property' => null,
|
||||
'query_builder' => null,
|
||||
'choices' => array(),
|
||||
'preferred_choices' => array(),
|
||||
'multiple' => false,
|
||||
'expanded' => false,
|
||||
);
|
||||
|
||||
$options = array_replace($defaultOptions, $options);
|
||||
|
||||
if (!isset($options['choice_list'])) {
|
||||
$defaultOptions['choice_list'] = new EntityChoiceList(
|
||||
$options['em'],
|
||||
$options['class'],
|
||||
$options['property'],
|
||||
$options['query_builder'],
|
||||
$options['choices']
|
||||
);
|
||||
}
|
||||
|
||||
return $defaultOptions;
|
||||
}
|
||||
|
||||
public function getParent(array $options)
|
||||
{
|
||||
return 'choice';
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'entity';
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
<?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\Bridge\Doctrine\Form\EventListener;
|
||||
|
||||
use Symfony\Component\Form\Events;
|
||||
use Symfony\Component\Form\Event\FilterDataEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Merge changes from the request to a Doctrine\Common\Collections\Collection instance.
|
||||
*
|
||||
* This works with ORM, MongoDB and CouchDB instances of the collection interface.
|
||||
*
|
||||
* @see Doctrine\Common\Collections\Collection
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
|
||||
*/
|
||||
class MergeCollectionListener implements EventSubscriberInterface
|
||||
{
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return Events::onBindNormData;
|
||||
}
|
||||
|
||||
public function onBindNormData(FilterDataEvent $event)
|
||||
{
|
||||
$collection = $event->getForm()->getData();
|
||||
$data = $event->getData();
|
||||
|
||||
if (!$collection) {
|
||||
$collection = $data;
|
||||
} else if (count($data) === 0) {
|
||||
$collection->clear();
|
||||
} else {
|
||||
// merge $data into $collection
|
||||
foreach ($collection as $entity) {
|
||||
if (!$data->contains($entity)) {
|
||||
$collection->removeElement($entity);
|
||||
} else {
|
||||
$data->removeElement($entity);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($data as $entity) {
|
||||
$collection->add($entity);
|
||||
}
|
||||
}
|
||||
|
||||
$event->setData($collection);
|
||||
}
|
||||
}
|
@ -9,16 +9,20 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\FieldFactory;
|
||||
namespace Symfony\Bridge\Doctrine\Form\Type\Guesser;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Symfony\Component\Form\Type\Guesser\Guess;
|
||||
use Symfony\Component\Form\Type\Guesser\TypeGuesserInterface;
|
||||
use Symfony\Component\Form\Type\Guesser\TypeGuess;
|
||||
use Symfony\Component\Form\Type\Guesser\ValueGuess;
|
||||
|
||||
/**
|
||||
* Guesses form fields from the metadata of Doctrine 2
|
||||
*
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
*/
|
||||
class EntityFieldFactoryGuesser implements FieldFactoryGuesserInterface
|
||||
class EntityTypeGuesser implements TypeGuesserInterface
|
||||
{
|
||||
/**
|
||||
* The Doctrine 2 entity manager
|
||||
@ -49,7 +53,7 @@ class EntityFieldFactoryGuesser implements FieldFactoryGuesserInterface
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function guessClass($class, $property)
|
||||
public function guessType($class, $property)
|
||||
{
|
||||
if ($this->isMappedClass($class)) {
|
||||
$metadata = $this->em->getClassMetadata($class);
|
||||
@ -58,86 +62,86 @@ class EntityFieldFactoryGuesser implements FieldFactoryGuesserInterface
|
||||
$multiple = $metadata->isCollectionValuedAssociation($property);
|
||||
$mapping = $metadata->getAssociationMapping($property);
|
||||
|
||||
return new FieldFactoryClassGuess(
|
||||
'Symfony\Component\Form\EntityChoiceField',
|
||||
return new TypeGuess(
|
||||
'entity',
|
||||
array(
|
||||
'em' => $this->em,
|
||||
'class' => $mapping['targetEntity'],
|
||||
'multiple' => $multiple,
|
||||
),
|
||||
FieldFactoryGuess::HIGH_CONFIDENCE
|
||||
Guess::HIGH_CONFIDENCE
|
||||
);
|
||||
} else {
|
||||
switch ($metadata->getTypeOfField($property))
|
||||
{
|
||||
// case 'array':
|
||||
// return new FieldFactoryClassGuess(
|
||||
// 'Symfony\Component\Form\CollectionField',
|
||||
// return new TypeGuess(
|
||||
// 'Collection',
|
||||
// array(),
|
||||
// FieldFactoryGuess::HIGH_CONFIDENCE
|
||||
// Guess::HIGH_CONFIDENCE
|
||||
// );
|
||||
case 'boolean':
|
||||
return new FieldFactoryClassGuess(
|
||||
'Symfony\Component\Form\CheckboxField',
|
||||
return new TypeGuess(
|
||||
'checkbox',
|
||||
array(),
|
||||
FieldFactoryGuess::HIGH_CONFIDENCE
|
||||
Guess::HIGH_CONFIDENCE
|
||||
);
|
||||
case 'datetime':
|
||||
case 'vardatetime':
|
||||
case 'datetimetz':
|
||||
return new FieldFactoryClassGuess(
|
||||
'Symfony\Component\Form\DateTimeField',
|
||||
return new TypeGuess(
|
||||
'datetime',
|
||||
array(),
|
||||
FieldFactoryGuess::HIGH_CONFIDENCE
|
||||
Guess::HIGH_CONFIDENCE
|
||||
);
|
||||
case 'date':
|
||||
return new FieldFactoryClassGuess(
|
||||
'Symfony\Component\Form\DateField',
|
||||
return new TypeGuess(
|
||||
'date',
|
||||
array(),
|
||||
FieldFactoryGuess::HIGH_CONFIDENCE
|
||||
Guess::HIGH_CONFIDENCE
|
||||
);
|
||||
case 'decimal':
|
||||
case 'float':
|
||||
return new FieldFactoryClassGuess(
|
||||
'Symfony\Component\Form\NumberField',
|
||||
return new TypeGuess(
|
||||
'number',
|
||||
array(),
|
||||
FieldFactoryGuess::MEDIUM_CONFIDENCE
|
||||
Guess::MEDIUM_CONFIDENCE
|
||||
);
|
||||
case 'integer':
|
||||
case 'bigint':
|
||||
case 'smallint':
|
||||
return new FieldFactoryClassGuess(
|
||||
'Symfony\Component\Form\IntegerField',
|
||||
return new TypeGuess(
|
||||
'integer',
|
||||
array(),
|
||||
FieldFactoryGuess::MEDIUM_CONFIDENCE
|
||||
Guess::MEDIUM_CONFIDENCE
|
||||
);
|
||||
case 'string':
|
||||
return new FieldFactoryClassGuess(
|
||||
'Symfony\Component\Form\TextField',
|
||||
return new TypeGuess(
|
||||
'text',
|
||||
array(),
|
||||
FieldFactoryGuess::MEDIUM_CONFIDENCE
|
||||
Guess::MEDIUM_CONFIDENCE
|
||||
);
|
||||
case 'text':
|
||||
return new FieldFactoryClassGuess(
|
||||
'Symfony\Component\Form\TextareaField',
|
||||
return new TypeGuess(
|
||||
'textarea',
|
||||
array(),
|
||||
FieldFactoryGuess::MEDIUM_CONFIDENCE
|
||||
Guess::MEDIUM_CONFIDENCE
|
||||
);
|
||||
case 'time':
|
||||
return new FieldFactoryClassGuess(
|
||||
'Symfony\Component\Form\TimeField',
|
||||
return new TypeGuess(
|
||||
'time',
|
||||
array(),
|
||||
FieldFactoryGuess::HIGH_CONFIDENCE
|
||||
Guess::HIGH_CONFIDENCE
|
||||
);
|
||||
// case 'object': ???
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new FieldFactoryClassGuess(
|
||||
'Symfony\Component\Form\TextField',
|
||||
return new TypeGuess(
|
||||
'text',
|
||||
array(),
|
||||
FieldFactoryGuess::LOW_CONFIDENCE
|
||||
Guess::LOW_CONFIDENCE
|
||||
);
|
||||
}
|
||||
|
||||
@ -151,15 +155,15 @@ class EntityFieldFactoryGuesser implements FieldFactoryGuesserInterface
|
||||
|
||||
if ($metadata->hasField($property)) {
|
||||
if (!$metadata->isNullable($property)) {
|
||||
return new FieldFactoryGuess(
|
||||
return new ValueGuess(
|
||||
true,
|
||||
FieldFactoryGuess::HIGH_CONFIDENCE
|
||||
Guess::HIGH_CONFIDENCE
|
||||
);
|
||||
}
|
||||
|
||||
return new FieldFactoryGuess(
|
||||
return new ValueGuess(
|
||||
false,
|
||||
FieldFactoryGuess::MEDIUM_CONFIDENCE
|
||||
Guess::MEDIUM_CONFIDENCE
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -178,9 +182,9 @@ class EntityFieldFactoryGuesser implements FieldFactoryGuesserInterface
|
||||
|
||||
|
||||
if (isset($mapping['length'])) {
|
||||
return new FieldFactoryGuess(
|
||||
return new ValueGuess(
|
||||
$mapping['length'],
|
||||
FieldFactoryGuess::HIGH_CONFIDENCE
|
||||
Guess::HIGH_CONFIDENCE
|
||||
);
|
||||
}
|
||||
}
|
244
src/Symfony/Bridge/Twig/Extension/FormExtension.php
Normal file
244
src/Symfony/Bridge/Twig/Extension/FormExtension.php
Normal file
@ -0,0 +1,244 @@
|
||||
<?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\Bridge\Twig\Extension;
|
||||
|
||||
use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\Exception\FormException;
|
||||
|
||||
/**
|
||||
* FormExtension extends Twig with form capabilities.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
*/
|
||||
class FormExtension extends \Twig_Extension
|
||||
{
|
||||
protected $resources;
|
||||
protected $templates;
|
||||
protected $environment;
|
||||
protected $themes;
|
||||
|
||||
public function __construct(array $resources = array())
|
||||
{
|
||||
$this->themes = new \SplObjectStorage();
|
||||
$this->resources = $resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function initRuntime(\Twig_Environment $environment)
|
||||
{
|
||||
$this->environment = $environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a theme for a given view.
|
||||
*
|
||||
* @param FormView $view A FormView instance
|
||||
* @param array $resources An array of resources
|
||||
*/
|
||||
public function setTheme(FormView $view, array $resources)
|
||||
{
|
||||
$this->themes->attach($view, $resources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token parser instance to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_TokenParser instances
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return array(
|
||||
// {% form_theme form "SomeBungle::widgets.twig" %}
|
||||
new FormThemeTokenParser(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getFunctions()
|
||||
{
|
||||
return array(
|
||||
'form_enctype' => new \Twig_Function_Method($this, 'renderEnctype', array('is_safe' => array('html'))),
|
||||
'form_widget' => new \Twig_Function_Method($this, 'renderWidget', array('is_safe' => array('html'))),
|
||||
'form_errors' => new \Twig_Function_Method($this, 'renderErrors', array('is_safe' => array('html'))),
|
||||
'form_label' => new \Twig_Function_Method($this, 'renderLabel', array('is_safe' => array('html'))),
|
||||
'form_row' => new \Twig_Function_Method($this, 'renderRow', array('is_safe' => array('html'))),
|
||||
'form_rest' => new \Twig_Function_Method($this, 'renderRest', array('is_safe' => array('html'))),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the HTML enctype in the form tag, if necessary
|
||||
*
|
||||
* Example usage in Twig templates:
|
||||
*
|
||||
* <form action="..." method="post" {{ form_enctype(form) }}>
|
||||
*
|
||||
* @param FormView $view The view for which to render the encoding type
|
||||
*/
|
||||
public function renderEnctype(FormView $view)
|
||||
{
|
||||
return $this->render($view, 'enctype');
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a row for the view.
|
||||
*
|
||||
* @param FormView $view The view to render as a row
|
||||
*/
|
||||
public function renderRow(FormView $view, array $variables = array())
|
||||
{
|
||||
return $this->render($view, 'row', $variables);
|
||||
}
|
||||
|
||||
public function renderRest(FormView $view, array $variables = array())
|
||||
{
|
||||
return $this->render($view, 'rest', $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the HTML for a given view
|
||||
*
|
||||
* Example usage in Twig:
|
||||
*
|
||||
* {{ form_widget(view) }}
|
||||
*
|
||||
* You can pass attributes element during the call:
|
||||
*
|
||||
* {{ form_widget(view, {'class': 'foo'}) }}
|
||||
*
|
||||
* Some fields also accept additional variables as parameters:
|
||||
*
|
||||
* {{ form_widget(view, {}, {'separator': '+++++'}) }}
|
||||
*
|
||||
* @param FormView $view The view to render
|
||||
* @param array $attributes HTML attributes passed to the template
|
||||
* @param array $parameters Additional variables passed to the template
|
||||
* @param array|string $resources A resource or array of resources
|
||||
*/
|
||||
public function renderWidget(FormView $view, array $variables = array(), $resources = null)
|
||||
{
|
||||
if (null !== $resources && !is_array($resources)) {
|
||||
$resources = array($resources);
|
||||
}
|
||||
|
||||
return $this->render($view, 'widget', $variables, $resources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the errors of the given view
|
||||
*
|
||||
* @param FormView $view The view to render the errors for
|
||||
* @param array $params Additional variables passed to the template
|
||||
*/
|
||||
public function renderErrors(FormView $view)
|
||||
{
|
||||
return $this->render($view, 'errors');
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the label of the given view
|
||||
*
|
||||
* @param FormView $view The view to render the label for
|
||||
*/
|
||||
public function renderLabel(FormView $view, $label = null)
|
||||
{
|
||||
return $this->render($view, 'label', null === $label ? array() : array('label' => $label));
|
||||
}
|
||||
|
||||
protected function render(FormView $view, $section, array $variables = array(), array $resources = null)
|
||||
{
|
||||
$templates = $this->getTemplates($view, $resources);
|
||||
$blocks = $view->get('types');
|
||||
foreach ($blocks as &$block) {
|
||||
$block = $block.'_'.$section;
|
||||
|
||||
if (isset($templates[$block])) {
|
||||
if ('widget' === $section || 'row' === $section) {
|
||||
$view->setRendered(true);
|
||||
}
|
||||
|
||||
return $templates[$block]->renderBlock($block, array_merge($view->all(), $variables));
|
||||
}
|
||||
}
|
||||
|
||||
throw new FormException(sprintf('Unable to render form as none of the following blocks exist: "%s".', implode('", "', $blocks)));
|
||||
}
|
||||
|
||||
protected function getTemplate(FormView $view, $name, array $resources = null)
|
||||
{
|
||||
$templates = $this->getTemplates($view, $resources);
|
||||
|
||||
return $templates[$name];
|
||||
}
|
||||
|
||||
protected function getTemplates(FormView $view, array $resources = null)
|
||||
{
|
||||
// templates are looked for in the following resources:
|
||||
// * resources provided directly into the function call
|
||||
// * resources from the themes (and its parents)
|
||||
// * default resources
|
||||
|
||||
// defaults
|
||||
$all = $this->resources;
|
||||
|
||||
// themes
|
||||
$parent = $view;
|
||||
do {
|
||||
if (isset($this->themes[$parent])) {
|
||||
$all = array_merge($all, $this->themes[$parent]);
|
||||
}
|
||||
} while ($parent = $parent->getParent());
|
||||
|
||||
// local
|
||||
$all = array_merge($all, null !== $resources ? (array) $resources : array());
|
||||
|
||||
$templates = array();
|
||||
foreach ($all as $resource) {
|
||||
if (!$resource instanceof \Twig_Template) {
|
||||
$resource = $this->environment->loadTemplate($resource);
|
||||
}
|
||||
|
||||
$blocks = array();
|
||||
foreach ($this->getBlockNames($resource) as $name) {
|
||||
$blocks[$name] = $resource;
|
||||
}
|
||||
|
||||
$templates = array_replace($templates, $blocks);
|
||||
}
|
||||
|
||||
return $templates;
|
||||
}
|
||||
|
||||
protected function getBlockNames($resource)
|
||||
{
|
||||
$names = $resource->getBlockNames();
|
||||
$parent = $resource;
|
||||
while (false !== $parent = $parent->getParent(array())) {
|
||||
$names = array_merge($names, $parent->getBlockNames());
|
||||
}
|
||||
|
||||
return array_unique($names);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'form';
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\TwigBundle\Node;
|
||||
namespace Symfony\Bridge\Twig\Node;
|
||||
|
||||
/**
|
||||
*
|
@ -9,9 +9,9 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\TwigBundle\TokenParser;
|
||||
namespace Symfony\Bridge\Twig\TokenParser;
|
||||
|
||||
use Symfony\Bundle\TwigBundle\Node\FormThemeNode;
|
||||
use Symfony\Bridge\Twig\Node\FormThemeNode;
|
||||
|
||||
/**
|
||||
*
|
@ -38,7 +38,7 @@
|
||||
<parameter key="doctrine.orm.proxy_cache_warmer.class">Symfony\Bundle\DoctrineBundle\CacheWarmer\ProxyCacheWarmer</parameter>
|
||||
|
||||
<!-- form field factory guesser -->
|
||||
<parameter key="form.field_factory.doctrine_guesser.class">Symfony\Component\Form\FieldFactory\EntityFieldFactoryGuesser</parameter>
|
||||
<parameter key="form.guesser.doctrine.class">Symfony\Bridge\Doctrine\Form\Type\Guesser\EntityTypeGuesser</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
@ -55,8 +55,13 @@
|
||||
<argument type="service" id="service_container" />
|
||||
</service>
|
||||
|
||||
<service id="form.field_factory.doctrine_guesser" class="%form.field_factory.doctrine_guesser.class%" public="false">
|
||||
<tag name="form.field_factory.guesser" />
|
||||
<service id="form.guesser.doctrine" class="%form.guesser.doctrine.class%" public="false">
|
||||
<tag name="form.guesser" />
|
||||
<argument type="service" id="doctrine.orm.entity_manager" />
|
||||
</service>
|
||||
|
||||
<service id="form.type.entity" class="Symfony\Bridge\Doctrine\Form\EntityType">
|
||||
<tag name="form.type" alias="entity" />
|
||||
<argument type="service" id="doctrine.orm.entity_manager" />
|
||||
</service>
|
||||
</services>
|
||||
|
@ -110,6 +110,7 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
|
||||
public function getNotCalledListeners()
|
||||
{
|
||||
$notCalled = array();
|
||||
|
||||
foreach (array_keys($this->getListeners()) as $name) {
|
||||
foreach ($this->getListeners($name) as $listener) {
|
||||
if (!isset($this->called[$name.'.'.get_class($listener)])) {
|
||||
|
@ -16,23 +16,25 @@ use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Adds all services with the tag "form.field_factory_guesser" as argument
|
||||
* to the "form.field_factory" service
|
||||
* Adds all services with the tag "form.guesser" as constructor argument of the
|
||||
* "form.factory" service
|
||||
*
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
*/
|
||||
class AddFieldFactoryGuessersPass implements CompilerPassInterface
|
||||
class AddFormGuessersPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition('form.field_factory')) {
|
||||
if (!$container->hasDefinition('form.factory')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$guessers = array_map(function($id) {
|
||||
return new Reference($id);
|
||||
}, array_keys($container->findTaggedServiceIds('form.field_factory.guesser')));
|
||||
$guessers = array();
|
||||
|
||||
$container->getDefinition('form.field_factory')->replaceArgument(0, $guessers);
|
||||
foreach ($container->findTaggedServiceIds('form.guesser') as $serviceId => $tag) {
|
||||
$guessers[] = new Reference($serviceId);
|
||||
}
|
||||
|
||||
$container->getDefinition('form.factory')->replaceArgument(1, $guessers);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
|
||||
/**
|
||||
* Adds all services with the tag "form.type" as argument
|
||||
* to the "form.type.loader" service
|
||||
*
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
|
||||
*/
|
||||
class AddFormTypesPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition('form.type.loader')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Builds an array with service IDs as keys and tag aliases as values
|
||||
$types = array();
|
||||
$tags = $container->findTaggedServiceIds('form.type');
|
||||
|
||||
foreach ($tags as $serviceId => $arguments) {
|
||||
$alias = isset($arguments[0]['alias'])
|
||||
? $arguments[0]['alias']
|
||||
: $serviceId;
|
||||
|
||||
// Flip, because we want tag aliases (= type identifiers) as keys
|
||||
$types[$alias] = $serviceId;
|
||||
}
|
||||
|
||||
$container->getDefinition('form.type.loader')->replaceArgument(1, $types);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<?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\Bundle\FrameworkBundle\Form;
|
||||
|
||||
use Symfony\Component\Form\Type\Loader\TypeLoaderInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
class ContainerAwareTypeLoader implements TypeLoaderInterface
|
||||
{
|
||||
private $container;
|
||||
|
||||
private $serviceIds;
|
||||
|
||||
public function __construct(ContainerInterface $container, array $serviceIds)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->serviceIds = $serviceIds;
|
||||
}
|
||||
|
||||
public function getType($identifier)
|
||||
{
|
||||
if (!isset($this->serviceIds[$identifier])) {
|
||||
throw new \InvalidArgumentException(sprintf('The field type "%s" is not registered with the service container.', $identifier));
|
||||
}
|
||||
|
||||
return $this->container->get($this->serviceIds[$identifier]);
|
||||
}
|
||||
|
||||
public function hasType($identifier)
|
||||
{
|
||||
return isset($this->serviceIds[$identifier]);
|
||||
}
|
||||
}
|
@ -12,7 +12,8 @@
|
||||
namespace Symfony\Bundle\FrameworkBundle;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass;
|
||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddFieldFactoryGuessersPass;
|
||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddFormTypesPass;
|
||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddFormGuessersPass;
|
||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass;
|
||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RegisterKernelListenersPass;
|
||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverPass;
|
||||
@ -77,7 +78,8 @@ class FrameworkBundle extends Bundle
|
||||
$container->addCompilerPass(new RegisterKernelListenersPass());
|
||||
$container->addCompilerPass(new TemplatingPass());
|
||||
$container->addCompilerPass(new AddConstraintValidatorsPass());
|
||||
$container->addCompilerPass(new AddFieldFactoryGuessersPass());
|
||||
$container->addCompilerPass(new AddFormTypesPass());
|
||||
$container->addCompilerPass(new AddFormGuessersPass());
|
||||
$container->addCompilerPass(new AddClassesToCachePass());
|
||||
$container->addCompilerPass(new AddClassesToAutoloadMapPass());
|
||||
$container->addCompilerPass(new TranslatorPass());
|
||||
|
@ -5,47 +5,147 @@
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="form.field_factory.class">Symfony\Component\Form\FieldFactory\FieldFactory</parameter>
|
||||
<parameter key="form.field_factory.validator_guesser.class">Symfony\Component\Form\FieldFactory\ValidatorFieldFactoryGuesser</parameter>
|
||||
<parameter key="form.factory.class">Symfony\Component\Form\FormFactory</parameter>
|
||||
<parameter key="form.type.loader.class">Symfony\Bundle\FrameworkBundle\Form\ContainerAwareTypeLoader</parameter>
|
||||
<parameter key="form.guesser.validator.class">Symfony\Component\Form\Type\Guesser\ValidatorTypeGuesser</parameter>
|
||||
<parameter key="form.csrf_provider.class">Symfony\Component\Form\CsrfProvider\SessionCsrfProvider</parameter>
|
||||
<parameter key="form.context.class">Symfony\Component\Form\FormContext</parameter>
|
||||
<parameter key="form.csrf_protection.enabled">true</parameter>
|
||||
<parameter key="form.csrf_protection.field_name">_token</parameter>
|
||||
<parameter key="form.csrf_protection.secret">secret</parameter>
|
||||
<parameter key="form.validation_groups">Default</parameter>
|
||||
<parameter key="file.temporary_storage.class">Symfony\Component\HttpFoundation\File\SessionBasedTemporaryStorage</parameter>
|
||||
<parameter key="file.temporary_storage.secret">abcdef</parameter>
|
||||
<parameter key="file.temporary_storage.nesting_levels">3</parameter>
|
||||
<parameter key="file.temporary_storage.directory"></parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
|
||||
<!-- FieldFactory -->
|
||||
<service id="form.field_factory" class="%form.field_factory.class%">
|
||||
<!-- All services with tag "form.field_factory.guesser" are inserted here by AddFieldFactoryGuessersPass -->
|
||||
<!-- FormFactory -->
|
||||
<service id="form.factory" class="%form.factory.class%">
|
||||
<argument type="service" id="form.type.loader" />
|
||||
<!--
|
||||
All services with tag "form.guesser" are inserted here by
|
||||
AddFormGuessersPass
|
||||
-->
|
||||
<argument type="collection" />
|
||||
</service>
|
||||
|
||||
<!-- ValidatorFieldFactoryGuesser -->
|
||||
<service id="form.field_factory.validator_guesser" class="%form.field_factory.validator_guesser.class%" public="false">
|
||||
<tag name="form.field_factory.guesser" />
|
||||
<!-- ValidatorTypeGuesser -->
|
||||
<service id="form.guesser.validator" class="%form.guesser.validator.class%" public="false">
|
||||
<tag name="form.guesser" />
|
||||
<argument type="service" id="validator.mapping.class_metadata_factory" />
|
||||
</service>
|
||||
|
||||
|
||||
<!-- CsrfProvider -->
|
||||
<service id="form.csrf_provider" class="%form.csrf_provider.class%">
|
||||
<argument type="service" id="session" />
|
||||
<argument>%form.csrf_protection.secret%</argument>
|
||||
</service>
|
||||
|
||||
<!-- FormContext -->
|
||||
<service id="form.context" class="%form.context.class%">
|
||||
<argument type="collection">
|
||||
<argument key="validator" type="service" id="validator" />
|
||||
<argument key="validation_groups">%form.validation_groups%</argument>
|
||||
<argument key="field_factory" type="service" id="form.field_factory" />
|
||||
<argument key="csrf_protection">%form.csrf_protection.enabled%</argument>
|
||||
<argument key="csrf_field_name">%form.csrf_protection.field_name%</argument>
|
||||
<argument key="csrf_provider" type="service" id="form.csrf_provider" />
|
||||
</argument>
|
||||
<!-- TemporaryStorage - where should we put this? -->
|
||||
<service id="file.temporary_storage" class="%file.temporary_storage.class%">
|
||||
<argument type="service" id="session" />
|
||||
<argument>%file.temporary_storage.secret%</argument>
|
||||
<argument>%file.temporary_storage.nesting_levels%</argument>
|
||||
<argument>%file.temporary_storage.directory%</argument>
|
||||
</service>
|
||||
|
||||
|
||||
<!-- ContainerAwareTypeLoader -->
|
||||
<service id="form.type.loader" class="%form.type.loader.class%">
|
||||
<argument type="service" id="service_container" />
|
||||
<!--
|
||||
All services with tag "form.type" are inserted here by
|
||||
AddFormTypesPass
|
||||
-->
|
||||
<argument type="collection" />
|
||||
</service>
|
||||
|
||||
<!-- FieldTypes -->
|
||||
<service id="form.type.field" class="Symfony\Component\Form\Type\FieldType">
|
||||
<tag name="form.type" alias="field" />
|
||||
<argument type="service" id="validator" />
|
||||
</service>
|
||||
<service id="form.type.form" class="Symfony\Component\Form\Type\FormType">
|
||||
<tag name="form.type" alias="form" />
|
||||
</service>
|
||||
<service id="form.type.birthday" class="Symfony\Component\Form\Type\BirthdayType">
|
||||
<tag name="form.type" alias="birthday" />
|
||||
</service>
|
||||
<service id="form.type.checkbox" class="Symfony\Component\Form\Type\CheckboxType">
|
||||
<tag name="form.type" alias="checkbox" />
|
||||
</service>
|
||||
<service id="form.type.choice" class="Symfony\Component\Form\Type\ChoiceType">
|
||||
<tag name="form.type" alias="choice" />
|
||||
</service>
|
||||
<service id="form.type.collection" class="Symfony\Component\Form\Type\CollectionType">
|
||||
<tag name="form.type" alias="collection" />
|
||||
</service>
|
||||
<service id="form.type.country" class="Symfony\Component\Form\Type\CountryType">
|
||||
<tag name="form.type" alias="country" />
|
||||
</service>
|
||||
<service id="form.type.csrf" class="Symfony\Component\Form\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\Type\DateType">
|
||||
<tag name="form.type" alias="date" />
|
||||
</service>
|
||||
<service id="form.type.datetime" class="Symfony\Component\Form\Type\DateTimeType">
|
||||
<tag name="form.type" alias="datetime" />
|
||||
</service>
|
||||
<service id="form.type.email" class="Symfony\Component\Form\Type\EmailType">
|
||||
<tag name="form.type" alias="email" />
|
||||
</service>
|
||||
<service id="form.type.file" class="Symfony\Component\Form\Type\FileType">
|
||||
<tag name="form.type" alias="file" />
|
||||
<argument type="service" id="file.temporary_storage" />
|
||||
</service>
|
||||
<service id="form.type.hidden" class="Symfony\Component\Form\Type\HiddenType">
|
||||
<tag name="form.type" alias="hidden" />
|
||||
</service>
|
||||
<service id="form.type.integer" class="Symfony\Component\Form\Type\IntegerType">
|
||||
<tag name="form.type" alias="integer" />
|
||||
</service>
|
||||
<service id="form.type.language" class="Symfony\Component\Form\Type\LanguageType">
|
||||
<tag name="form.type" alias="language" />
|
||||
</service>
|
||||
<service id="form.type.locale" class="Symfony\Component\Form\Type\LocaleType">
|
||||
<tag name="form.type" alias="locale" />
|
||||
</service>
|
||||
<service id="form.type.money" class="Symfony\Component\Form\Type\MoneyType">
|
||||
<tag name="form.type" alias="money" />
|
||||
</service>
|
||||
<service id="form.type.number" class="Symfony\Component\Form\Type\NumberType">
|
||||
<tag name="form.type" alias="number" />
|
||||
</service>
|
||||
<service id="form.type.password" class="Symfony\Component\Form\Type\PasswordType">
|
||||
<tag name="form.type" alias="password" />
|
||||
</service>
|
||||
<service id="form.type.percent" class="Symfony\Component\Form\Type\PercentType">
|
||||
<tag name="form.type" alias="percent" />
|
||||
</service>
|
||||
<service id="form.type.radio" class="Symfony\Component\Form\Type\RadioType">
|
||||
<tag name="form.type" alias="radio" />
|
||||
</service>
|
||||
<service id="form.type.repeated" class="Symfony\Component\Form\Type\RepeatedType">
|
||||
<tag name="form.type" alias="repeated" />
|
||||
</service>
|
||||
<service id="form.type.textarea" class="Symfony\Component\Form\Type\TextareaType">
|
||||
<tag name="form.type" alias="textarea" />
|
||||
</service>
|
||||
<service id="form.type.text" class="Symfony\Component\Form\Type\TextType">
|
||||
<tag name="form.type" alias="text" />
|
||||
</service>
|
||||
<service id="form.type.time" class="Symfony\Component\Form\Type\TimeType">
|
||||
<tag name="form.type" alias="time" />
|
||||
</service>
|
||||
<service id="form.type.timezone" class="Symfony\Component\Form\Type\TimezoneType">
|
||||
<tag name="form.type" alias="timezone" />
|
||||
</service>
|
||||
<service id="form.type.url" class="Symfony\Component\Form\Type\UrlType">
|
||||
<tag name="form.type" alias="url" />
|
||||
</service>
|
||||
|
||||
</services>
|
||||
</container>
|
||||
|
@ -1,9 +0,0 @@
|
||||
<input type="checkbox"
|
||||
id="<?php echo $field->getId() ?>"
|
||||
name="<?php echo $field->getName() ?>"
|
||||
<?php if ($field->hasValue()): ?>value="<?php echo $field->getValue() ?>"<?php endif ?>
|
||||
<?php if ($field->isDisabled()): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($field->isRequired()): ?>required="required"<?php endif ?>
|
||||
<?php if ($field->isChecked()): ?>checked="checked"<?php endif ?>
|
||||
<?php echo $view['form']->attributes($attr) ?>
|
||||
/>
|
@ -0,0 +1,8 @@
|
||||
<input type="checkbox"
|
||||
<?php echo $view['form']->attributes() ?>
|
||||
name="<?php echo $view->escape($name) ?>"
|
||||
<?php if ($value): ?>value="<?php echo $view->escape($value) ?>"<?php endif ?>
|
||||
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($required): ?>required="required"<?php endif ?>
|
||||
<?php if ($checked): ?>checked="checked"<?php endif ?>
|
||||
/>
|
@ -1,48 +0,0 @@
|
||||
<?php if ($field->isExpanded()): ?>
|
||||
<?php foreach ($field as $choice => $child): ?>
|
||||
<?php echo $view['form']->render($child) ?>
|
||||
<label for="<?php echo $child->getId() ?>"><?php echo $field->getLabel($choice) ?></label>
|
||||
<?php endforeach ?>
|
||||
<?php else: ?>
|
||||
<select
|
||||
id="<?php echo $field->getId() ?>"
|
||||
name="<?php echo $field->getName() ?>"
|
||||
<?php if ($field->isDisabled()): ?> disabled="disabled"<?php endif ?>
|
||||
<?php if ($field->isMultipleChoice()): ?> multiple="multiple"<?php endif ?>
|
||||
<?php echo $view['form']->attributes($attr) ?>
|
||||
>
|
||||
<?php if (count($field->getPreferredChoices()) > 0): ?>
|
||||
<?php foreach ($field->getPreferredChoices() as $choice => $label): ?>
|
||||
<?php if ($field->isChoiceGroup($label)): ?>
|
||||
<optgroup label="<?php echo $choice ?>">
|
||||
<?php foreach ($label as $nestedChoice => $nestedLabel): ?>
|
||||
<option value="<?php echo $nestedChoice ?>"<?php if ($field->isChoiceSelected($nestedChoice)): ?> selected="selected"<?php endif?>>
|
||||
<?php echo $nestedLabel ?>
|
||||
</option>
|
||||
<?php endforeach ?>
|
||||
</optgroup>
|
||||
<?php else: ?>
|
||||
<option value="<?php echo $choice ?>"<?php if ($field->isChoiceSelected($choice)): ?> selected="selected"<?php endif?>>
|
||||
<?php echo $label ?>
|
||||
</option>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
<option disabled="disabled"><?php echo isset($separator) ? $separator : '-----------------' ?></option>
|
||||
<?php endif ?>
|
||||
<?php foreach ($field->getOtherChoices() as $choice => $label): ?>
|
||||
<?php if ($field->isChoiceGroup($label)): ?>
|
||||
<optgroup label="<?php echo $choice ?>">
|
||||
<?php foreach ($label as $nestedChoice => $nestedLabel): ?>
|
||||
<option value="<?php echo $nestedChoice ?>"<?php if ($field->isChoiceSelected($nestedChoice)): ?> selected="selected"<?php endif?>>
|
||||
<?php echo $nestedLabel ?>
|
||||
</option>
|
||||
<?php endforeach ?>
|
||||
</optgroup>
|
||||
<?php else: ?>
|
||||
<option value="<?php echo $choice ?>"<?php if ($field->isChoiceSelected($choice)): ?> selected="selected"<?php endif?>>
|
||||
<?php echo $label ?>
|
||||
</option>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
<?php endif ?>
|
@ -0,0 +1,42 @@
|
||||
<?php if ($expanded): ?>
|
||||
<div<?php echo $view['form']->attributes() ?>>
|
||||
<?php foreach ($form as $choice => $child): ?>
|
||||
<?php echo $view['form']->widget($child) ?>
|
||||
<?php echo $view['form']->label($child) ?>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<select
|
||||
<?php echo $view['form']->attributes() ?>
|
||||
name="<?php echo $view->escape($name) ?>"
|
||||
<?php if ($read_only): ?> disabled="disabled"<?php endif ?>
|
||||
<?php if ($multiple): ?> multiple="multiple"<?php endif ?>
|
||||
>
|
||||
<?php if (!$multiple && !$required): ?><option value=""><?php echo $empty_value; ?></option><?php endif; ?>
|
||||
<?php if (count($preferred_choices) > 0): ?>
|
||||
<?php foreach ($preferred_choices as $choice => $label): ?>
|
||||
<?php if ($form->isChoiceGroup($label)): ?>
|
||||
<optgroup label="<?php echo $view->escape($choice) ?>">
|
||||
<?php foreach ($label as $nestedChoice => $nestedLabel): ?>
|
||||
<option value="<?php echo $view->escape($nestedChoice) ?>"<?php if ($form->isChoiceSelected($nestedChoice)): ?> selected="selected"<?php endif?>><?php echo $view->escape($nestedLabel) ?></option>
|
||||
<?php endforeach ?>
|
||||
</optgroup>
|
||||
<?php else: ?>
|
||||
<option value="<?php echo $view->escape($choice) ?>"<?php if ($form->isChoiceSelected($choice)): ?> selected="selected"<?php endif?>><?php echo $view->escape($label) ?></option>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
<option disabled="disabled"><?php echo $separator ?></option>
|
||||
<?php endif ?>
|
||||
<?php foreach ($choices as $choice => $label): ?>
|
||||
<?php if ($form->isChoiceGroup($label)): ?>
|
||||
<optgroup label="<?php echo $view->escape($choice) ?>">
|
||||
<?php foreach ($label as $nestedChoice => $nestedLabel): ?>
|
||||
<option value="<?php echo $view->escape($nestedChoice) ?>"<?php if ($form->isChoiceSelected($nestedChoice)): ?> selected="selected"<?php endif?>><?php echo $view->escape($nestedLabel) ?></option>
|
||||
<?php endforeach ?>
|
||||
</optgroup>
|
||||
<?php else: ?>
|
||||
<option value="<?php echo $view->escape($choice) ?>"<?php if ($form->isChoiceSelected($choice)): ?> selected="selected"<?php endif?>><?php echo $view->escape($label) ?></option>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
<?php endif ?>
|
@ -0,0 +1 @@
|
||||
<?php echo $view['form']->render('form', 'widget', $renderer->all()); ?>
|
@ -0,0 +1,8 @@
|
||||
<?php if (!$form->hasParent() || !$form->getParent()->hasParent()): ?>
|
||||
<input type="hidden"
|
||||
<?php echo $view['form']->attributes() ?>
|
||||
name="<?php echo $view->escape($name) ?>"
|
||||
value="<?php echo $view->escape($value) ?>"
|
||||
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
|
||||
/>
|
||||
<?php endif ?>
|
@ -1,16 +0,0 @@
|
||||
<?php if ($field->isField()): ?>
|
||||
<input type="text"
|
||||
id="<?php echo $field->getId() ?>"
|
||||
name="<?php echo $field->getName() ?>"
|
||||
value="<?php echo $field->getDisplayedData() ?>"
|
||||
<?php if ($field->isDisabled()): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($field->isRequired()): ?>required="required"<?php endif ?>
|
||||
<?php echo $view['form']->attributes($attr) ?>
|
||||
/>
|
||||
<?php else: ?>
|
||||
<?php echo str_replace(array('{{ year }}', '{{ month }}', '{{ day }}'), array(
|
||||
$view['form']->render($field['year']),
|
||||
$view['form']->render($field['month']),
|
||||
$view['form']->render($field['day']),
|
||||
), $field->getPattern()) ?>
|
||||
<?php endif ?>
|
@ -1,3 +0,0 @@
|
||||
<?php echo $view['form']->render($field['date']) ?>
|
||||
<!-- keep space -->
|
||||
<?php echo $view['form']->render($field['time']) ?>
|
@ -0,0 +1,18 @@
|
||||
<?php if ($widget == 'text'): ?>
|
||||
<input type="text"
|
||||
<?php echo $view['form']->attributes() ?>
|
||||
name="<?php echo $view->escape($name) ?>"
|
||||
value="<?php echo $view->escape($value) ?>"
|
||||
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($required): ?>required="required"<?php endif ?>
|
||||
<?php if ($max_length): ?>maxlength="<?php echo $max_length ?>"<?php endif ?>
|
||||
/>
|
||||
<?php else: ?>
|
||||
<div<?php echo $view['form']->attributes() ?>>
|
||||
<?php echo str_replace(array('{{ year }}', '{{ month }}', '{{ day }}'), array(
|
||||
$view['form']->widget($form['year']),
|
||||
$view['form']->widget($form['month']),
|
||||
$view['form']->widget($form['day']),
|
||||
), $date_pattern) ?>
|
||||
</div>
|
||||
<?php endif ?>
|
@ -0,0 +1,5 @@
|
||||
<div<?php echo $view['form']->attributes() ?>>
|
||||
<?php echo $view['form']->widget($form['date'])
|
||||
. ' '
|
||||
. $view['form']->widget($form['time']) ?>
|
||||
</div>
|
@ -0,0 +1,8 @@
|
||||
<input type="email"
|
||||
<?php echo $view['form']->attributes() ?>
|
||||
name="<?php echo $view->escape($name) ?>"
|
||||
value="<?php echo $view->escape($value) ?>"
|
||||
<?php if ($max_length): ?>maxlength="<?php echo $view->escape($max_length) ?>"<?php endif ?>
|
||||
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($required): ?>required="required"<?php endif ?>
|
||||
/>
|
@ -0,0 +1 @@
|
||||
<?php if ($form->get('multipart')): ?>enctype="multipart/form-data"<?php endif ?>
|
@ -1,6 +1,6 @@
|
||||
<?php if ($field->hasErrors()): ?>
|
||||
<?php if ($errors): ?>
|
||||
<ul>
|
||||
<?php foreach ($field->getErrors() as $error): ?>
|
||||
<?php foreach ($errors as $error): ?>
|
||||
<li><?php echo $view['translator']->trans(
|
||||
$error->getMessageTemplate(),
|
||||
$error->getMessageParameters(),
|
@ -0,0 +1 @@
|
||||
<label for="<?php echo $view->escape($id) ?>"><?php echo $view->escape($view['translator']->trans($label)) ?></label>
|
@ -0,0 +1,5 @@
|
||||
<?php foreach ($form->getChildren() as $child): ?>
|
||||
<?php if (!$child->isRendered()): ?>
|
||||
<?php echo $view['form']->row($child) ?>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
@ -1,5 +1,5 @@
|
||||
<div>
|
||||
<?php echo $view['form']->label($field) ?>
|
||||
<?php echo $view['form']->errors($field) ?>
|
||||
<?php echo $view['form']->render($field) ?>
|
||||
<?php echo $view['form']->label($form) ?>
|
||||
<?php echo $view['form']->errors($form) ?>
|
||||
<?php echo $view['form']->widget($form) ?>
|
||||
</div>
|
@ -0,0 +1,7 @@
|
||||
<input
|
||||
<?php echo $view['form']->attributes() ?>
|
||||
name="<?php echo $view->escape($name) ?>"
|
||||
value="<?php echo $view->escape($value) ?>"
|
||||
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($required): ?>required="required"<?php endif ?>
|
||||
/>
|
@ -1,10 +0,0 @@
|
||||
<input type="file"
|
||||
id="<?php echo $field['file']->getId() ?>"
|
||||
name="<?php echo $field['file']->getName() ?>"
|
||||
<?php if ($field['file']->isDisabled()): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($field['file']->isRequired()): ?>required="required"<?php endif ?>
|
||||
<?php echo $view['form']->attributes($attr) ?>
|
||||
/>
|
||||
|
||||
<?php echo $view['form']->render($field['token']) ?>
|
||||
<?php echo $view['form']->render($field['original_name']) ?>
|
@ -0,0 +1,11 @@
|
||||
<div<?php echo $view['form']->attributes() ?>>
|
||||
<input type="file"
|
||||
id="<?php echo $view->escape($form['file']->get('id')) ?>"
|
||||
name="<?php echo $view->escape($form['file']->get('name')) ?>"
|
||||
<?php if ($form['file']->get('disabled')): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($form['file']->get('required')): ?>required="required"<?php endif ?>
|
||||
/>
|
||||
|
||||
<?php echo $view['form']->widget($form['token']) ?>
|
||||
<?php echo $view['form']->widget($form['name']) ?>
|
||||
</div>
|
@ -1,9 +0,0 @@
|
||||
<?php echo $view['form']->errors($field) ?>
|
||||
|
||||
<div>
|
||||
<?php foreach ($field->getVisibleFields() as $child): ?>
|
||||
<?php echo $view['form']->row($child) ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<?php echo $view['form']->hidden($field) ?>
|
@ -0,0 +1,8 @@
|
||||
<div<?php echo $view['form']->attributes() ?>>
|
||||
<?php echo $view['form']->errors($form); ?>
|
||||
<?php foreach ($form->getChildren() as $child): ?>
|
||||
<?php echo $view['form']->row($child); ?>
|
||||
<?php endforeach; ?>
|
||||
<?php echo $view['form']->rest($form) ?>
|
||||
</div>
|
||||
|
@ -1,3 +0,0 @@
|
||||
<?php foreach ($field->getAllHiddenFields() as $child): ?>
|
||||
<?php echo $view['form']->render($child) ?>
|
||||
<?php endforeach; ?>
|
@ -1,7 +0,0 @@
|
||||
<input type="hidden"
|
||||
id="<?php echo $field->getId() ?>"
|
||||
name="<?php echo $field->getName() ?>"
|
||||
value="<?php echo $field->getDisplayedData() ?>"
|
||||
<?php if ($field->isDisabled()): ?>disabled="disabled"<?php endif ?>
|
||||
<?php echo $view['form']->attributes($attr) ?>
|
||||
/>
|
@ -0,0 +1 @@
|
||||
<?php echo $view['form']->widget($form) ?>
|
@ -0,0 +1,6 @@
|
||||
<input type="hidden"
|
||||
<?php echo $view['form']->attributes() ?>
|
||||
name="<?php echo $view->escape($name) ?>"
|
||||
value="<?php echo $view->escape($value) ?>"
|
||||
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
|
||||
/>
|
@ -0,0 +1,7 @@
|
||||
<input type="number"
|
||||
<?php echo $view['form']->attributes() ?>
|
||||
name="<?php echo $view->escape($name) ?>"
|
||||
value="<?php echo $view->escape($value) ?>"
|
||||
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($required): ?>required="required"<?php endif ?>
|
||||
/>
|
@ -1 +0,0 @@
|
||||
<label for="<?php echo $field->getId() ?>"><?php echo $view['translator']->trans($label) ?></label>
|
@ -1,4 +0,0 @@
|
||||
<?php echo str_replace('{{ widget }}',
|
||||
$view['form']->render($field, array(), array(), 'FrameworkBundle:Form:number_field.html.php'),
|
||||
$field->getPattern()
|
||||
) ?>
|
@ -0,0 +1,4 @@
|
||||
<?php echo str_replace('{{ widget }}',
|
||||
$view['form']->render('FrameworkBundle:Form:number_widget.html.php'),
|
||||
$money_pattern
|
||||
) ?>
|
@ -1,8 +0,0 @@
|
||||
<input type="number"
|
||||
id="<?php echo $field->getId() ?>"
|
||||
name="<?php echo $field->getName() ?>"
|
||||
value="<?php echo $field->getDisplayedData() ?>"
|
||||
<?php if ($field->isDisabled()): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($field->isRequired()): ?>required="required"<?php endif ?>
|
||||
<?php echo $view['form']->attributes($attr) ?>
|
||||
/>
|
@ -0,0 +1,8 @@
|
||||
<input type="text"
|
||||
<?php echo $view['form']->attributes() ?>
|
||||
name="<?php echo $view->escape($name) ?>"
|
||||
value="<?php echo $view->escape($value) ?>"
|
||||
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($required): ?>required="required"<?php endif ?>
|
||||
<?php if ($max_length): ?>maxlength="<?php echo $max_length ?>"<?php endif ?>
|
||||
/>
|
@ -1,9 +0,0 @@
|
||||
<input type="password"
|
||||
id="<?php echo $field->getId() ?>"
|
||||
name="<?php echo $field->getName() ?>"
|
||||
value="<?php echo $field->getDisplayedData() ?>"
|
||||
<?php if ($field->isDisabled()): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($field->isRequired()): ?>required="required"<?php endif ?>
|
||||
<?php // FIXME if (!isset($attr['maxlength']) && $field->getMaxLength() > 0) $attr['maxlength'] = $field->getMaxLength() ?>
|
||||
<?php echo $view['form']->attributes($attr) ?>
|
||||
/>
|
@ -0,0 +1,8 @@
|
||||
<input type="password"
|
||||
<?php echo $view['form']->attributes() ?>
|
||||
name="<?php echo $view->escape($name) ?>"
|
||||
value="<?php echo $view->escape($value) ?>"
|
||||
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($required): ?>required="required"<?php endif ?>
|
||||
<?php if ($max_length): ?>maxlength="<?php echo $max_length ?>"<?php endif ?>
|
||||
/>
|
@ -1 +0,0 @@
|
||||
<?php echo $view['form']->render($field, array(), array(), 'FrameworkBundle:Form:number_field.html.php') ?> %
|
@ -0,0 +1 @@
|
||||
<?php echo $view['form']->render('FrameworkBundle:Form:number_widget.html.php') ?> %
|
@ -1,9 +0,0 @@
|
||||
<input type="radio"
|
||||
id="<?php echo $field->getId() ?>"
|
||||
name="<?php echo $field->getName() ?>"
|
||||
<?php if ($field->hasValue()): ?>value="<?php echo $field->getValue() ?>"<?php endif ?>
|
||||
<?php if ($field->isDisabled()): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($field->isRequired()): ?>required="required"<?php endif ?>
|
||||
<?php if ($field->isChecked()): ?>checked="checked"<?php endif ?>
|
||||
<?php echo $view['form']->attributes($attr) ?>
|
||||
/>
|
@ -0,0 +1,8 @@
|
||||
<input type="radio"
|
||||
<?php echo $view['form']->attributes() ?>
|
||||
name="<?php echo $view->escape($name) ?>"
|
||||
value="<?php echo $view->escape($value) ?>"
|
||||
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($required): ?>required="required"<?php endif ?>
|
||||
<?php if ($checked): ?>checked="checked"<?php endif ?>
|
||||
/>
|
@ -0,0 +1,5 @@
|
||||
<?php echo $view['form']->errors($form) ?>
|
||||
|
||||
<?php foreach ($form->getChildren() as $child): ?>
|
||||
<?php echo $view['form']->row($child); ?>
|
||||
<?php endforeach; ?>
|
@ -1,9 +0,0 @@
|
||||
<input type="text"
|
||||
id="<?php echo $field->getId() ?>"
|
||||
name="<?php echo $field->getName() ?>"
|
||||
value="<?php echo $field->getDisplayedData() ?>"
|
||||
<?php if ($field->isDisabled()): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($field->isRequired()): ?>required="required"<?php endif ?>
|
||||
<?php if (!isset($attr['maxlength']) && $field->getMaxLength() > 0) $attr['maxlength'] = $field->getMaxLength() ?>
|
||||
<?php echo $view['form']->attributes($attr) ?>
|
||||
/>
|
@ -0,0 +1,8 @@
|
||||
<input type="text"
|
||||
<?php echo $view['form']->attributes() ?>
|
||||
name="<?php echo $view->escape($name) ?>"
|
||||
value="<?php echo $view->escape($value) ?>"
|
||||
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($required): ?>required="required"<?php endif ?>
|
||||
<?php if ($max_length): ?>maxlength="<?php echo $max_length ?>"<?php endif ?>
|
||||
/>
|
@ -1,9 +0,0 @@
|
||||
<textarea
|
||||
id="<?php echo $field->getId() ?>"
|
||||
name="<?php echo $field->getName() ?>"
|
||||
<?php if ($field->isDisabled()): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($field->isRequired()): ?>required="required"<?php endif ?>
|
||||
<?php echo $view['form']->attributes($attr) ?>
|
||||
><?php
|
||||
echo $view->escape($field->getDisplayedData())
|
||||
?></textarea>
|
@ -0,0 +1,6 @@
|
||||
<textarea
|
||||
<?php echo $view['form']->attributes() ?>
|
||||
name="<?php echo $view->escape($name) ?>"
|
||||
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($required): ?>required="required"<?php endif ?>
|
||||
><?php echo $view->escape($value) ?></textarea>
|
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
// There should be no spaces between the colons and the widgets, that's why
|
||||
// this block is written in a single PHP tag
|
||||
echo $view['form']->render($field['hour'], array('size' => 1));
|
||||
echo ':';
|
||||
echo $view['form']->render($field['minute'], array('size' => 1));
|
||||
|
||||
if ($field->isWithSeconds()) {
|
||||
echo ':';
|
||||
echo $view['form']->render($field['second'], array('size' => 1));
|
||||
}
|
||||
?>
|
@ -0,0 +1,14 @@
|
||||
<div<?php echo $view['form']->attributes() ?>>
|
||||
<?php
|
||||
// There should be no spaces between the colons and the widgets, that's why
|
||||
// this block is written in a single PHP tag
|
||||
echo $view['form']->widget($form['hour'], array('attr' => array('size' => 1)));
|
||||
echo ':';
|
||||
echo $view['form']->widget($form['minute'], array('attr' => array('size' => 1)));
|
||||
|
||||
if ($with_seconds) {
|
||||
echo ':';
|
||||
echo $view['form']->widget($form['second'], array('attr' => array('size' => 1)));
|
||||
}
|
||||
?>
|
||||
</div>
|
@ -1,8 +0,0 @@
|
||||
<input type="url"
|
||||
id="<?php echo $field->getId() ?>"
|
||||
name="<?php echo $field->getName() ?>"
|
||||
value="<?php echo $field->getDisplayedData() ?>"
|
||||
<?php if ($field->isDisabled()): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($field->isRequired()): ?>required="required"<?php endif ?>
|
||||
<?php echo $view['form']->attributes($attr) ?>
|
||||
/>
|
@ -0,0 +1,7 @@
|
||||
<input type="url"
|
||||
<?php echo $view['form']->attributes() ?>
|
||||
name="<?php echo $view->escape($name) ?>"
|
||||
value="<?php echo $view->escape($value) ?>"
|
||||
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
|
||||
<?php if ($required): ?>required="required"<?php endif ?>
|
||||
/>
|
@ -12,12 +12,12 @@
|
||||
namespace Symfony\Bundle\FrameworkBundle\Templating\Helper;
|
||||
|
||||
use Symfony\Component\Templating\Helper\Helper;
|
||||
use Symfony\Component\Form\FieldInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
|
||||
use Symfony\Component\Templating\EngineInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\Exception\FormException;
|
||||
|
||||
/**
|
||||
* Form is a factory that wraps Form instances.
|
||||
*
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
@ -28,82 +28,45 @@ class FormHelper extends Helper
|
||||
|
||||
protected $engine;
|
||||
|
||||
protected $varStack = array();
|
||||
|
||||
public function __construct(EngineInterface $engine)
|
||||
{
|
||||
$this->engine = $engine;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
public function attributes()
|
||||
{
|
||||
return 'form';
|
||||
}
|
||||
$html = '';
|
||||
$attr = array();
|
||||
|
||||
public function attributes($attributes)
|
||||
{
|
||||
if ($attributes instanceof \Traversable) {
|
||||
$attributes = iterator_to_array($attributes);
|
||||
}
|
||||
if (count($this->varStack) > 0) {
|
||||
$vars = end($this->varStack);
|
||||
|
||||
return implode('', array_map(array($this, 'attributesCallback'), array_keys($attributes), array_values($attributes)));
|
||||
}
|
||||
if (isset($vars['attr'])) {
|
||||
$attr = $vars['attr'];
|
||||
}
|
||||
|
||||
private function attribute($name, $value)
|
||||
{
|
||||
return sprintf('%s="%s"', $name, true === $value ? $name : $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares an attribute key and value for HTML representation.
|
||||
*
|
||||
* It removes empty attributes, except for the value one.
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
* @param string $value The attribute value
|
||||
*
|
||||
* @return string The HTML representation of the HTML key attribute pair.
|
||||
*/
|
||||
private function attributesCallback($name, $value)
|
||||
{
|
||||
if (false === $value || null === $value || ('' === $value && 'value' != $name)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return ' '.$this->attribute($name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the form tag.
|
||||
*
|
||||
* This method only renders the opening form tag.
|
||||
* You need to close it after the form rendering.
|
||||
*
|
||||
* This method takes into account the multipart widgets.
|
||||
*
|
||||
* @param string $url The URL for the action
|
||||
* @param array $attributes An array of HTML attributes
|
||||
*
|
||||
* @return string An HTML representation of the opening form tag
|
||||
*/
|
||||
public function enctype(/*Form */$form)
|
||||
{
|
||||
return $form->isMultipart() ? ' enctype="multipart/form-data"' : '';
|
||||
}
|
||||
|
||||
public function render(/*FieldInterface */$field, array $attributes = array(), array $parameters = array(), $template = null)
|
||||
{
|
||||
if (null === $template) {
|
||||
$template = $this->lookupTemplate($field);
|
||||
|
||||
if (null === $template) {
|
||||
throw new \RuntimeException(sprintf('Unable to find a template to render the "%s" widget.', $field->getKey()));
|
||||
if (isset($vars['id'])) {
|
||||
$attr['id'] = $vars['id'];
|
||||
}
|
||||
}
|
||||
|
||||
return trim($this->engine->render($template, array(
|
||||
'field' => $field,
|
||||
'attr' => $attributes,
|
||||
'params' => $parameters,
|
||||
)));
|
||||
foreach ($attr as $k => $v) {
|
||||
$html .= ' '.$this->engine->escape($k).'="'.$this->engine->escape($v).'"';
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function enctype(FormView $view)
|
||||
{
|
||||
return $this->renderSection($view, 'enctype');
|
||||
}
|
||||
|
||||
public function widget(FormView $view, array $variables = array())
|
||||
{
|
||||
return trim($this->renderSection($view, 'widget', $variables));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,85 +75,89 @@ class FormHelper extends Helper
|
||||
* @param FieldInterface $field
|
||||
* @return string
|
||||
*/
|
||||
public function row(/*FieldInterface*/ $field, $template = null)
|
||||
public function row(FormView $view, array $variables = array())
|
||||
{
|
||||
if (null === $template) {
|
||||
$template = 'FrameworkBundle:Form:field_row.html.php';
|
||||
}
|
||||
|
||||
return $this->engine->render($template, array(
|
||||
'field' => $field,
|
||||
));
|
||||
return $this->renderSection($view, 'row', $variables);
|
||||
}
|
||||
|
||||
public function label(/*FieldInterface */$field, $label = false, array $parameters = array(), $template = null)
|
||||
public function label(FormView $view, $label = null)
|
||||
{
|
||||
if (null === $template) {
|
||||
$template = 'FrameworkBundle:Form:label.html.php';
|
||||
}
|
||||
|
||||
return $this->engine->render($template, array(
|
||||
'field' => $field,
|
||||
'params' => $parameters,
|
||||
'label' => $label ? $label : ucfirst(strtolower(str_replace('_', ' ', $field->getKey())))
|
||||
));
|
||||
return $this->renderSection($view, 'label', null === $label ? array() : array('label' => $label));
|
||||
}
|
||||
|
||||
public function errors(/*FieldInterface */$field, array $parameters = array(), $template = null)
|
||||
public function errors(FormView $view)
|
||||
{
|
||||
if (null === $template) {
|
||||
$template = 'FrameworkBundle:Form:errors.html.php';
|
||||
}
|
||||
|
||||
return $this->engine->render($template, array(
|
||||
'field' => $field,
|
||||
'params' => $parameters,
|
||||
));
|
||||
return $this->renderSection($view, 'errors');
|
||||
}
|
||||
|
||||
public function hidden(/*FormInterface */$form, array $parameters = array(), $template = null)
|
||||
public function rest(FormView $view, array $variables = array())
|
||||
{
|
||||
if (null === $template) {
|
||||
$template = 'FrameworkBundle:Form:hidden.html.php';
|
||||
}
|
||||
|
||||
return $this->engine->render($template, array(
|
||||
'field' => $form,
|
||||
'params' => $parameters,
|
||||
));
|
||||
return $this->renderSection($view, 'rest', $variables);
|
||||
}
|
||||
|
||||
protected function lookupTemplate(/*FieldInterface */$field)
|
||||
protected function renderSection(FormView $view, $section, array $variables = array())
|
||||
{
|
||||
$fqClassName = get_class($field);
|
||||
$template = null;
|
||||
$blocks = $view->get('types');
|
||||
|
||||
if (isset(self::$cache[$fqClassName])) {
|
||||
return self::$cache[$fqClassName];
|
||||
}
|
||||
foreach ($blocks as &$block) {
|
||||
$block = $block.'_'.$section;
|
||||
$template = $this->lookupTemplate($block);
|
||||
|
||||
// find a template for the given class or one of its parents
|
||||
$currentFqClassName = $fqClassName;
|
||||
|
||||
do {
|
||||
$parts = explode('\\', $currentFqClassName);
|
||||
$className = array_pop($parts);
|
||||
|
||||
$underscoredName = strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr($className, '_', '.')));
|
||||
|
||||
if ($this->engine->exists($guess = 'FrameworkBundle:Form:'.$underscoredName.'.html.php')) {
|
||||
$template = $guess;
|
||||
if ($template) {
|
||||
break;
|
||||
}
|
||||
|
||||
$currentFqClassName = get_parent_class($currentFqClassName);
|
||||
} while (null === $template && false !== $currentFqClassName);
|
||||
|
||||
if (null === $template && $field instanceof FormInterface) {
|
||||
$template = 'FrameworkBundle:Form:form.html.php';
|
||||
}
|
||||
|
||||
self::$cache[$fqClassName] = $template;
|
||||
if (!$template) {
|
||||
throw new FormException(sprintf('Unable to render form as none of the following blocks exist: "%s".', implode('", "', $blocks)));
|
||||
}
|
||||
|
||||
if ('widget' === $section || 'row' === $section) {
|
||||
$view->setRendered(true);
|
||||
}
|
||||
|
||||
return $this->render($template, array_merge($view->all(), $variables));
|
||||
}
|
||||
|
||||
public function render($template, array $variables = array())
|
||||
{
|
||||
array_push($this->varStack, array_merge(
|
||||
count($this->varStack) > 0 ? end($this->varStack) : array(),
|
||||
$variables
|
||||
));
|
||||
|
||||
$html = $this->engine->render($template, end($this->varStack));
|
||||
|
||||
array_pop($this->varStack);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
protected function lookupTemplate($templateName)
|
||||
{
|
||||
if (isset(self::$cache[$templateName])) {
|
||||
return self::$cache[$templateName];
|
||||
}
|
||||
|
||||
$template = $templateName.'.html.php';
|
||||
/*
|
||||
if ($this->templateDir) {
|
||||
$template = $this->templateDir.':'.$template;
|
||||
}
|
||||
*/
|
||||
$template = 'FrameworkBundle:Form:'.$template;
|
||||
if (!$this->engine->exists($template)) {
|
||||
$template = false;
|
||||
}
|
||||
|
||||
self::$cache[$templateName] = $template;
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'form';
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
<?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\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures;
|
||||
|
||||
use Symfony\Component\Templating\TemplateNameParserInterface;
|
||||
use Symfony\Component\Templating\TemplateReference;
|
||||
|
||||
class StubTemplateNameParser implements TemplateNameParserInterface
|
||||
{
|
||||
private $root;
|
||||
|
||||
public function __construct($root)
|
||||
{
|
||||
$this->root = $root;
|
||||
}
|
||||
|
||||
public function parse($name)
|
||||
{
|
||||
$parts = explode(':', $name);
|
||||
$name = $parts[count($parts)-1];
|
||||
|
||||
return new TemplateReference($this->root.'/'.$name, 'php');
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
<?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\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures;
|
||||
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
|
||||
class StubTranslator implements TranslatorInterface
|
||||
{
|
||||
public function trans($id, array $parameters = array(), $domain = null, $locale = null)
|
||||
{
|
||||
return '[trans]'.$id.'[/trans]';
|
||||
}
|
||||
|
||||
public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)
|
||||
{
|
||||
return '[trans]'.$id.'[/trans]';
|
||||
}
|
||||
|
||||
public function setLocale($locale)
|
||||
{
|
||||
}
|
||||
|
||||
public function getLocale()
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
<?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\Bundle\FrameworkBundle\Tests\Templating\Helper;
|
||||
|
||||
require_once __DIR__.'/Fixtures/StubTemplateNameParser.php';
|
||||
require_once __DIR__.'/Fixtures/StubTranslator.php';
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Templating\Helper\FormHelper;
|
||||
use Symfony\Bundle\FrameworkBundle\Templating\Helper\TranslatorHelper;
|
||||
use Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTemplateNameParser;
|
||||
use Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTranslator;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Templating\PhpEngine;
|
||||
use Symfony\Component\Templating\TemplateNameParser;
|
||||
use Symfony\Component\Templating\Loader\FilesystemLoader;
|
||||
use Symfony\Tests\Component\Form\AbstractDivLayoutTest;
|
||||
|
||||
class FormHelperTest extends AbstractDivLayoutTest
|
||||
{
|
||||
protected $helper;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$root = realpath(__DIR__.'/../../../Resources/views/Form');
|
||||
$templateNameParser = new StubTemplateNameParser($root);
|
||||
$loader = new FilesystemLoader(array());
|
||||
$engine = new PhpEngine($templateNameParser, $loader);
|
||||
|
||||
$this->helper = new FormHelper($engine);
|
||||
|
||||
$engine->setHelpers(array(
|
||||
$this->helper,
|
||||
new TranslatorHelper(new StubTranslator()),
|
||||
));
|
||||
}
|
||||
|
||||
protected function renderEnctype(FormView $view)
|
||||
{
|
||||
return (string)$this->helper->enctype($view);
|
||||
}
|
||||
|
||||
protected function renderLabel(FormView $view, $label = null)
|
||||
{
|
||||
return (string)$this->helper->label($view, $label);
|
||||
}
|
||||
|
||||
protected function renderErrors(FormView $view)
|
||||
{
|
||||
return (string)$this->helper->errors($view);
|
||||
}
|
||||
|
||||
protected function renderWidget(FormView $view, array $vars = array())
|
||||
{
|
||||
return (string)$this->helper->widget($view, $vars);
|
||||
}
|
||||
|
||||
protected function renderRow(FormView $view, array $vars = array())
|
||||
{
|
||||
return (string)$this->helper->row($view, $vars);
|
||||
}
|
||||
|
||||
protected function renderRest(FormView $view, array $vars = array())
|
||||
{
|
||||
return (string)$this->helper->rest($view, $vars);
|
||||
}
|
||||
|
||||
}
|
@ -59,11 +59,11 @@ class Configuration implements ConfigurationInterface
|
||||
->children()
|
||||
->arrayNode('resources')
|
||||
->addDefaultsIfNotSet()
|
||||
->defaultValue(array('TwigBundle::form.html.twig'))
|
||||
->defaultValue(array('TwigBundle:Form:div_layout.html.twig'))
|
||||
->validate()
|
||||
->always()
|
||||
->then(function($v){
|
||||
return array_merge(array('TwigBundle::form.html.twig'), $v);
|
||||
return array_merge(array('TwigBundle:Form:div_layout.html.twig'), $v);
|
||||
})
|
||||
->end()
|
||||
->prototype('scalar')->end()
|
||||
|
@ -1,300 +0,0 @@
|
||||
<?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\Bundle\TwigBundle\Extension;
|
||||
|
||||
use Symfony\Component\Form\Form;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FieldInterface;
|
||||
use Symfony\Component\Form\CollectionField;
|
||||
use Symfony\Component\Form\HybridField;
|
||||
use Symfony\Bundle\TwigBundle\TokenParser\FormThemeTokenParser;
|
||||
|
||||
/**
|
||||
* FormExtension extends Twig with form capabilities.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
*/
|
||||
class FormExtension extends \Twig_Extension
|
||||
{
|
||||
protected $resources;
|
||||
protected $templates;
|
||||
protected $environment;
|
||||
protected $themes;
|
||||
|
||||
public function __construct(array $resources = array())
|
||||
{
|
||||
$this->themes = new \SplObjectStorage();
|
||||
$this->resources = $resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function initRuntime(\Twig_Environment $environment)
|
||||
{
|
||||
$this->environment = $environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a theme for a given field.
|
||||
*
|
||||
* @param FieldInterface $field A FieldInterface instance
|
||||
* @param array $resources An array of resources
|
||||
*/
|
||||
public function setTheme(FieldInterface $field, array $resources)
|
||||
{
|
||||
$this->themes->attach($field, $resources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token parser instance to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_TokenParser instances
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return array(
|
||||
// {% form_theme form "SomeBungle::widgets.twig" %}
|
||||
new FormThemeTokenParser(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getFunctions()
|
||||
{
|
||||
return array(
|
||||
'form_enctype' => new \Twig_Function_Method($this, 'renderEnctype', array('is_safe' => array('html'))),
|
||||
'form_field' => new \Twig_Function_Method($this, 'renderField', array('is_safe' => array('html'))),
|
||||
'form_hidden' => new \Twig_Function_Method($this, 'renderHidden', array('is_safe' => array('html'))),
|
||||
'form_errors' => new \Twig_Function_Method($this, 'renderErrors', array('is_safe' => array('html'))),
|
||||
'form_label' => new \Twig_Function_Method($this, 'renderLabel', array('is_safe' => array('html'))),
|
||||
'form_data' => new \Twig_Function_Method($this, 'renderData', array('is_safe' => array('html'))),
|
||||
'form_row' => new \Twig_Function_Method($this, 'renderRow', array('is_safe' => array('html'))),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the HTML enctype in the form tag, if necessary
|
||||
*
|
||||
* Example usage in Twig templates:
|
||||
*
|
||||
* <form action="..." method="post" {{ form_enctype(form) }}>
|
||||
*
|
||||
* @param Form $form The form for which to render the encoding type
|
||||
*/
|
||||
public function renderEnctype(Form $form)
|
||||
{
|
||||
return $form->isMultipart() ? 'enctype="multipart/form-data"' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a field row.
|
||||
*
|
||||
* @param FieldInterface $field The field to render as a row
|
||||
*/
|
||||
public function renderRow(FieldInterface $field)
|
||||
{
|
||||
return $this->render($field, 'field_row', array(
|
||||
'child' => $field,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the HTML for an individual form field
|
||||
*
|
||||
* Example usage in Twig:
|
||||
*
|
||||
* {{ form_field(field) }}
|
||||
*
|
||||
* You can pass attributes element during the call:
|
||||
*
|
||||
* {{ form_field(field, {'class': 'foo'}) }}
|
||||
*
|
||||
* Some fields also accept additional variables as parameters:
|
||||
*
|
||||
* {{ form_field(field, {}, {'separator': '+++++'}) }}
|
||||
*
|
||||
* @param FieldInterface $field The field to render
|
||||
* @param array $attributes HTML attributes passed to the template
|
||||
* @param array $parameters Additional variables passed to the template
|
||||
* @param array|string $resources A resource or array of resources
|
||||
*/
|
||||
public function renderField(FieldInterface $field, array $attributes = array(), array $parameters = array(), $resources = null)
|
||||
{
|
||||
if (null !== $resources && !is_array($resources)) {
|
||||
$resources = array($resources);
|
||||
}
|
||||
|
||||
return $this->render($field, 'field', array(
|
||||
'field' => $field,
|
||||
'attr' => $attributes,
|
||||
'params' => $parameters,
|
||||
), $resources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders all hidden fields of the given field group
|
||||
*
|
||||
* @param FormInterface $group The field group
|
||||
* @param array $params Additional variables passed to the
|
||||
* template
|
||||
*/
|
||||
public function renderHidden(FormInterface $group, array $parameters = array())
|
||||
{
|
||||
return $this->render($group, 'hidden', array(
|
||||
'field' => $group,
|
||||
'params' => $parameters,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the errors of the given field
|
||||
*
|
||||
* @param FieldInterface $field The field to render the errors for
|
||||
* @param array $params Additional variables passed to the template
|
||||
*/
|
||||
public function renderErrors(FieldInterface $field, array $parameters = array())
|
||||
{
|
||||
return $this->render($field, 'errors', array(
|
||||
'field' => $field,
|
||||
'params' => $parameters,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the label of the given field
|
||||
*
|
||||
* @param FieldInterface $field The field to render the label for
|
||||
* @param array $params Additional variables passed to the template
|
||||
*/
|
||||
public function renderLabel(FieldInterface $field, $label = null, array $parameters = array())
|
||||
{
|
||||
return $this->render($field, 'label', array(
|
||||
'field' => $field,
|
||||
'params' => $parameters,
|
||||
'label' => null !== $label ? $label : ucfirst(strtolower(str_replace('_', ' ', $field->getKey()))),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the widget data of the given field
|
||||
*
|
||||
* @param FieldInterface $field The field to render the data for
|
||||
*/
|
||||
public function renderData(FieldInterface $field)
|
||||
{
|
||||
return $field->getData();
|
||||
}
|
||||
|
||||
protected function render(FieldInterface $field, $name, array $arguments, array $resources = null)
|
||||
{
|
||||
if ('field' === $name) {
|
||||
list($name, $template) = $this->getWidget($field, $resources);
|
||||
} else {
|
||||
$template = $this->getTemplate($field, $name);
|
||||
}
|
||||
|
||||
return $template->renderBlock($name, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FieldInterface $field The field to get the widget for
|
||||
* @param array $resources An array of template resources
|
||||
* @return array
|
||||
*/
|
||||
protected function getWidget(FieldInterface $field, array $resources = null)
|
||||
{
|
||||
$class = get_class($field);
|
||||
$templates = $this->getTemplates($field, $resources);
|
||||
|
||||
// find a template for the given class or one of its parents
|
||||
do {
|
||||
$parts = explode('\\', $class);
|
||||
$c = array_pop($parts);
|
||||
|
||||
// convert the base class name (e.g. TextareaField) to underscores (e.g. textarea_field)
|
||||
$underscore = strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr($c, '_', '.')));
|
||||
|
||||
if (isset($templates[$underscore])) {
|
||||
return array($underscore, $templates[$underscore]);
|
||||
}
|
||||
} while (false !== $class = get_parent_class($class));
|
||||
|
||||
throw new \RuntimeException(sprintf('Unable to render the "%s" field.', $field->getKey()));
|
||||
}
|
||||
|
||||
protected function getTemplate(FieldInterface $field, $name, array $resources = null)
|
||||
{
|
||||
$templates = $this->getTemplates($field, $resources);
|
||||
|
||||
return $templates[$name];
|
||||
}
|
||||
|
||||
protected function getTemplates(FieldInterface $field, array $resources = null)
|
||||
{
|
||||
// templates are looked for in the following resources:
|
||||
// * resources provided directly into the function call
|
||||
// * resources from the themes (and its parents)
|
||||
// * default resources
|
||||
|
||||
// defaults
|
||||
$all = $this->resources;
|
||||
|
||||
// themes
|
||||
$parent = $field;
|
||||
do {
|
||||
if (isset($this->themes[$parent])) {
|
||||
$all = array_merge($all, $this->themes[$parent]);
|
||||
}
|
||||
} while ($parent = $parent->getParent());
|
||||
|
||||
// local
|
||||
$all = array_merge($all, null !== $resources ? (array) $resources : array());
|
||||
|
||||
$templates = array();
|
||||
foreach ($all as $resource) {
|
||||
if (!$resource instanceof \Twig_Template) {
|
||||
$resource = $this->environment->loadTemplate($resource);
|
||||
}
|
||||
|
||||
$blocks = array();
|
||||
foreach ($this->getBlockNames($resource) as $name) {
|
||||
$blocks[$name] = $resource;
|
||||
}
|
||||
|
||||
$templates = array_replace($templates, $blocks);
|
||||
}
|
||||
|
||||
return $templates;
|
||||
}
|
||||
|
||||
protected function getBlockNames($resource)
|
||||
{
|
||||
$names = $resource->getBlockNames();
|
||||
$parent = $resource;
|
||||
while (false !== $parent = $parent->getParent(array())) {
|
||||
$names = array_merge($names, $parent->getBlockNames());
|
||||
}
|
||||
|
||||
return array_unique($names);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'form';
|
||||
}
|
||||
}
|
@ -52,7 +52,7 @@
|
||||
<tag name="twig.extension" />
|
||||
</service>
|
||||
|
||||
<service id="twig.extension.form" class="Symfony\Bundle\TwigBundle\Extension\FormExtension" public="false">
|
||||
<service id="twig.extension.form" class="Symfony\Bridge\Twig\Extension\FormExtension" public="false">
|
||||
<tag name="twig.extension" />
|
||||
<argument>%twig.form.resources%</argument>
|
||||
</service>
|
||||
|
@ -0,0 +1,265 @@
|
||||
{% block field_rows %}
|
||||
{% spaceless %}
|
||||
{{ form_errors(form) }}
|
||||
{% for child in form.children %}
|
||||
{{ form_row(child) }}
|
||||
{% endfor %}
|
||||
{% endspaceless %}
|
||||
{% endblock field_rows %}
|
||||
|
||||
{% block field_enctype %}
|
||||
{% spaceless %}
|
||||
{% if multipart %}enctype="multipart/form-data"{% endif %}
|
||||
{% endspaceless %}
|
||||
{% endblock field_enctype %}
|
||||
|
||||
{% block field_errors %}
|
||||
{% spaceless %}
|
||||
{% if errors|length > 0 %}
|
||||
<ul>
|
||||
{% for error in errors %}
|
||||
<li>{{ error.messageTemplate|trans(error.messageParameters, 'validators') }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endspaceless %}
|
||||
{% endblock field_errors %}
|
||||
|
||||
{% block field_rest %}
|
||||
{% spaceless %}
|
||||
{% for child in form.children %}
|
||||
{% if not child.rendered %}
|
||||
{{ form_row(child) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endspaceless %}
|
||||
{% endblock field_rest %}
|
||||
|
||||
{% block field_label %}
|
||||
{% spaceless %}
|
||||
<label for="{{ id }}">{{ label|trans }}</label>
|
||||
{% endspaceless %}
|
||||
{% endblock field_label %}
|
||||
|
||||
{% block attributes %}
|
||||
{% spaceless %}
|
||||
id="{{ id }}" name="{{ name }}"{% if read_only %} disabled="disabled"{% endif %}{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}
|
||||
{% for attrname,attrvalue in attr %}{{attrname}}="{{attrvalue}}" {% endfor %}
|
||||
{% endspaceless %}
|
||||
{% endblock attributes %}
|
||||
|
||||
{% block field_widget %}
|
||||
{% spaceless %}
|
||||
{% set type = type|default('text') %}
|
||||
<input type="{{ type }}" {{ block('attributes') }} value="{{ value }}" />
|
||||
{% endspaceless %}
|
||||
{% endblock field_widget %}
|
||||
|
||||
{% block text_widget %}
|
||||
{% spaceless %}
|
||||
{% set type = type|default('text') %}
|
||||
{{ block('field_widget') }}
|
||||
{% endspaceless %}
|
||||
{% endblock text_widget %}
|
||||
|
||||
{% block password_widget %}
|
||||
{% spaceless %}
|
||||
{% set type = type|default('password') %}
|
||||
{{ block('field_widget') }}
|
||||
{% endspaceless %}
|
||||
{% endblock password_widget %}
|
||||
|
||||
{% block hidden_widget %}
|
||||
{% set type = type|default('hidden') %}
|
||||
{{ block('field_widget') }}
|
||||
{% endblock hidden_widget %}
|
||||
|
||||
{% block csrf_widget %}
|
||||
{% if not form.hasParent or not form.getParent.hasParent %}
|
||||
{% set type = type|default('hidden') %}
|
||||
{{ block('field_widget') }}
|
||||
{% endif %}
|
||||
{% endblock csrf_widget %}
|
||||
|
||||
{% block hidden_row %}
|
||||
{{ form_widget(form) }}
|
||||
{% endblock hidden_row %}
|
||||
|
||||
{% block textarea_widget %}
|
||||
{% spaceless %}
|
||||
<textarea {{ block('attributes') }}>{{ value }}</textarea>
|
||||
{% endspaceless %}
|
||||
{% endblock textarea_widget %}
|
||||
|
||||
{% block options %}
|
||||
{% spaceless %}
|
||||
{% for choice, label in options %}
|
||||
{% if form.choiceGroup(label) %}
|
||||
<optgroup label="{{ choice }}">
|
||||
{% for nestedChoice, nestedLabel in label %}
|
||||
<option value="{{ nestedChoice }}"{% if form.choiceSelected(nestedChoice) %} selected="selected"{% endif %}>{{ nestedLabel }}</option>
|
||||
{% endfor %}
|
||||
</optgroup>
|
||||
{% else %}
|
||||
<option value="{{ choice }}"{% if form.choiceSelected(choice) %} selected="selected"{% endif %}>{{ label }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endspaceless %}
|
||||
{% endblock options %}
|
||||
|
||||
{% block choice_widget %}
|
||||
{% spaceless %}
|
||||
{% if expanded %}
|
||||
<div {{ block('attributes') }}>
|
||||
{% for choice, child in form %}
|
||||
{{ form_widget(child) }}
|
||||
{{ form_label(child) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<select {{ block('attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
|
||||
{% if not multiple and not required %}
|
||||
<option value="">{{ empty_value }}</option>
|
||||
{% endif %}
|
||||
{% if preferred_choices|length > 0 %}
|
||||
{% set options = preferred_choices %}
|
||||
{{ block('options') }}
|
||||
<option disabled="disabled">{{ separator }}</option>
|
||||
{% endif %}
|
||||
{% set options = choices %}
|
||||
{{ block('options') }}
|
||||
</select>
|
||||
{% endif %}
|
||||
{% endspaceless %}
|
||||
{% endblock choice_widget %}
|
||||
|
||||
{% block checkbox_widget %}
|
||||
{% spaceless %}
|
||||
<input type="checkbox" {{ block('attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
|
||||
{% endspaceless %}
|
||||
{% endblock checkbox_widget %}
|
||||
|
||||
{% block radio_widget %}
|
||||
{% spaceless %}
|
||||
<input type="radio" {{ block('attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
|
||||
{% endspaceless %}
|
||||
{% endblock radio_widget %}
|
||||
|
||||
{% block datetime_widget %}
|
||||
{% spaceless %}
|
||||
<div {{ block('attributes') }}>
|
||||
{{ form_errors(form.date) }}
|
||||
{{ form_errors(form.time) }}
|
||||
{{ form_widget(form.date) }}
|
||||
{{ form_widget(form.time) }}
|
||||
</div>
|
||||
{% endspaceless %}
|
||||
{% endblock datetime_widget %}
|
||||
|
||||
{% block date_widget %}
|
||||
{% spaceless %}
|
||||
{% if widget == 'text' %}
|
||||
{{ block('text_widget') }}
|
||||
{% else %}
|
||||
<div {{ block('attributes') }}>
|
||||
{{ date_pattern|replace({
|
||||
'{{ year }}': form_widget(form.year),
|
||||
'{{ month }}': form_widget(form.month),
|
||||
'{{ day }}': form_widget(form.day),
|
||||
})|raw }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endspaceless %}
|
||||
{% endblock date_widget %}
|
||||
|
||||
{% block time_widget %}
|
||||
{% spaceless %}
|
||||
<div {{ block('attributes') }}>
|
||||
{{ form_widget(form.hour, { 'attr': { 'size': '1' } }) }}:{{ form_widget(form.minute, { 'attr': { 'size': '1' } }) }}{% if with_seconds %}:{{ form_widget(form.second, { 'attr': { 'size': '1' } }) }}{% endif %}
|
||||
</div>
|
||||
{% endspaceless %}
|
||||
{% endblock time_widget %}
|
||||
|
||||
{% block number_widget %}
|
||||
{% spaceless %}
|
||||
{# type="number" doesn't work with floats #}
|
||||
{% set type = type|default('text') %}
|
||||
{{ block('field_widget') }}
|
||||
{% endspaceless %}
|
||||
{% endblock number_widget %}
|
||||
|
||||
{% block integer_widget %}
|
||||
{% spaceless %}
|
||||
{% set type = type|default('number') %}
|
||||
{{ block('field_widget') }}
|
||||
{% endspaceless %}
|
||||
{% endblock integer_widget %}
|
||||
|
||||
{% block money_widget %}
|
||||
{% spaceless %}
|
||||
{{ money_pattern|replace({ '{{ widget }}': block('field_widget') })|raw }}
|
||||
{% endspaceless %}
|
||||
{% endblock money_widget %}
|
||||
|
||||
{% block url_widget %}
|
||||
{% spaceless %}
|
||||
{% set type = type|default('url') %}
|
||||
{{ block('field_widget') }}
|
||||
{% endspaceless %}
|
||||
{% endblock url_widget %}
|
||||
|
||||
{% block percent_widget %}
|
||||
{% spaceless %}
|
||||
{% set type = type|default('text') %}
|
||||
{{ block('field_widget') }} %
|
||||
{% endspaceless %}
|
||||
{% endblock percent_widget %}
|
||||
|
||||
{% block file_widget %}
|
||||
{% spaceless %}
|
||||
<div {{ block('attributes') }}>
|
||||
{{ form_widget(form.file) }}
|
||||
{{ form_widget(form.token) }}
|
||||
{{ form_widget(form.name) }}
|
||||
</div>
|
||||
{% endspaceless %}
|
||||
{% endblock file_widget %}
|
||||
|
||||
{% block collection_widget %}
|
||||
{% spaceless %}
|
||||
{{ block('form_widget') }}
|
||||
{% endspaceless %}
|
||||
{% endblock collection_widget %}
|
||||
|
||||
{% block repeated_row %}
|
||||
{% spaceless %}
|
||||
{{ block('field_rows') }}
|
||||
{% endspaceless %}
|
||||
{% endblock repeated_row %}
|
||||
|
||||
|
||||
{% block field_row %}
|
||||
{% spaceless %}
|
||||
<div>
|
||||
{{ form_label(form) }}
|
||||
{{ form_errors(form) }}
|
||||
{{ form_widget(form) }}
|
||||
</div>
|
||||
{% endspaceless %}
|
||||
{% endblock field_row %}
|
||||
|
||||
{% block form_widget %}
|
||||
{% spaceless %}
|
||||
<div {{ block('attributes') }}>
|
||||
{{ block('field_rows') }}
|
||||
{{ form_rest(form) }}
|
||||
</div>
|
||||
{% endspaceless %}
|
||||
{% endblock form_widget %}
|
||||
|
||||
{% block email_widget %}
|
||||
{% spaceless %}
|
||||
{% set type = type|default('email') %}
|
||||
{{ block('field_widget') }}
|
||||
{% endspaceless %}
|
||||
{% endblock email_widget %}
|
@ -0,0 +1,52 @@
|
||||
{% extends "TwigBundle:Form:div_layout.html.twig" %}
|
||||
|
||||
{% block field_row %}
|
||||
{% spaceless %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ form_label(form) }}
|
||||
</td>
|
||||
<td>
|
||||
{{ form_errors(form) }}
|
||||
{{ form_widget(form) }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endspaceless %}
|
||||
{% endblock field_row %}
|
||||
|
||||
{% block form_errors %}
|
||||
{% spaceless %}
|
||||
{% if errors|length > 0 %}
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
{{ block('field_errors') }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endspaceless %}
|
||||
{% endblock form_errors %}
|
||||
|
||||
{% block hidden_row %}
|
||||
{% spaceless %}
|
||||
<tr style="display: none">
|
||||
<td colspan="2">
|
||||
{{ form_widget(form) }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endspaceless %}
|
||||
{% endblock hidden_row %}
|
||||
|
||||
{% block repeated_errors %}
|
||||
{% spaceless %}
|
||||
{{ block('form_errors') }}
|
||||
{% endspaceless %}
|
||||
{% endblock repeated_errors %}
|
||||
|
||||
{% block form_widget %}
|
||||
{% spaceless %}
|
||||
<table {{ block('attributes') }}>
|
||||
{{ block('field_rows') }}
|
||||
{{ form_rest(form) }}
|
||||
</table>
|
||||
{% endspaceless %}
|
||||
{% endblock form_widget %}
|
@ -1,203 +0,0 @@
|
||||
{% block field_row %}
|
||||
{% spaceless %}
|
||||
<div>
|
||||
{# TODO: would be nice to rename this variable to "field" #}
|
||||
{{ form_label(child) }}
|
||||
{{ form_errors(child) }}
|
||||
{{ form_field(child) }}
|
||||
</div>
|
||||
{% endspaceless %}
|
||||
{% endblock field_row %}
|
||||
|
||||
{% block form %}
|
||||
{% spaceless %}
|
||||
{{ form_errors(field) }}
|
||||
{% for child in field.visibleFields %}
|
||||
{{ block('field_row') }}
|
||||
{% endfor %}
|
||||
{{ form_hidden(field) }}
|
||||
{% endspaceless %}
|
||||
{% endblock form %}
|
||||
|
||||
{% block errors %}
|
||||
{% spaceless %}
|
||||
{% if field.hasErrors %}
|
||||
<ul>
|
||||
{% for error in field.errors %}
|
||||
<li>{% trans error.messageTemplate with error.messageParameters from 'validators' %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endspaceless %}
|
||||
{% endblock errors %}
|
||||
|
||||
{% block hidden %}
|
||||
{% spaceless %}
|
||||
{% for child in field.allHiddenFields %}
|
||||
{{ form_field(child) }}
|
||||
{% endfor %}
|
||||
{% endspaceless %}
|
||||
{% endblock hidden %}
|
||||
|
||||
{% block label %}
|
||||
{% spaceless %}
|
||||
<label for="{{ field.id }}">{% trans label %}</label>
|
||||
{% endspaceless %}
|
||||
{% endblock label %}
|
||||
|
||||
{% block attributes %}
|
||||
{% spaceless %}
|
||||
{% for key, value in attr %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
{% endspaceless %}
|
||||
{% endblock attributes %}
|
||||
|
||||
{% block field_attributes %}
|
||||
{% spaceless %}
|
||||
id="{{ field.id }}" name="{{ field.name }}"{% if field.disabled %} disabled="disabled"{% endif %}{% if field.required %} required="required"{% endif %}
|
||||
{{ block('attributes') }}
|
||||
{% endspaceless %}
|
||||
{% endblock field_attributes %}
|
||||
|
||||
{% block text_field %}
|
||||
{% spaceless %}
|
||||
{% if attr.type is defined and attr.type != "text" %}
|
||||
<input {{ block('field_attributes') }} value="{{ field.displayedData }}" />
|
||||
{% else %}
|
||||
{% set attr = attr|merge({ 'maxlength': attr.maxlength|default(field.maxlength) }) %}
|
||||
<input type="text" {{ block('field_attributes') }} value="{{ field.displayedData }}" />
|
||||
{% endif %}
|
||||
{% endspaceless %}
|
||||
{% endblock text_field %}
|
||||
|
||||
{% block password_field %}
|
||||
{% spaceless %}
|
||||
{% set attr = attr|merge({ 'maxlength': attr.maxlength|default(field.maxlength) }) %}
|
||||
<input type="password" {{ block('field_attributes') }} value="{{ field.displayedData }}" />
|
||||
{% endspaceless %}
|
||||
{% endblock password_field %}
|
||||
|
||||
{% block hidden_field %}
|
||||
{% spaceless %}
|
||||
<input type="hidden" id="{{ field.id }}" name="{{ field.name }}"{% if field.disabled %} disabled="disabled"{% endif %} value="{{ field.displayedData }}" />
|
||||
{% endspaceless %}
|
||||
{% endblock hidden_field %}
|
||||
|
||||
{% block textarea_field %}
|
||||
{% spaceless %}
|
||||
<textarea {{ block('field_attributes') }}>{{ field.displayedData }}</textarea>
|
||||
{% endspaceless %}
|
||||
{% endblock textarea_field %}
|
||||
|
||||
{% block options %}
|
||||
{% spaceless %}
|
||||
{% for choice, label in options %}
|
||||
{% if field.isChoiceGroup(label) %}
|
||||
<optgroup label="{{ choice }}">
|
||||
{% for nestedChoice, nestedLabel in label %}
|
||||
<option value="{{ nestedChoice }}"{% if field.isChoiceSelected(nestedChoice) %} selected="selected"{% endif %}>{{ nestedLabel }}</option>
|
||||
{% endfor %}
|
||||
</optgroup>
|
||||
{% else %}
|
||||
<option value="{{ choice }}"{% if field.isChoiceSelected(choice) %} selected="selected"{% endif %}>{{ label }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endspaceless %}
|
||||
{% endblock options %}
|
||||
|
||||
{% block choice_field %}
|
||||
{% spaceless %}
|
||||
{% if field.isExpanded %}
|
||||
{% for choice, child in field %}
|
||||
{{ form_field(child) }}
|
||||
<label for="{{ child.id }}">{{ field.label(choice) }}</label>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<select {{ block('field_attributes') }}{% if field.isMultipleChoice %} multiple="multiple"{% endif %}>
|
||||
{% if field.preferredChoices|length > 0 %}
|
||||
{% set options = field.preferredChoices %}
|
||||
{{ block('options') }}
|
||||
<option disabled="disabled">{{ params.separator|default('-------------------') }}</option>
|
||||
{% endif %}
|
||||
{% set options = field.otherChoices %}
|
||||
{{ block('options') }}
|
||||
</select>
|
||||
{% endif %}
|
||||
{% endspaceless %}
|
||||
{% endblock choice_field %}
|
||||
|
||||
{% block checkbox_field %}
|
||||
{% spaceless %}
|
||||
<input type="checkbox" {{ block('field_attributes') }}{% if field.hasValue %} value="{{ field.value }}"{% endif %}{% if field.ischecked %} checked="checked"{% endif %} />
|
||||
{% endspaceless %}
|
||||
{% endblock checkbox_field %}
|
||||
|
||||
{% block radio_field %}
|
||||
{% spaceless %}
|
||||
<input type="radio" {{ block('field_attributes') }}{% if field.hasValue %} value="{{ field.value }}"{% endif %}{% if field.ischecked %} checked="checked"{% endif %} />
|
||||
{% endspaceless %}
|
||||
{% endblock radio_field %}
|
||||
|
||||
{% block date_time_field %}
|
||||
{% spaceless %}
|
||||
{{ form_errors(field.date) }}
|
||||
{{ form_errors(field.time) }}
|
||||
{{ form_field(field.date) }}
|
||||
{{ form_field(field.time) }}
|
||||
{% endspaceless %}
|
||||
{% endblock date_time_field %}
|
||||
|
||||
{% block date_field %}
|
||||
{% spaceless %}
|
||||
{% if field.isField %}
|
||||
{{ block('text_field') }}
|
||||
{% else %}
|
||||
{{ field.pattern|replace({ '{{ year }}': form_field(field.year), '{{ month }}': form_field(field.month), '{{ day }}': form_field(field.day) })|raw }}
|
||||
{% endif %}
|
||||
{% endspaceless %}
|
||||
{% endblock date_field %}
|
||||
|
||||
{% block time_field %}
|
||||
{% spaceless %}
|
||||
{% if field.isField %}{% set attr = attr|merge({ 'size': 1 }) %}{% endif %}
|
||||
{{ form_field(field.hour, attr) }}:{{ form_field(field.minute, attr) }}{% if field.isWithSeconds %}:{{ form_field(field.second, attr) }}{% endif %}
|
||||
{% endspaceless %}
|
||||
{% endblock time_field %}
|
||||
|
||||
{% block number_field %}
|
||||
{% spaceless %}
|
||||
{% set attr = attr|merge({ 'type': 'number' }) %}
|
||||
{{ block('text_field') }}
|
||||
{% endspaceless %}
|
||||
{% endblock number_field %}
|
||||
|
||||
{% block money_field %}
|
||||
{% spaceless %}
|
||||
{{ field.pattern|replace({ '{{ widget }}': block('number_field') })|raw }}
|
||||
{% endspaceless %}
|
||||
{% endblock money_field %}
|
||||
|
||||
{% block url_field %}
|
||||
{% spaceless %}
|
||||
{% set attr = attr|merge({ 'type': 'url' }) %}
|
||||
{{ block('text_field') }}
|
||||
{% endspaceless %}
|
||||
{% endblock url_field %}
|
||||
|
||||
{% block percent_field %}
|
||||
{% spaceless %}
|
||||
{{ block('text_field') }} %
|
||||
{% endspaceless %}
|
||||
{% endblock percent_field %}
|
||||
|
||||
{% block file_field %}
|
||||
{% spaceless %}
|
||||
{% set group = field %}
|
||||
{% set field = group.file %}
|
||||
<input type="file" {{ block('field_attributes') }} />
|
||||
{{ form_field(group.token) }}
|
||||
{{ form_field(group.original_name) }}
|
||||
{% endspaceless %}
|
||||
{% endblock file_field %}
|
||||
|
@ -35,7 +35,7 @@ class TwigExtensionTest extends TestCase
|
||||
|
||||
$this->assertEquals('Twig_Environment', $container->getParameter('twig.class'), '->load() loads the twig.xml file');
|
||||
$this->assertFalse($container->getDefinition('twig.cache_warmer')->hasTag('kernel.cache_warmer'), '->load() does not enable cache warming by default');
|
||||
$this->assertContains('TwigBundle::form.html.twig', $container->getParameter('twig.form.resources'), '->load() includes default template for form resources');
|
||||
$this->assertContains('TwigBundle:Form:div_layout.html.twig', $container->getParameter('twig.form.resources'), '->load() includes default template for form resources');
|
||||
|
||||
// Twig options
|
||||
$options = $container->getParameter('twig.options');
|
||||
@ -65,7 +65,7 @@ class TwigExtensionTest extends TestCase
|
||||
|
||||
// Form resources
|
||||
$resources = $container->getParameter('twig.form.resources');
|
||||
$this->assertContains('TwigBundle::form.html.twig', $resources, '->load() includes default template for form resources');
|
||||
$this->assertContains('TwigBundle:Form:div_layout.html.twig', $resources, '->load() includes default template for form resources');
|
||||
$this->assertContains('MyBundle::form.html.twig', $resources, '->load() merges new templates into form resources');
|
||||
|
||||
// Globals
|
||||
|
@ -1,35 +0,0 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* A field for entering a birthday date
|
||||
*
|
||||
* This field is a preconfigured DateField with allowed years between the
|
||||
* current year and 120 years in the past.
|
||||
*
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
*/
|
||||
class BirthdayField extends DateField
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$currentYear = date('Y');
|
||||
|
||||
$this->addOption('years', range($currentYear-120, $currentYear));
|
||||
|
||||
parent::configure();
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* A checkbox field for selecting boolean values.
|
||||
*
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
*/
|
||||
class CheckboxField extends ToggleField
|
||||
{
|
||||
/**
|
||||
* Available options:
|
||||
*
|
||||
* * value: The value of the input checkbox. If the checkbox is checked,
|
||||
* this value will be posted as the value of the field.
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addOption('value', '1');
|
||||
|
||||
parent::configure();
|
||||
}
|
||||
}
|
@ -1,294 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\Form\Exception\InvalidOptionsException;
|
||||
|
||||
/**
|
||||
* Lets the user select between different choices.
|
||||
*
|
||||
* Available options:
|
||||
*
|
||||
* * choices: An array of key-value pairs that will represent the choices
|
||||
* * preferred_choices: An array of choices (by key) that should be displayed
|
||||
* above all other options in the field
|
||||
* * empty_value: If set to a non-false value, an "empty" option will
|
||||
* be added to the top of the countries choices. A
|
||||
* common value might be "Choose a country". Default: false.
|
||||
*
|
||||
* The multiple and expanded options control exactly which HTML element
|
||||
* that should be used to render this field:
|
||||
*
|
||||
* * expanded = false, multiple = false A drop-down select element
|
||||
* * expanded = false, multiple = true A multiple select element
|
||||
* * expanded = true, multiple = false A series of input radio elements
|
||||
* * expanded = true, multiple = true A series of input checkboxes
|
||||
*
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
*/
|
||||
class ChoiceField extends HybridField
|
||||
{
|
||||
/**
|
||||
* Stores the preferred choices with the choices as keys
|
||||
* @var array
|
||||
*/
|
||||
protected $preferredChoices = array();
|
||||
|
||||
/**
|
||||
* Stores the choices
|
||||
* You should only access this property through getChoices()
|
||||
* @var array
|
||||
*/
|
||||
private $choices = array();
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->addRequiredOption('choices');
|
||||
$this->addOption('preferred_choices', array());
|
||||
$this->addOption('multiple', false);
|
||||
$this->addOption('expanded', false);
|
||||
$this->addOption('empty_value', '');
|
||||
|
||||
parent::configure();
|
||||
|
||||
$choices = $this->getOption('choices');
|
||||
|
||||
if (!is_array($choices) && !$choices instanceof \Closure) {
|
||||
throw new InvalidOptionsException('The choices option must be an array or a closure', array('choices'));
|
||||
}
|
||||
|
||||
if (!is_array($this->getOption('preferred_choices'))) {
|
||||
throw new InvalidOptionsException('The preferred_choices option must be an array', array('preferred_choices'));
|
||||
}
|
||||
|
||||
if (count($this->getOption('preferred_choices')) > 0) {
|
||||
$this->preferredChoices = array_flip($this->getOption('preferred_choices'));
|
||||
}
|
||||
|
||||
if ($this->isExpanded()) {
|
||||
$this->setFieldMode(self::FORM);
|
||||
|
||||
$choices = $this->getChoices();
|
||||
|
||||
foreach ($this->preferredChoices as $choice => $_) {
|
||||
$this->add($this->newChoiceField($choice, $choices[$choice]));
|
||||
}
|
||||
|
||||
foreach ($choices as $choice => $value) {
|
||||
if (!isset($this->preferredChoices[$choice])) {
|
||||
$this->add($this->newChoiceField($choice, $value));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->setFieldMode(self::FIELD);
|
||||
}
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
// TESTME
|
||||
$name = parent::getName();
|
||||
|
||||
// Add "[]" to the name in case a select tag with multiple options is
|
||||
// displayed. Otherwise only one of the selected options is sent in the
|
||||
// POST request.
|
||||
if ($this->isMultipleChoice() && !$this->isExpanded()) {
|
||||
$name .= '[]';
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the choices
|
||||
*
|
||||
* If the choices were given as a closure, the closure is executed now.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function initializeChoices()
|
||||
{
|
||||
if (!$this->choices) {
|
||||
$this->choices = $this->getInitializedChoices();
|
||||
|
||||
if (!$this->isRequired()) {
|
||||
$this->choices = array('' => $this->getOption('empty_value')) + $this->choices;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getInitializedChoices()
|
||||
{
|
||||
$choices = $this->getOption('choices');
|
||||
|
||||
if ($choices instanceof \Closure) {
|
||||
$choices = $choices->__invoke();
|
||||
}
|
||||
|
||||
if (!is_array($choices)) {
|
||||
throw new InvalidOptionsException('The "choices" option must be an array or a closure returning an array', array('choices'));
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the choices
|
||||
*
|
||||
* If the choices were given as a closure, the closure is executed on
|
||||
* the first call of this method.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getChoices()
|
||||
{
|
||||
$this->initializeChoices();
|
||||
|
||||
return $this->choices;
|
||||
}
|
||||
|
||||
public function getPreferredChoices()
|
||||
{
|
||||
return array_intersect_key($this->getChoices(), $this->preferredChoices);
|
||||
}
|
||||
|
||||
public function getOtherChoices()
|
||||
{
|
||||
return array_diff_key($this->getChoices(), $this->preferredChoices);
|
||||
}
|
||||
|
||||
public function getLabel($choice)
|
||||
{
|
||||
$choices = $this->getChoices();
|
||||
|
||||
return isset($choices[$choice]) ? $choices[$choice] : null;
|
||||
}
|
||||
|
||||
public function isChoiceGroup($choice)
|
||||
{
|
||||
return is_array($choice) || $choice instanceof \Traversable;
|
||||
}
|
||||
|
||||
public function isChoiceSelected($choice)
|
||||
{
|
||||
return in_array((string) $choice, (array) $this->getDisplayedData(), true);
|
||||
}
|
||||
|
||||
public function isMultipleChoice()
|
||||
{
|
||||
return $this->getOption('multiple');
|
||||
}
|
||||
|
||||
public function isExpanded()
|
||||
{
|
||||
return $this->getOption('expanded');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new field of type radio button or checkbox.
|
||||
*
|
||||
* @param string $choice The key for the option
|
||||
* @param string $label The label for the option
|
||||
*/
|
||||
protected function newChoiceField($choice, $label)
|
||||
{
|
||||
if ($this->isMultipleChoice()) {
|
||||
return new CheckboxField($choice, array(
|
||||
'value' => $choice,
|
||||
));
|
||||
}
|
||||
|
||||
return new RadioField($choice, array(
|
||||
'value' => $choice,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Takes care of converting the input from a single radio button
|
||||
* to an array.
|
||||
*/
|
||||
public function submit($value)
|
||||
{
|
||||
if (!$this->isMultipleChoice() && $this->isExpanded()) {
|
||||
$value = null === $value ? array() : array($value => true);
|
||||
}
|
||||
|
||||
parent::submit($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a single choice or an array of choices to a format appropriate
|
||||
* for the nested checkboxes/radio buttons.
|
||||
*
|
||||
* The result is an array with the options as keys and true/false as values,
|
||||
* depending on whether a given option is selected. If this field is rendered
|
||||
* as select tag, the value is not modified.
|
||||
*
|
||||
* @param mixed $value An array if "multiple" is set to true, a scalar
|
||||
* value otherwise.
|
||||
* @return mixed An array if "expanded" or "multiple" is set to true,
|
||||
* a scalar value otherwise.
|
||||
*/
|
||||
protected function transform($value)
|
||||
{
|
||||
if ($this->isExpanded()) {
|
||||
$value = parent::transform($value);
|
||||
$choices = $this->getChoices();
|
||||
|
||||
foreach ($choices as $choice => $_) {
|
||||
$choices[$choice] = $this->isMultipleChoice()
|
||||
? in_array($choice, (array)$value, true)
|
||||
: ($choice === $value);
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
|
||||
return parent::transform($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a checkbox/radio button array to a single choice or an array
|
||||
* of choices.
|
||||
*
|
||||
* The input value is an array with the choices as keys and true/false as
|
||||
* values, depending on whether a given choice is selected. The output
|
||||
* is an array with the selected choices or a single selected choice.
|
||||
*
|
||||
* @param mixed $value An array if "expanded" or "multiple" is set to true,
|
||||
* a scalar value otherwise.
|
||||
* @return mixed $value An array if "multiple" is set to true, a scalar
|
||||
* value otherwise.
|
||||
*/
|
||||
protected function reverseTransform($value)
|
||||
{
|
||||
if ($this->isExpanded()) {
|
||||
$choices = array();
|
||||
|
||||
foreach ($value as $choice => $selected) {
|
||||
if ($selected) {
|
||||
$choices[] = $choice;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->isMultipleChoice()) {
|
||||
$value = $choices;
|
||||
} else {
|
||||
$value = count($choices) > 0 ? current($choices) : null;
|
||||
}
|
||||
}
|
||||
|
||||
return parent::reverseTransform($value);
|
||||
}
|
||||
}
|
55
src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php
Normal file
55
src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?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\ChoiceList;
|
||||
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
class ArrayChoiceList implements ChoiceListInterface
|
||||
{
|
||||
protected $choices;
|
||||
|
||||
protected $loaded = false;
|
||||
|
||||
public function __construct($choices)
|
||||
{
|
||||
if (!is_array($choices) && !$choices instanceof \Closure) {
|
||||
throw new UnexpectedTypeException($choices, 'array or \Closure');
|
||||
}
|
||||
|
||||
$this->choices = $choices;
|
||||
}
|
||||
|
||||
public function getChoices()
|
||||
{
|
||||
if (!$this->loaded) {
|
||||
$this->load();
|
||||
}
|
||||
|
||||
return $this->choices;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Symfony\Component\Form\ChoiceList\ChoiceListInterface::getChoices
|
||||
*/
|
||||
protected function load()
|
||||
{
|
||||
$this->loaded = true;
|
||||
|
||||
if ($this->choices instanceof \Closure) {
|
||||
$this->choices = $this->choices->__invoke();
|
||||
|
||||
if (!is_array($this->choices)) {
|
||||
throw new UnexpectedTypeException($this->choices, 'array');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<?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\ChoiceList;
|
||||
|
||||
interface ChoiceListInterface
|
||||
{
|
||||
function getChoices();
|
||||
}
|
56
src/Symfony/Component/Form/ChoiceList/MonthChoiceList.php
Normal file
56
src/Symfony/Component/Form/ChoiceList/MonthChoiceList.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?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\ChoiceList;
|
||||
|
||||
class MonthChoiceList extends PaddedChoiceList
|
||||
{
|
||||
private $formatter;
|
||||
|
||||
/**
|
||||
* Generates an array of localized month choices
|
||||
*
|
||||
* @param array $months The month numbers to generate
|
||||
* @return array The localized months respecting the configured
|
||||
* locale and date format
|
||||
*/
|
||||
public function __construct(\IntlDateFormatter $formatter, array $months)
|
||||
{
|
||||
parent::__construct($months, 2, '0', STR_PAD_LEFT);
|
||||
|
||||
$this->formatter = $formatter;
|
||||
}
|
||||
|
||||
protected function load()
|
||||
{
|
||||
parent::load();
|
||||
|
||||
$pattern = $this->formatter->getPattern();
|
||||
$timezone = $this->formatter->getTimezoneId();
|
||||
|
||||
$this->formatter->setTimezoneId(\DateTimeZone::UTC);
|
||||
|
||||
if (preg_match('/M+/', $pattern, $matches)) {
|
||||
$this->formatter->setPattern($matches[0]);
|
||||
|
||||
foreach ($this->choices as $choice => $value) {
|
||||
// It's important to specify the first day of the month here!
|
||||
$this->choices[$choice] = $this->formatter->format(gmmktime(0, 0, 0, $choice, 1));
|
||||
}
|
||||
|
||||
// I'd like to clone the formatter above, but then we get a
|
||||
// segmentation fault, so let's restore the old state instead
|
||||
$this->formatter->setPattern($pattern);
|
||||
}
|
||||
|
||||
$this->formatter->setTimezoneId($timezone);
|
||||
}
|
||||
}
|
53
src/Symfony/Component/Form/ChoiceList/PaddedChoiceList.php
Normal file
53
src/Symfony/Component/Form/ChoiceList/PaddedChoiceList.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?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\ChoiceList;
|
||||
|
||||
class PaddedChoiceList extends ArrayChoiceList
|
||||
{
|
||||
private $padLength;
|
||||
|
||||
private $padString;
|
||||
|
||||
private $padType;
|
||||
|
||||
/**
|
||||
* Generates an array of choices for the given values
|
||||
*
|
||||
* If the values are shorter than $padLength characters, they are padded with
|
||||
* zeros on the left side.
|
||||
*
|
||||
* @param array $values The available choices
|
||||
* @param integer $padLength The length to pad the choices
|
||||
* @return array An array with the input values as keys and the
|
||||
* padded values as values
|
||||
*/
|
||||
public function __construct($values, $padLength, $padString, $padType = STR_PAD_LEFT)
|
||||
{
|
||||
parent::__construct($values);
|
||||
|
||||
$this->padLength = $padLength;
|
||||
$this->padString = $padString;
|
||||
$this->padType = $padType;
|
||||
}
|
||||
|
||||
protected function load()
|
||||
{
|
||||
parent::load();
|
||||
|
||||
$choices = $this->choices;
|
||||
$this->choices = array();
|
||||
|
||||
foreach ($choices as $value) {
|
||||
$this->choices[$value] = str_pad($value, $this->padLength, $this->padString, $this->padType);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,14 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form;
|
||||
namespace Symfony\Component\Form\ChoiceList;
|
||||
|
||||
/**
|
||||
* Represents a field where each timezone is broken down by continent.
|
||||
* Represents a choice list where each timezone is broken down by continent.
|
||||
*
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
*/
|
||||
class TimezoneField extends ChoiceField
|
||||
class TimezoneChoiceList extends ArrayChoiceList
|
||||
{
|
||||
/**
|
||||
* Stores the available timezone choices
|
||||
@ -24,34 +24,13 @@ class TimezoneField extends ChoiceField
|
||||
*/
|
||||
protected static $timezones = array();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function configure()
|
||||
public function __construct()
|
||||
{
|
||||
$this->addOption('choices', self::getTimezoneChoices());
|
||||
|
||||
parent::configure();
|
||||
parent::__construct(array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Preselects the server timezone if the field is empty and required
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getDisplayedData()
|
||||
{
|
||||
$data = parent::getDisplayedData();
|
||||
|
||||
if (null == $data && $this->isRequired()) {
|
||||
$data = date_default_timezone_get();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timezone choices
|
||||
* Loads the timezone choices
|
||||
*
|
||||
* The choices are generated from the ICU function
|
||||
* \DateTimeZone::listIdentifiers(). They are cached during a single request,
|
||||
@ -60,8 +39,10 @@ class TimezoneField extends ChoiceField
|
||||
*
|
||||
* @return array The timezone choices
|
||||
*/
|
||||
protected static function getTimezoneChoices()
|
||||
protected function load()
|
||||
{
|
||||
parent::load();
|
||||
|
||||
if (count(self::$timezones) == 0) {
|
||||
foreach (\DateTimeZone::listIdentifiers() as $timezone) {
|
||||
$parts = explode('/', $timezone);
|
||||
@ -85,6 +66,6 @@ class TimezoneField extends ChoiceField
|
||||
}
|
||||
}
|
||||
|
||||
return self::$timezones;
|
||||
$this->choices = self::$timezones;
|
||||
}
|
||||
}
|
@ -1,148 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\Form\FieldInterface;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
/**
|
||||
* A field group that repeats the given field multiple times over a collection
|
||||
* specified by the property path if the field.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* $form->add(new CollectionField(new TextField('emails')));
|
||||
*
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
*/
|
||||
class CollectionField extends Form
|
||||
{
|
||||
/**
|
||||
* Remembers which fields were removed upon submitting
|
||||
* @var array
|
||||
*/
|
||||
protected $removedFields = array();
|
||||
|
||||
/**
|
||||
* The prototype field for the collection rows
|
||||
* @var FieldInterface
|
||||
*/
|
||||
protected $prototype;
|
||||
|
||||
public function __construct($key, array $options = array())
|
||||
{
|
||||
// This doesn't work with addOption(), because the value of this option
|
||||
// needs to be accessed before Configurable::__construct() is reached
|
||||
// Setting all options in the constructor of the root field
|
||||
// is conceptually flawed
|
||||
if (isset($options['prototype'])) {
|
||||
$this->prototype = $options['prototype'];
|
||||
unset($options['prototype']);
|
||||
}
|
||||
|
||||
parent::__construct($key, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Available options:
|
||||
*
|
||||
* * modifiable: If true, elements in the collection can be added
|
||||
* and removed by the presence of absence of the
|
||||
* corresponding field groups. Field groups could be
|
||||
* added or removed via Javascript and reflected in
|
||||
* the underlying collection. Default: false.
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addOption('modifiable', false);
|
||||
|
||||
if ($this->getOption('modifiable')) {
|
||||
$field = $this->newField('$$key$$', null);
|
||||
// TESTME
|
||||
$field->setRequired(false);
|
||||
$this->add($field);
|
||||
}
|
||||
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
public function setData($collection)
|
||||
{
|
||||
if (!is_array($collection) && !$collection instanceof \Traversable) {
|
||||
throw new UnexpectedTypeException($collection, 'array or \Traversable');
|
||||
}
|
||||
|
||||
foreach ($this as $name => $field) {
|
||||
if (!$this->getOption('modifiable') || '$$key$$' != $name) {
|
||||
$this->remove($name);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($collection as $name => $value) {
|
||||
$this->add($this->newField($name, $name));
|
||||
}
|
||||
|
||||
parent::setData($collection);
|
||||
}
|
||||
|
||||
public function submit($data)
|
||||
{
|
||||
$this->removedFields = array();
|
||||
|
||||
if (null === $data) {
|
||||
$data = array();
|
||||
}
|
||||
|
||||
foreach ($this as $name => $field) {
|
||||
if (!isset($data[$name]) && $this->getOption('modifiable') && '$$key$$' != $name) {
|
||||
$this->remove($name);
|
||||
$this->removedFields[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($data as $name => $value) {
|
||||
if (!isset($this[$name]) && $this->getOption('modifiable')) {
|
||||
$this->add($this->newField($name, $name));
|
||||
}
|
||||
}
|
||||
|
||||
parent::submit($data);
|
||||
}
|
||||
|
||||
protected function writeObject(&$objectOrArray)
|
||||
{
|
||||
parent::writeObject($objectOrArray);
|
||||
|
||||
foreach ($this->removedFields as $name) {
|
||||
unset($objectOrArray[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function newField($key, $propertyPath)
|
||||
{
|
||||
if (null !== $propertyPath) {
|
||||
$propertyPath = '['.$propertyPath.']';
|
||||
}
|
||||
|
||||
if ($this->prototype) {
|
||||
$field = clone $this->prototype;
|
||||
$field->setKey($key);
|
||||
$field->setPropertyPath($propertyPath);
|
||||
} else {
|
||||
$field = new TextField($key, array(
|
||||
'property_path' => $propertyPath,
|
||||
));
|
||||
}
|
||||
|
||||
return $field;
|
||||
}
|
||||
}
|
@ -1,145 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\Form\Exception\MissingOptionsException;
|
||||
use Symfony\Component\Form\Exception\InvalidOptionsException;
|
||||
|
||||
/**
|
||||
* A class configurable via options
|
||||
*
|
||||
* Options can be passed to the constructor of this class. After constructions,
|
||||
* these options cannot be changed anymore. This way, options remain light
|
||||
* weight. There is no need to monitor changes of options.
|
||||
*
|
||||
* If you want options that can change, you're recommended to implement plain
|
||||
* properties with setters and getters.
|
||||
*
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
*/
|
||||
abstract class Configurable
|
||||
{
|
||||
/**
|
||||
* The options and their values
|
||||
* @var array
|
||||
*/
|
||||
private $options = array();
|
||||
|
||||
/**
|
||||
* The names of the valid options
|
||||
* @var array
|
||||
*/
|
||||
private $knownOptions = array();
|
||||
|
||||
/**
|
||||
* The names of the required options
|
||||
* @var array
|
||||
*/
|
||||
private $requiredOptions = array();
|
||||
|
||||
/**
|
||||
* Reads, validates and stores the given options
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
$this->options = array_merge($this->options, $options);
|
||||
|
||||
$this->configure();
|
||||
|
||||
// check option names
|
||||
if ($diff = array_diff_key($this->options, $this->knownOptions)) {
|
||||
throw new InvalidOptionsException(sprintf('%s does not support the following options: "%s".', get_class($this), implode('", "', array_keys($diff))), array_keys($diff));
|
||||
}
|
||||
|
||||
// check required options
|
||||
if ($diff = array_diff_key($this->requiredOptions, $this->options)) {
|
||||
throw new MissingOptionsException(sprintf('%s requires the following options: \'%s\'.', get_class($this), implode('", "', array_keys($diff))), array_keys($diff));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the valid options
|
||||
*
|
||||
* This method should call addOption() or addRequiredOption() for every
|
||||
* accepted option.
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an option value.
|
||||
*
|
||||
* @param string $name The option name
|
||||
*
|
||||
* @return mixed The option value
|
||||
*/
|
||||
public function getOption($name)
|
||||
{
|
||||
return isset($this->options[$name]) ? $this->options[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new option value with a default value.
|
||||
*
|
||||
* @param string $name The option name
|
||||
* @param mixed $value The default value
|
||||
*/
|
||||
protected function addOption($name, $value = null, array $allowedValues = array())
|
||||
{
|
||||
$this->knownOptions[$name] = true;
|
||||
|
||||
if (!array_key_exists($name, $this->options)) {
|
||||
$this->options[$name] = $value;
|
||||
}
|
||||
|
||||
if (count($allowedValues) > 0 && !in_array($this->options[$name], $allowedValues)) {
|
||||
throw new InvalidOptionsException(sprintf('The option "%s" is expected to be one of "%s", but is "%s"', $name, implode('", "', $allowedValues), $this->options[$name]), array($name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a required option.
|
||||
*
|
||||
* @param string $name The option name
|
||||
*/
|
||||
protected function addRequiredOption($name, array $allowedValues = array())
|
||||
{
|
||||
$this->knownOptions[$name] = true;
|
||||
$this->requiredOptions[$name] = true;
|
||||
|
||||
// only test if the option is set, otherwise an error will be thrown
|
||||
// anyway
|
||||
if (isset($this->options[$name]) && count($allowedValues) > 0 && !in_array($this->options[$name], $allowedValues)) {
|
||||
throw new InvalidOptionsException(sprintf('The option "%s" is expected to be one of "%s", but is "%s"', $name, implode('", "', $allowedValues), $this->options[$name]), array($name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the option exists.
|
||||
*
|
||||
* @param string $name The option name
|
||||
*
|
||||
* @return Boolean true if the option is set, false otherwise
|
||||
*/
|
||||
public function hasOption($name)
|
||||
{
|
||||
return isset($this->options[$name]);
|
||||
}
|
||||
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\Locale\Locale;
|
||||
|
||||
/**
|
||||
* A field for selecting from a list of countries.
|
||||
*
|
||||
* @see Symfony\Component\Form\ChoiceField
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
*/
|
||||
class CountryField extends ChoiceField
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->addOption('choices', Locale::getDisplayCountries(\Locale::getDefault()));
|
||||
|
||||
parent::configure();
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ namespace Symfony\Component\Form\CsrfProvider;
|
||||
*
|
||||
* If you want to secure a form submission against CSRF attacks, you could
|
||||
* use the class name of the form as page ID. This way you make sure that the
|
||||
* form can only be submitted to pages that are designed to handle the form,
|
||||
* form can only be bound to pages that are designed to handle the form,
|
||||
* that is, that use the same class name to validate the CSRF token with
|
||||
* isCsrfTokenValid().
|
||||
*
|
||||
|
@ -1,21 +0,0 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Wraps errors in the form data
|
||||
*
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
*/
|
||||
class DataError extends Error
|
||||
{
|
||||
}
|
@ -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\DataMapper;
|
||||
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
|
||||
interface DataMapperInterface
|
||||
{
|
||||
function mapDataToForms($data, array $forms);
|
||||
|
||||
function mapDataToForm($data, FormInterface $form);
|
||||
|
||||
function mapFormsToData(array $forms, &$data);
|
||||
|
||||
function mapFormToData(FormInterface $form, &$data);
|
||||
}
|
85
src/Symfony/Component/Form/DataMapper/PropertyPathMapper.php
Normal file
85
src/Symfony/Component/Form/DataMapper/PropertyPathMapper.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?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\DataMapper;
|
||||
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\Util\VirtualFormAwareIterator;
|
||||
use Symfony\Component\Form\Exception\FormException;
|
||||
|
||||
class PropertyPathMapper implements DataMapperInterface
|
||||
{
|
||||
/**
|
||||
* Stores the class that the data of this form must be instances of
|
||||
* @var string
|
||||
*/
|
||||
private $dataClass;
|
||||
|
||||
public function __construct($dataClass = null)
|
||||
{
|
||||
$this->dataClass = $dataClass;
|
||||
}
|
||||
|
||||
public function mapDataToForms($data, array $forms)
|
||||
{
|
||||
if (!empty($data) && !is_array($data) && !is_object($data)) {
|
||||
throw new \InvalidArgumentException(sprintf('Expected argument of type object or array, %s given', gettype($data)));
|
||||
}
|
||||
|
||||
if (!empty($data)) {
|
||||
if ($this->dataClass && !$data instanceof $this->dataClass) {
|
||||
throw new FormException(sprintf('Form data should be instance of %s', $this->dataClass));
|
||||
}
|
||||
|
||||
$iterator = new VirtualFormAwareIterator($forms);
|
||||
$iterator = new \RecursiveIteratorIterator($iterator);
|
||||
|
||||
foreach ($iterator as $form) {
|
||||
$this->mapDataToForm($data, $form);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function mapDataToForm($data, FormInterface $form)
|
||||
{
|
||||
if (!empty($data)) {
|
||||
if ($form->getAttribute('property_path') !== null) {
|
||||
$form->setData($form->getAttribute('property_path')->getValue($data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function mapFormsToData(array $forms, &$data)
|
||||
{
|
||||
$iterator = new VirtualFormAwareIterator($forms);
|
||||
$iterator = new \RecursiveIteratorIterator($iterator);
|
||||
|
||||
foreach ($iterator as $form) {
|
||||
$this->mapFormToData($form, $data);
|
||||
}
|
||||
}
|
||||
|
||||
public function mapFormToData(FormInterface $form, &$data)
|
||||
{
|
||||
if ($form->getAttribute('property_path') !== null && $form->isSynchronized()) {
|
||||
$propertyPath = $form->getAttribute('property_path');
|
||||
|
||||
// If the data is identical to the value in $data, we are
|
||||
// dealing with a reference
|
||||
$isReference = $form->getData() === $propertyPath->getValue($data);
|
||||
$byReference = $form->getAttribute('by_reference');
|
||||
|
||||
if (!(is_object($data) && $isReference && $byReference)) {
|
||||
$propertyPath->setValue($data, $form->getData());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
<?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\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
class ArrayToBooleanChoicesTransformer implements DataTransformerInterface
|
||||
{
|
||||
private $choiceList;
|
||||
|
||||
public function __construct(ChoiceListInterface $choiceList)
|
||||
{
|
||||
$this->choiceList = $choiceList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a single choice or an array of choices to a format appropriate
|
||||
* for the nested checkboxes/radio buttons.
|
||||
*
|
||||
* The result is an array with the options as keys and true/false as values,
|
||||
* depending on whether a given option is selected. If this field is rendered
|
||||
* as select tag, the value is not modified.
|
||||
*
|
||||
* @param mixed $value An array if "multiple" is set to true, a scalar
|
||||
* value otherwise.
|
||||
* @return mixed An array if "expanded" or "multiple" is set to true,
|
||||
* a scalar value otherwise.
|
||||
*/
|
||||
public function transform($array)
|
||||
{
|
||||
if (null === $array) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if (!is_array($array)) {
|
||||
throw new UnexpectedTypeException($array, 'array');
|
||||
}
|
||||
|
||||
$choices = $this->choiceList->getChoices();
|
||||
|
||||
foreach ($choices as $choice => $_) {
|
||||
$choices[$choice] = in_array($choice, $array, true);
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a checkbox/radio button array to a single choice or an array
|
||||
* of choices.
|
||||
*
|
||||
* The input value is an array with the choices as keys and true/false as
|
||||
* values, depending on whether a given choice is selected. The output
|
||||
* is an array with the selected choices or a single selected choice.
|
||||
*
|
||||
* @param mixed $value An array if "expanded" or "multiple" is set to true,
|
||||
* a scalar value otherwise.
|
||||
* @return mixed $value An array if "multiple" is set to true, a scalar
|
||||
* value otherwise.
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
throw new UnexpectedTypeException($value, 'array');
|
||||
}
|
||||
|
||||
$choices = array();
|
||||
|
||||
foreach ($value as $choice => $selected) {
|
||||
if ($selected) {
|
||||
$choices[] = $choice;
|
||||
}
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
<?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\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\Util\FormUtil;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
class ArrayToChoicesTransformer implements DataTransformerInterface
|
||||
{
|
||||
public function transform($array)
|
||||
{
|
||||
if (null === $array) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if (!is_array($array)) {
|
||||
throw new UnexpectedTypeException($array, 'array');
|
||||
}
|
||||
|
||||
return FormUtil::toArrayKeys($array);
|
||||
}
|
||||
|
||||
public function reverseTransform($array)
|
||||
{
|
||||
if (null === $array) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if (!is_array($array)) {
|
||||
throw new UnexpectedTypeException($array, 'array');
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
<?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\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
|
||||
*/
|
||||
class ArrayToPartsTransformer implements DataTransformerInterface
|
||||
{
|
||||
private $partMapping;
|
||||
|
||||
public function __construct(array $partMapping)
|
||||
{
|
||||
$this->partMapping = $partMapping;
|
||||
}
|
||||
|
||||
public function transform($array)
|
||||
{
|
||||
if (null === $array) {
|
||||
$array = array();
|
||||
}
|
||||
|
||||
if (!is_array($array) ) {
|
||||
throw new UnexpectedTypeException($array, 'array');
|
||||
}
|
||||
|
||||
$result = array();
|
||||
|
||||
foreach ($this->partMapping as $partKey => $originalKeys) {
|
||||
if (empty($array)) {
|
||||
$result[$partKey] = null;
|
||||
} else {
|
||||
$result[$partKey] = array_intersect_key($array, array_flip($originalKeys));
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function reverseTransform($array)
|
||||
{
|
||||
if (!is_array($array) ) {
|
||||
throw new UnexpectedTypeException($array, 'array');
|
||||
}
|
||||
|
||||
$result = array();
|
||||
$emptyKeys = array();
|
||||
|
||||
foreach ($this->partMapping as $partKey => $originalKeys) {
|
||||
if (!empty($array[$partKey])) {
|
||||
foreach ($originalKeys as $originalKey) {
|
||||
if (isset($array[$partKey][$originalKey])) {
|
||||
$result[$originalKey] = $array[$partKey][$originalKey];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$emptyKeys[] = $partKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($emptyKeys) > 0) {
|
||||
if (count($emptyKeys) === count($this->partMapping)) {
|
||||
// All parts empty
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new TransformationFailedException(sprintf(
|
||||
'The keys "%s" should not be empty', implode('", "', $emptyKeys)));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?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\DataTransformer;
|
||||
|
||||
abstract class BaseDateTimeTransformer implements DataTransformerInterface
|
||||
{
|
||||
protected static $formats = array(
|
||||
\IntlDateFormatter::NONE,
|
||||
\IntlDateFormatter::FULL,
|
||||
\IntlDateFormatter::LONG,
|
||||
\IntlDateFormatter::MEDIUM,
|
||||
\IntlDateFormatter::SHORT,
|
||||
);
|
||||
|
||||
protected $inputTimezone;
|
||||
|
||||
protected $outputTimezone;
|
||||
|
||||
public function __construct($inputTimezone = null, $outputTimezone = null)
|
||||
{
|
||||
$this->inputTimezone = $inputTimezone ?: date_default_timezone_get();
|
||||
$this->outputTimezone = $outputTimezone ?: date_default_timezone_get();
|
||||
}
|
||||
}
|
@ -9,9 +9,8 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\ValueTransformer;
|
||||
namespace Symfony\Component\Form\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\Configurable;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
/**
|
||||
@ -20,7 +19,7 @@ use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
* @author Florian Eckerstorfer <florian@eckerstorfer.org>
|
||||
*/
|
||||
class BooleanToStringTransformer extends Configurable implements ValueTransformerInterface
|
||||
class BooleanToStringTransformer implements DataTransformerInterface
|
||||
{
|
||||
/**
|
||||
* Transforms a Boolean into a string.
|
||||
@ -49,6 +48,10 @@ class BooleanToStringTransformer extends Configurable implements ValueTransforme
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_string($value)) {
|
||||
throw new UnexpectedTypeException($value, 'string');
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
<?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\DataTransformer;
|
||||
|
||||
class CallbackTransformer implements DataTransformerInterface
|
||||
{
|
||||
private $transform;
|
||||
|
||||
private $reverseTransform;
|
||||
|
||||
public function __construct(\Closure $transform, \Closure $reverseTransform)
|
||||
{
|
||||
$this->transform = $transform;
|
||||
$this->reverseTransform = $reverseTransform;
|
||||
}
|
||||
|
||||
public function transform($data)
|
||||
{
|
||||
return $this->transform->__invoke($data);
|
||||
}
|
||||
|
||||
public function reverseTransform($data)
|
||||
{
|
||||
return $this->reverseTransform->__invoke($data);
|
||||
}
|
||||
}
|
@ -9,14 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\ValueTransformer;
|
||||
namespace Symfony\Component\Form\DataTransformer;
|
||||
|
||||
/**
|
||||
* Passes a value through multiple value transformers
|
||||
*
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
*/
|
||||
class ValueTransformerChain implements ValueTransformerInterface
|
||||
class DataTransformerChain implements DataTransformerInterface
|
||||
{
|
||||
/**
|
||||
* The value transformers
|
@ -9,14 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\ValueTransformer;
|
||||
namespace Symfony\Component\Form\DataTransformer;
|
||||
|
||||
/**
|
||||
* Transforms a value between different representations.
|
||||
*
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
*/
|
||||
interface ValueTransformerInterface
|
||||
interface DataTransformerInterface
|
||||
{
|
||||
/**
|
||||
* Transforms a value from the original representation to a transformed representation.
|
||||
@ -24,8 +24,8 @@ interface ValueTransformerInterface
|
||||
* This method is called on two occasions inside a form field:
|
||||
*
|
||||
* 1. When the form field is initialized with the data attached from the datasource (object or array).
|
||||
* 2. When data from a request is bound using {@link Field::submit()} to transform the new input data
|
||||
* back into the renderable format. For example if you have a date field and submit '2009-10-10' onto
|
||||
* 2. When data from a request is bound using {@link Field::bind()} to transform the new input data
|
||||
* back into the renderable format. For example if you have a date field and bind '2009-10-10' onto
|
||||
* it you might accept this value because its easily parsed, but the transformer still writes back
|
||||
* "2009/10/10" onto the form field (for further displaying or other purposes).
|
||||
*
|
||||
@ -42,7 +42,7 @@ interface ValueTransformerInterface
|
||||
* @param mixed $value The value in the original representation
|
||||
* @return mixed The value in the transformed representation
|
||||
* @throws UnexpectedTypeException when the argument is no string
|
||||
* @throws ValueTransformerException when the transformation fails
|
||||
* @throws DataTransformerException when the transformation fails
|
||||
*/
|
||||
function transform($value);
|
||||
|
||||
@ -50,7 +50,7 @@ interface ValueTransformerInterface
|
||||
* Transforms a value from the transformed representation to its original
|
||||
* representation.
|
||||
*
|
||||
* This method is called when {@link Field::submit()} is called to transform the requests tainted data
|
||||
* This method is called when {@link Field::bind()} is called to transform the requests tainted data
|
||||
* into an acceptable format for your data processing/model layer.
|
||||
*
|
||||
* This method must be able to deal with empty values. Usually this will
|
||||
@ -67,7 +67,7 @@ interface ValueTransformerInterface
|
||||
* @param mixed $value The value in the transformed representation
|
||||
* @throws UnexpectedTypeException when the argument is not of the
|
||||
* expected type
|
||||
* @throws ValueTransformerException when the transformation fails
|
||||
* @throws DataTransformerException when the transformation fails
|
||||
*/
|
||||
function reverseTransform($value);
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\ValueTransformer;
|
||||
namespace Symfony\Component\Form\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
@ -27,17 +27,20 @@ use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
*/
|
||||
class DateTimeToArrayTransformer extends BaseDateTimeTransformer
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addOption('input_timezone', date_default_timezone_get());
|
||||
$this->addOption('output_timezone', date_default_timezone_get());
|
||||
$this->addOption('pad', false);
|
||||
$this->addOption('fields', array('year', 'month', 'day', 'hour', 'minute', 'second'));
|
||||
private $pad;
|
||||
|
||||
parent::configure();
|
||||
private $fields;
|
||||
|
||||
public function __construct($inputTimezone = null, $outputTimezone = null, $fields = null, $pad = false)
|
||||
{
|
||||
parent::__construct($inputTimezone, $outputTimezone);
|
||||
|
||||
if (is_null($fields)) {
|
||||
$fields = array('year', 'month', 'day', 'hour', 'minute', 'second');
|
||||
}
|
||||
|
||||
$this->fields = $fields;
|
||||
$this->pad =$pad;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -49,22 +52,22 @@ class DateTimeToArrayTransformer extends BaseDateTimeTransformer
|
||||
public function transform($dateTime)
|
||||
{
|
||||
if (null === $dateTime) {
|
||||
return array(
|
||||
return array_intersect_key(array(
|
||||
'year' => '',
|
||||
'month' => '',
|
||||
'day' => '',
|
||||
'hour' => '',
|
||||
'minute' => '',
|
||||
'second' => '',
|
||||
);
|
||||
), array_flip($this->fields));
|
||||
}
|
||||
|
||||
if (!$dateTime instanceof \DateTime) {
|
||||
throw new UnexpectedTypeException($dateTime, '\DateTime');
|
||||
}
|
||||
|
||||
$inputTimezone = $this->getOption('input_timezone');
|
||||
$outputTimezone = $this->getOption('output_timezone');
|
||||
$inputTimezone = $this->inputTimezone;
|
||||
$outputTimezone = $this->outputTimezone;
|
||||
|
||||
if ($inputTimezone != $outputTimezone) {
|
||||
$dateTime->setTimezone(new \DateTimeZone($outputTimezone));
|
||||
@ -77,9 +80,9 @@ class DateTimeToArrayTransformer extends BaseDateTimeTransformer
|
||||
'hour' => $dateTime->format('H'),
|
||||
'minute' => $dateTime->format('i'),
|
||||
'second' => $dateTime->format('s'),
|
||||
), array_flip($this->getOption('fields')));
|
||||
), array_flip($this->fields));
|
||||
|
||||
if (!$this->getOption('pad')) {
|
||||
if (!$this->pad) {
|
||||
foreach ($result as &$entry) {
|
||||
// remove leading zeros
|
||||
$entry = (string)(int)$entry;
|
||||
@ -101,8 +104,8 @@ class DateTimeToArrayTransformer extends BaseDateTimeTransformer
|
||||
return null;
|
||||
}
|
||||
|
||||
$inputTimezone = $this->getOption('input_timezone');
|
||||
$outputTimezone = $this->getOption('output_timezone');
|
||||
$inputTimezone = $this->inputTimezone;
|
||||
$outputTimezone = $this->outputTimezone;
|
||||
|
||||
if (!is_array($value)) {
|
||||
throw new UnexpectedTypeException($value, 'array');
|
||||
@ -112,6 +115,19 @@ class DateTimeToArrayTransformer extends BaseDateTimeTransformer
|
||||
return null;
|
||||
}
|
||||
|
||||
$emptyFields = array();
|
||||
|
||||
foreach ($this->fields as $field) {
|
||||
if (!isset($value[$field])) {
|
||||
$emptyFields[] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($emptyFields) > 0) {
|
||||
throw new TransformationFailedException(sprintf(
|
||||
'The fields "%s" should not be empty', implode('", "', $emptyFields)));
|
||||
}
|
||||
|
||||
try {
|
||||
$dateTime = new \DateTime(sprintf(
|
||||
'%s-%s-%s %s:%s:%s %s',
|
@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\ValueTransformer;
|
||||
namespace Symfony\Component\Form\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
@ -27,25 +27,32 @@ use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
*/
|
||||
class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function configure()
|
||||
private $dateFormat;
|
||||
|
||||
private $timeFormat;
|
||||
|
||||
public function __construct($inputTimezone = null, $outputTimezone = null, $dateFormat = null, $timeFormat = null)
|
||||
{
|
||||
$this->addOption('date_format', self::MEDIUM);
|
||||
$this->addOption('time_format', self::SHORT);
|
||||
$this->addOption('input_timezone', 'UTC');
|
||||
$this->addOption('output_timezone', 'UTC');
|
||||
parent::__construct($inputTimezone, $outputTimezone);
|
||||
|
||||
if (!in_array($this->getOption('date_format'), self::$formats, true)) {
|
||||
throw new \InvalidArgumentException(sprintf('The option "date_format" is expected to be one of "%s". Is "%s"', implode('", "', self::$formats), $this->getOption('time_format')));
|
||||
if (is_null($dateFormat)) {
|
||||
$dateFormat = \IntlDateFormatter::MEDIUM;
|
||||
}
|
||||
|
||||
if (!in_array($this->getOption('time_format'), self::$formats, true)) {
|
||||
throw new \InvalidArgumentException(sprintf('The option "time_format" is expected to be one of "%s". Is "%s"', implode('", "', self::$formats), $this->getOption('time_format')));
|
||||
if (is_null($timeFormat)) {
|
||||
$timeFormat = \IntlDateFormatter::SHORT;
|
||||
}
|
||||
|
||||
parent::configure();
|
||||
if (!in_array($dateFormat, self::$formats, true)) {
|
||||
throw new \InvalidArgumentException(sprintf('The value $dateFormat is expected to be one of "%s". Is "%s"', implode('", "', self::$formats), $dateFormat));
|
||||
}
|
||||
|
||||
if (!in_array($timeFormat, self::$formats, true)) {
|
||||
throw new \InvalidArgumentException(sprintf('The value $timeFormat is expected to be one of "%s". Is "%s"', implode('", "', self::$formats), $timeFormat));
|
||||
}
|
||||
|
||||
$this->dateFormat = $dateFormat;
|
||||
$this->timeFormat = $timeFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,7 +71,7 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
|
||||
throw new UnexpectedTypeException($dateTime, '\DateTime');
|
||||
}
|
||||
|
||||
$inputTimezone = $this->getOption('input_timezone');
|
||||
$inputTimezone = $this->inputTimezone;
|
||||
|
||||
// convert time to UTC before passing it to the formatter
|
||||
if ('UTC' != $inputTimezone) {
|
||||
@ -88,7 +95,7 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
$inputTimezone = $this->getOption('input_timezone');
|
||||
$inputTimezone = $this->inputTimezone;
|
||||
|
||||
if (!is_string($value)) {
|
||||
throw new UnexpectedTypeException($value, 'string');
|
||||
@ -121,9 +128,9 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
|
||||
*/
|
||||
protected function getIntlDateFormatter()
|
||||
{
|
||||
$dateFormat = $this->getIntlFormatConstant($this->getOption('date_format'));
|
||||
$timeFormat = $this->getIntlFormatConstant($this->getOption('time_format'));
|
||||
$timezone = $this->getOption('output_timezone');
|
||||
$dateFormat = $this->dateFormat;
|
||||
$timeFormat = $this->timeFormat;
|
||||
$timezone = $this->outputTimezone;
|
||||
|
||||
return new \IntlDateFormatter(\Locale::getDefault(), $dateFormat, $timeFormat, $timezone);
|
||||
}
|
@ -9,9 +9,8 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\ValueTransformer;
|
||||
namespace Symfony\Component\Form\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\Configurable;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
/**
|
||||
@ -20,18 +19,15 @@ use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||
* @author Florian Eckerstorfer <florian@eckerstorfer.org>
|
||||
*/
|
||||
class DateTimeToStringTransformer extends Configurable implements ValueTransformerInterface
|
||||
class DateTimeToStringTransformer extends BaseDateTimeTransformer
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addOption('input_timezone', date_default_timezone_get());
|
||||
$this->addOption('output_timezone', date_default_timezone_get());
|
||||
$this->addOption('format', 'Y-m-d H:i:s');
|
||||
private $format;
|
||||
|
||||
parent::configure();
|
||||
public function __construct($inputTimezone = null, $outputTimezone = null, $format = 'Y-m-d H:i:s')
|
||||
{
|
||||
parent::__construct($inputTimezone, $outputTimezone);
|
||||
|
||||
$this->format = $format;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -51,9 +47,9 @@ class DateTimeToStringTransformer extends Configurable implements ValueTransform
|
||||
throw new UnexpectedTypeException($value, '\DateTime');
|
||||
}
|
||||
|
||||
$value->setTimezone(new \DateTimeZone($this->getOption('output_timezone')));
|
||||
$value->setTimezone(new \DateTimeZone($this->outputTimezone));
|
||||
|
||||
return $value->format($this->getOption('format'));
|
||||
return $value->format($this->format);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,8 +68,8 @@ class DateTimeToStringTransformer extends Configurable implements ValueTransform
|
||||
throw new UnexpectedTypeException($value, 'string');
|
||||
}
|
||||
|
||||
$outputTimezone = $this->getOption('output_timezone');
|
||||
$inputTimezone = $this->getOption('input_timezone');
|
||||
$outputTimezone = $this->outputTimezone;
|
||||
$inputTimezone = $this->inputTimezone;
|
||||
|
||||
try {
|
||||
$dateTime = new \DateTime("$value $outputTimezone");
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user