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.
|
* 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 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
|
* Guesses form fields from the metadata of Doctrine 2
|
||||||
*
|
*
|
||||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||||
*/
|
*/
|
||||||
class EntityFieldFactoryGuesser implements FieldFactoryGuesserInterface
|
class EntityTypeGuesser implements TypeGuesserInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The Doctrine 2 entity manager
|
* The Doctrine 2 entity manager
|
||||||
@ -49,7 +53,7 @@ class EntityFieldFactoryGuesser implements FieldFactoryGuesserInterface
|
|||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function guessClass($class, $property)
|
public function guessType($class, $property)
|
||||||
{
|
{
|
||||||
if ($this->isMappedClass($class)) {
|
if ($this->isMappedClass($class)) {
|
||||||
$metadata = $this->em->getClassMetadata($class);
|
$metadata = $this->em->getClassMetadata($class);
|
||||||
@ -58,86 +62,86 @@ class EntityFieldFactoryGuesser implements FieldFactoryGuesserInterface
|
|||||||
$multiple = $metadata->isCollectionValuedAssociation($property);
|
$multiple = $metadata->isCollectionValuedAssociation($property);
|
||||||
$mapping = $metadata->getAssociationMapping($property);
|
$mapping = $metadata->getAssociationMapping($property);
|
||||||
|
|
||||||
return new FieldFactoryClassGuess(
|
return new TypeGuess(
|
||||||
'Symfony\Component\Form\EntityChoiceField',
|
'entity',
|
||||||
array(
|
array(
|
||||||
'em' => $this->em,
|
'em' => $this->em,
|
||||||
'class' => $mapping['targetEntity'],
|
'class' => $mapping['targetEntity'],
|
||||||
'multiple' => $multiple,
|
'multiple' => $multiple,
|
||||||
),
|
),
|
||||||
FieldFactoryGuess::HIGH_CONFIDENCE
|
Guess::HIGH_CONFIDENCE
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
switch ($metadata->getTypeOfField($property))
|
switch ($metadata->getTypeOfField($property))
|
||||||
{
|
{
|
||||||
// case 'array':
|
// case 'array':
|
||||||
// return new FieldFactoryClassGuess(
|
// return new TypeGuess(
|
||||||
// 'Symfony\Component\Form\CollectionField',
|
// 'Collection',
|
||||||
// array(),
|
// array(),
|
||||||
// FieldFactoryGuess::HIGH_CONFIDENCE
|
// Guess::HIGH_CONFIDENCE
|
||||||
// );
|
// );
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
return new FieldFactoryClassGuess(
|
return new TypeGuess(
|
||||||
'Symfony\Component\Form\CheckboxField',
|
'checkbox',
|
||||||
array(),
|
array(),
|
||||||
FieldFactoryGuess::HIGH_CONFIDENCE
|
Guess::HIGH_CONFIDENCE
|
||||||
);
|
);
|
||||||
case 'datetime':
|
case 'datetime':
|
||||||
case 'vardatetime':
|
case 'vardatetime':
|
||||||
case 'datetimetz':
|
case 'datetimetz':
|
||||||
return new FieldFactoryClassGuess(
|
return new TypeGuess(
|
||||||
'Symfony\Component\Form\DateTimeField',
|
'datetime',
|
||||||
array(),
|
array(),
|
||||||
FieldFactoryGuess::HIGH_CONFIDENCE
|
Guess::HIGH_CONFIDENCE
|
||||||
);
|
);
|
||||||
case 'date':
|
case 'date':
|
||||||
return new FieldFactoryClassGuess(
|
return new TypeGuess(
|
||||||
'Symfony\Component\Form\DateField',
|
'date',
|
||||||
array(),
|
array(),
|
||||||
FieldFactoryGuess::HIGH_CONFIDENCE
|
Guess::HIGH_CONFIDENCE
|
||||||
);
|
);
|
||||||
case 'decimal':
|
case 'decimal':
|
||||||
case 'float':
|
case 'float':
|
||||||
return new FieldFactoryClassGuess(
|
return new TypeGuess(
|
||||||
'Symfony\Component\Form\NumberField',
|
'number',
|
||||||
array(),
|
array(),
|
||||||
FieldFactoryGuess::MEDIUM_CONFIDENCE
|
Guess::MEDIUM_CONFIDENCE
|
||||||
);
|
);
|
||||||
case 'integer':
|
case 'integer':
|
||||||
case 'bigint':
|
case 'bigint':
|
||||||
case 'smallint':
|
case 'smallint':
|
||||||
return new FieldFactoryClassGuess(
|
return new TypeGuess(
|
||||||
'Symfony\Component\Form\IntegerField',
|
'integer',
|
||||||
array(),
|
array(),
|
||||||
FieldFactoryGuess::MEDIUM_CONFIDENCE
|
Guess::MEDIUM_CONFIDENCE
|
||||||
);
|
);
|
||||||
case 'string':
|
case 'string':
|
||||||
return new FieldFactoryClassGuess(
|
return new TypeGuess(
|
||||||
'Symfony\Component\Form\TextField',
|
'text',
|
||||||
array(),
|
array(),
|
||||||
FieldFactoryGuess::MEDIUM_CONFIDENCE
|
Guess::MEDIUM_CONFIDENCE
|
||||||
);
|
);
|
||||||
case 'text':
|
case 'text':
|
||||||
return new FieldFactoryClassGuess(
|
return new TypeGuess(
|
||||||
'Symfony\Component\Form\TextareaField',
|
'textarea',
|
||||||
array(),
|
array(),
|
||||||
FieldFactoryGuess::MEDIUM_CONFIDENCE
|
Guess::MEDIUM_CONFIDENCE
|
||||||
);
|
);
|
||||||
case 'time':
|
case 'time':
|
||||||
return new FieldFactoryClassGuess(
|
return new TypeGuess(
|
||||||
'Symfony\Component\Form\TimeField',
|
'time',
|
||||||
array(),
|
array(),
|
||||||
FieldFactoryGuess::HIGH_CONFIDENCE
|
Guess::HIGH_CONFIDENCE
|
||||||
);
|
);
|
||||||
// case 'object': ???
|
// case 'object': ???
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FieldFactoryClassGuess(
|
return new TypeGuess(
|
||||||
'Symfony\Component\Form\TextField',
|
'text',
|
||||||
array(),
|
array(),
|
||||||
FieldFactoryGuess::LOW_CONFIDENCE
|
Guess::LOW_CONFIDENCE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,15 +155,15 @@ class EntityFieldFactoryGuesser implements FieldFactoryGuesserInterface
|
|||||||
|
|
||||||
if ($metadata->hasField($property)) {
|
if ($metadata->hasField($property)) {
|
||||||
if (!$metadata->isNullable($property)) {
|
if (!$metadata->isNullable($property)) {
|
||||||
return new FieldFactoryGuess(
|
return new ValueGuess(
|
||||||
true,
|
true,
|
||||||
FieldFactoryGuess::HIGH_CONFIDENCE
|
Guess::HIGH_CONFIDENCE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FieldFactoryGuess(
|
return new ValueGuess(
|
||||||
false,
|
false,
|
||||||
FieldFactoryGuess::MEDIUM_CONFIDENCE
|
Guess::MEDIUM_CONFIDENCE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,9 +182,9 @@ class EntityFieldFactoryGuesser implements FieldFactoryGuesserInterface
|
|||||||
|
|
||||||
|
|
||||||
if (isset($mapping['length'])) {
|
if (isset($mapping['length'])) {
|
||||||
return new FieldFactoryGuess(
|
return new ValueGuess(
|
||||||
$mapping['length'],
|
$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.
|
* 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.
|
* 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>
|
<parameter key="doctrine.orm.proxy_cache_warmer.class">Symfony\Bundle\DoctrineBundle\CacheWarmer\ProxyCacheWarmer</parameter>
|
||||||
|
|
||||||
<!-- form field factory guesser -->
|
<!-- 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>
|
</parameters>
|
||||||
|
|
||||||
<services>
|
<services>
|
||||||
@ -55,8 +55,13 @@
|
|||||||
<argument type="service" id="service_container" />
|
<argument type="service" id="service_container" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="form.field_factory.doctrine_guesser" class="%form.field_factory.doctrine_guesser.class%" public="false">
|
<service id="form.guesser.doctrine" class="%form.guesser.doctrine.class%" public="false">
|
||||||
<tag name="form.field_factory.guesser" />
|
<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" />
|
<argument type="service" id="doctrine.orm.entity_manager" />
|
||||||
</service>
|
</service>
|
||||||
</services>
|
</services>
|
||||||
|
@ -110,6 +110,7 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
|
|||||||
public function getNotCalledListeners()
|
public function getNotCalledListeners()
|
||||||
{
|
{
|
||||||
$notCalled = array();
|
$notCalled = array();
|
||||||
|
|
||||||
foreach (array_keys($this->getListeners()) as $name) {
|
foreach (array_keys($this->getListeners()) as $name) {
|
||||||
foreach ($this->getListeners($name) as $listener) {
|
foreach ($this->getListeners($name) as $listener) {
|
||||||
if (!isset($this->called[$name.'.'.get_class($listener)])) {
|
if (!isset($this->called[$name.'.'.get_class($listener)])) {
|
||||||
|
@ -16,23 +16,25 @@ use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
|||||||
use Symfony\Component\DependencyInjection\Reference;
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds all services with the tag "form.field_factory_guesser" as argument
|
* Adds all services with the tag "form.guesser" as constructor argument of the
|
||||||
* to the "form.field_factory" service
|
* "form.factory" service
|
||||||
*
|
*
|
||||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||||
*/
|
*/
|
||||||
class AddFieldFactoryGuessersPass implements CompilerPassInterface
|
class AddFormGuessersPass implements CompilerPassInterface
|
||||||
{
|
{
|
||||||
public function process(ContainerBuilder $container)
|
public function process(ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
if (!$container->hasDefinition('form.field_factory')) {
|
if (!$container->hasDefinition('form.factory')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$guessers = array_map(function($id) {
|
$guessers = array();
|
||||||
return new Reference($id);
|
|
||||||
}, array_keys($container->findTaggedServiceIds('form.field_factory.guesser')));
|
|
||||||
|
|
||||||
$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;
|
namespace Symfony\Bundle\FrameworkBundle;
|
||||||
|
|
||||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass;
|
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\TemplatingPass;
|
||||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RegisterKernelListenersPass;
|
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RegisterKernelListenersPass;
|
||||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverPass;
|
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverPass;
|
||||||
@ -77,7 +78,8 @@ class FrameworkBundle extends Bundle
|
|||||||
$container->addCompilerPass(new RegisterKernelListenersPass());
|
$container->addCompilerPass(new RegisterKernelListenersPass());
|
||||||
$container->addCompilerPass(new TemplatingPass());
|
$container->addCompilerPass(new TemplatingPass());
|
||||||
$container->addCompilerPass(new AddConstraintValidatorsPass());
|
$container->addCompilerPass(new AddConstraintValidatorsPass());
|
||||||
$container->addCompilerPass(new AddFieldFactoryGuessersPass());
|
$container->addCompilerPass(new AddFormTypesPass());
|
||||||
|
$container->addCompilerPass(new AddFormGuessersPass());
|
||||||
$container->addCompilerPass(new AddClassesToCachePass());
|
$container->addCompilerPass(new AddClassesToCachePass());
|
||||||
$container->addCompilerPass(new AddClassesToAutoloadMapPass());
|
$container->addCompilerPass(new AddClassesToAutoloadMapPass());
|
||||||
$container->addCompilerPass(new TranslatorPass());
|
$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">
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
<parameters>
|
<parameters>
|
||||||
<parameter key="form.field_factory.class">Symfony\Component\Form\FieldFactory\FieldFactory</parameter>
|
<parameter key="form.factory.class">Symfony\Component\Form\FormFactory</parameter>
|
||||||
<parameter key="form.field_factory.validator_guesser.class">Symfony\Component\Form\FieldFactory\ValidatorFieldFactoryGuesser</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.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.enabled">true</parameter>
|
||||||
<parameter key="form.csrf_protection.field_name">_token</parameter>
|
<parameter key="form.csrf_protection.field_name">_token</parameter>
|
||||||
<parameter key="form.csrf_protection.secret">secret</parameter>
|
<parameter key="form.csrf_protection.secret">secret</parameter>
|
||||||
<parameter key="form.validation_groups">Default</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>
|
</parameters>
|
||||||
|
|
||||||
<services>
|
<services>
|
||||||
|
|
||||||
<!-- FieldFactory -->
|
<!-- FormFactory -->
|
||||||
<service id="form.field_factory" class="%form.field_factory.class%">
|
<service id="form.factory" class="%form.factory.class%">
|
||||||
<!-- All services with tag "form.field_factory.guesser" are inserted here by AddFieldFactoryGuessersPass -->
|
<argument type="service" id="form.type.loader" />
|
||||||
|
<!--
|
||||||
|
All services with tag "form.guesser" are inserted here by
|
||||||
|
AddFormGuessersPass
|
||||||
|
-->
|
||||||
<argument type="collection" />
|
<argument type="collection" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<!-- ValidatorFieldFactoryGuesser -->
|
<!-- ValidatorTypeGuesser -->
|
||||||
<service id="form.field_factory.validator_guesser" class="%form.field_factory.validator_guesser.class%" public="false">
|
<service id="form.guesser.validator" class="%form.guesser.validator.class%" public="false">
|
||||||
<tag name="form.field_factory.guesser" />
|
<tag name="form.guesser" />
|
||||||
<argument type="service" id="validator.mapping.class_metadata_factory" />
|
<argument type="service" id="validator.mapping.class_metadata_factory" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<!-- CsrfProvider -->
|
<!-- CsrfProvider -->
|
||||||
<service id="form.csrf_provider" class="%form.csrf_provider.class%">
|
<service id="form.csrf_provider" class="%form.csrf_provider.class%">
|
||||||
<argument type="service" id="session" />
|
<argument type="service" id="session" />
|
||||||
<argument>%form.csrf_protection.secret%</argument>
|
<argument>%form.csrf_protection.secret%</argument>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<!-- FormContext -->
|
<!-- TemporaryStorage - where should we put this? -->
|
||||||
<service id="form.context" class="%form.context.class%">
|
<service id="file.temporary_storage" class="%file.temporary_storage.class%">
|
||||||
<argument type="collection">
|
<argument type="service" id="session" />
|
||||||
<argument key="validator" type="service" id="validator" />
|
<argument>%file.temporary_storage.secret%</argument>
|
||||||
<argument key="validation_groups">%form.validation_groups%</argument>
|
<argument>%file.temporary_storage.nesting_levels%</argument>
|
||||||
<argument key="field_factory" type="service" id="form.field_factory" />
|
<argument>%file.temporary_storage.directory%</argument>
|
||||||
<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>
|
|
||||||
</service>
|
</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>
|
</services>
|
||||||
</container>
|
</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>
|
<ul>
|
||||||
<?php foreach ($field->getErrors() as $error): ?>
|
<?php foreach ($errors as $error): ?>
|
||||||
<li><?php echo $view['translator']->trans(
|
<li><?php echo $view['translator']->trans(
|
||||||
$error->getMessageTemplate(),
|
$error->getMessageTemplate(),
|
||||||
$error->getMessageParameters(),
|
$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>
|
<div>
|
||||||
<?php echo $view['form']->label($field) ?>
|
<?php echo $view['form']->label($form) ?>
|
||||||
<?php echo $view['form']->errors($field) ?>
|
<?php echo $view['form']->errors($form) ?>
|
||||||
<?php echo $view['form']->render($field) ?>
|
<?php echo $view['form']->widget($form) ?>
|
||||||
</div>
|
</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;
|
namespace Symfony\Bundle\FrameworkBundle\Templating\Helper;
|
||||||
|
|
||||||
use Symfony\Component\Templating\Helper\Helper;
|
use Symfony\Component\Templating\Helper\Helper;
|
||||||
use Symfony\Component\Form\FieldInterface;
|
use Symfony\Component\Templating\EngineInterface;
|
||||||
use Symfony\Component\Form\FormInterface;
|
use Symfony\Component\Form\FormView;
|
||||||
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
|
use Symfony\Component\Form\Exception\FormException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form is a factory that wraps Form instances.
|
*
|
||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||||
@ -28,82 +28,45 @@ class FormHelper extends Helper
|
|||||||
|
|
||||||
protected $engine;
|
protected $engine;
|
||||||
|
|
||||||
|
protected $varStack = array();
|
||||||
|
|
||||||
public function __construct(EngineInterface $engine)
|
public function __construct(EngineInterface $engine)
|
||||||
{
|
{
|
||||||
$this->engine = $engine;
|
$this->engine = $engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getName()
|
public function attributes()
|
||||||
{
|
{
|
||||||
return 'form';
|
$html = '';
|
||||||
}
|
$attr = array();
|
||||||
|
|
||||||
public function attributes($attributes)
|
if (count($this->varStack) > 0) {
|
||||||
{
|
$vars = end($this->varStack);
|
||||||
if ($attributes instanceof \Traversable) {
|
|
||||||
$attributes = iterator_to_array($attributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
if (isset($vars['id'])) {
|
||||||
{
|
$attr['id'] = $vars['id'];
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return trim($this->engine->render($template, array(
|
foreach ($attr as $k => $v) {
|
||||||
'field' => $field,
|
$html .= ' '.$this->engine->escape($k).'="'.$this->engine->escape($v).'"';
|
||||||
'attr' => $attributes,
|
}
|
||||||
'params' => $parameters,
|
|
||||||
)));
|
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
|
* @param FieldInterface $field
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function row(/*FieldInterface*/ $field, $template = null)
|
public function row(FormView $view, array $variables = array())
|
||||||
{
|
{
|
||||||
if (null === $template) {
|
return $this->renderSection($view, 'row', $variables);
|
||||||
$template = 'FrameworkBundle:Form:field_row.html.php';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->engine->render($template, array(
|
|
||||||
'field' => $field,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function label(/*FieldInterface */$field, $label = false, array $parameters = array(), $template = null)
|
public function label(FormView $view, $label = null)
|
||||||
{
|
{
|
||||||
if (null === $template) {
|
return $this->renderSection($view, 'label', null === $label ? array() : array('label' => $label));
|
||||||
$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())))
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function errors(/*FieldInterface */$field, array $parameters = array(), $template = null)
|
public function errors(FormView $view)
|
||||||
{
|
{
|
||||||
if (null === $template) {
|
return $this->renderSection($view, 'errors');
|
||||||
$template = 'FrameworkBundle:Form:errors.html.php';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->engine->render($template, array(
|
|
||||||
'field' => $field,
|
|
||||||
'params' => $parameters,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hidden(/*FormInterface */$form, array $parameters = array(), $template = null)
|
public function rest(FormView $view, array $variables = array())
|
||||||
{
|
{
|
||||||
if (null === $template) {
|
return $this->renderSection($view, 'rest', $variables);
|
||||||
$template = 'FrameworkBundle:Form:hidden.html.php';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->engine->render($template, array(
|
|
||||||
'field' => $form,
|
|
||||||
'params' => $parameters,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function lookupTemplate(/*FieldInterface */$field)
|
protected function renderSection(FormView $view, $section, array $variables = array())
|
||||||
{
|
{
|
||||||
$fqClassName = get_class($field);
|
|
||||||
$template = null;
|
$template = null;
|
||||||
|
$blocks = $view->get('types');
|
||||||
|
|
||||||
if (isset(self::$cache[$fqClassName])) {
|
foreach ($blocks as &$block) {
|
||||||
return self::$cache[$fqClassName];
|
$block = $block.'_'.$section;
|
||||||
}
|
$template = $this->lookupTemplate($block);
|
||||||
|
|
||||||
// find a template for the given class or one of its parents
|
if ($template) {
|
||||||
$currentFqClassName = $fqClassName;
|
break;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$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;
|
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()
|
->children()
|
||||||
->arrayNode('resources')
|
->arrayNode('resources')
|
||||||
->addDefaultsIfNotSet()
|
->addDefaultsIfNotSet()
|
||||||
->defaultValue(array('TwigBundle::form.html.twig'))
|
->defaultValue(array('TwigBundle:Form:div_layout.html.twig'))
|
||||||
->validate()
|
->validate()
|
||||||
->always()
|
->always()
|
||||||
->then(function($v){
|
->then(function($v){
|
||||||
return array_merge(array('TwigBundle::form.html.twig'), $v);
|
return array_merge(array('TwigBundle:Form:div_layout.html.twig'), $v);
|
||||||
})
|
})
|
||||||
->end()
|
->end()
|
||||||
->prototype('scalar')->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" />
|
<tag name="twig.extension" />
|
||||||
</service>
|
</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" />
|
<tag name="twig.extension" />
|
||||||
<argument>%twig.form.resources%</argument>
|
<argument>%twig.form.resources%</argument>
|
||||||
</service>
|
</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->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->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
|
// Twig options
|
||||||
$options = $container->getParameter('twig.options');
|
$options = $container->getParameter('twig.options');
|
||||||
@ -65,7 +65,7 @@ class TwigExtensionTest extends TestCase
|
|||||||
|
|
||||||
// Form resources
|
// Form resources
|
||||||
$resources = $container->getParameter('twig.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');
|
$this->assertContains('MyBundle::form.html.twig', $resources, '->load() merges new templates into form resources');
|
||||||
|
|
||||||
// Globals
|
// 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.
|
* 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>
|
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||||
*/
|
*/
|
||||||
class TimezoneField extends ChoiceField
|
class TimezoneChoiceList extends ArrayChoiceList
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Stores the available timezone choices
|
* Stores the available timezone choices
|
||||||
@ -24,34 +24,13 @@ class TimezoneField extends ChoiceField
|
|||||||
*/
|
*/
|
||||||
protected static $timezones = array();
|
protected static $timezones = array();
|
||||||
|
|
||||||
/**
|
public function __construct()
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function configure()
|
|
||||||
{
|
{
|
||||||
$this->addOption('choices', self::getTimezoneChoices());
|
parent::__construct(array());
|
||||||
|
|
||||||
parent::configure();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preselects the server timezone if the field is empty and required
|
* Loads the timezone choices
|
||||||
*
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getDisplayedData()
|
|
||||||
{
|
|
||||||
$data = parent::getDisplayedData();
|
|
||||||
|
|
||||||
if (null == $data && $this->isRequired()) {
|
|
||||||
$data = date_default_timezone_get();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the timezone choices
|
|
||||||
*
|
*
|
||||||
* The choices are generated from the ICU function
|
* The choices are generated from the ICU function
|
||||||
* \DateTimeZone::listIdentifiers(). They are cached during a single request,
|
* \DateTimeZone::listIdentifiers(). They are cached during a single request,
|
||||||
@ -60,8 +39,10 @@ class TimezoneField extends ChoiceField
|
|||||||
*
|
*
|
||||||
* @return array The timezone choices
|
* @return array The timezone choices
|
||||||
*/
|
*/
|
||||||
protected static function getTimezoneChoices()
|
protected function load()
|
||||||
{
|
{
|
||||||
|
parent::load();
|
||||||
|
|
||||||
if (count(self::$timezones) == 0) {
|
if (count(self::$timezones) == 0) {
|
||||||
foreach (\DateTimeZone::listIdentifiers() as $timezone) {
|
foreach (\DateTimeZone::listIdentifiers() as $timezone) {
|
||||||
$parts = explode('/', $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
|
* 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
|
* 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
|
* that is, that use the same class name to validate the CSRF token with
|
||||||
* isCsrfTokenValid().
|
* 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.
|
* 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;
|
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,7 +19,7 @@ use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
|||||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||||
* @author Florian Eckerstorfer <florian@eckerstorfer.org>
|
* @author Florian Eckerstorfer <florian@eckerstorfer.org>
|
||||||
*/
|
*/
|
||||||
class BooleanToStringTransformer extends Configurable implements ValueTransformerInterface
|
class BooleanToStringTransformer implements DataTransformerInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Transforms a Boolean into a string.
|
* Transforms a Boolean into a string.
|
||||||
@ -49,6 +48,10 @@ class BooleanToStringTransformer extends Configurable implements ValueTransforme
|
|||||||
*/
|
*/
|
||||||
public function reverseTransform($value)
|
public function reverseTransform($value)
|
||||||
{
|
{
|
||||||
|
if (null === $value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!is_string($value)) {
|
if (!is_string($value)) {
|
||||||
throw new UnexpectedTypeException($value, 'string');
|
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.
|
* 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
|
* Passes a value through multiple value transformers
|
||||||
*
|
*
|
||||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||||
*/
|
*/
|
||||||
class ValueTransformerChain implements ValueTransformerInterface
|
class DataTransformerChain implements DataTransformerInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The value transformers
|
* The value transformers
|
@ -9,14 +9,14 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Component\Form\ValueTransformer;
|
namespace Symfony\Component\Form\DataTransformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms a value between different representations.
|
* Transforms a value between different representations.
|
||||||
*
|
*
|
||||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||||
*/
|
*/
|
||||||
interface ValueTransformerInterface
|
interface DataTransformerInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Transforms a value from the original representation to a transformed representation.
|
* 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:
|
* 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).
|
* 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
|
* 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 submit '2009-10-10' onto
|
* 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
|
* 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).
|
* "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
|
* @param mixed $value The value in the original representation
|
||||||
* @return mixed The value in the transformed representation
|
* @return mixed The value in the transformed representation
|
||||||
* @throws UnexpectedTypeException when the argument is no string
|
* @throws UnexpectedTypeException when the argument is no string
|
||||||
* @throws ValueTransformerException when the transformation fails
|
* @throws DataTransformerException when the transformation fails
|
||||||
*/
|
*/
|
||||||
function transform($value);
|
function transform($value);
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ interface ValueTransformerInterface
|
|||||||
* Transforms a value from the transformed representation to its original
|
* Transforms a value from the transformed representation to its original
|
||||||
* representation.
|
* 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.
|
* into an acceptable format for your data processing/model layer.
|
||||||
*
|
*
|
||||||
* This method must be able to deal with empty values. Usually this will
|
* 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
|
* @param mixed $value The value in the transformed representation
|
||||||
* @throws UnexpectedTypeException when the argument is not of the
|
* @throws UnexpectedTypeException when the argument is not of the
|
||||||
* expected type
|
* expected type
|
||||||
* @throws ValueTransformerException when the transformation fails
|
* @throws DataTransformerException when the transformation fails
|
||||||
*/
|
*/
|
||||||
function reverseTransform($value);
|
function reverseTransform($value);
|
||||||
}
|
}
|
@ -9,7 +9,7 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Component\Form\ValueTransformer;
|
namespace Symfony\Component\Form\DataTransformer;
|
||||||
|
|
||||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||||
|
|
||||||
@ -27,17 +27,20 @@ use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
|||||||
*/
|
*/
|
||||||
class DateTimeToArrayTransformer extends BaseDateTimeTransformer
|
class DateTimeToArrayTransformer extends BaseDateTimeTransformer
|
||||||
{
|
{
|
||||||
/**
|
private $pad;
|
||||||
* {@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'));
|
|
||||||
|
|
||||||
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)
|
public function transform($dateTime)
|
||||||
{
|
{
|
||||||
if (null === $dateTime) {
|
if (null === $dateTime) {
|
||||||
return array(
|
return array_intersect_key(array(
|
||||||
'year' => '',
|
'year' => '',
|
||||||
'month' => '',
|
'month' => '',
|
||||||
'day' => '',
|
'day' => '',
|
||||||
'hour' => '',
|
'hour' => '',
|
||||||
'minute' => '',
|
'minute' => '',
|
||||||
'second' => '',
|
'second' => '',
|
||||||
);
|
), array_flip($this->fields));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$dateTime instanceof \DateTime) {
|
if (!$dateTime instanceof \DateTime) {
|
||||||
throw new UnexpectedTypeException($dateTime, '\DateTime');
|
throw new UnexpectedTypeException($dateTime, '\DateTime');
|
||||||
}
|
}
|
||||||
|
|
||||||
$inputTimezone = $this->getOption('input_timezone');
|
$inputTimezone = $this->inputTimezone;
|
||||||
$outputTimezone = $this->getOption('output_timezone');
|
$outputTimezone = $this->outputTimezone;
|
||||||
|
|
||||||
if ($inputTimezone != $outputTimezone) {
|
if ($inputTimezone != $outputTimezone) {
|
||||||
$dateTime->setTimezone(new \DateTimeZone($outputTimezone));
|
$dateTime->setTimezone(new \DateTimeZone($outputTimezone));
|
||||||
@ -77,9 +80,9 @@ class DateTimeToArrayTransformer extends BaseDateTimeTransformer
|
|||||||
'hour' => $dateTime->format('H'),
|
'hour' => $dateTime->format('H'),
|
||||||
'minute' => $dateTime->format('i'),
|
'minute' => $dateTime->format('i'),
|
||||||
'second' => $dateTime->format('s'),
|
'second' => $dateTime->format('s'),
|
||||||
), array_flip($this->getOption('fields')));
|
), array_flip($this->fields));
|
||||||
|
|
||||||
if (!$this->getOption('pad')) {
|
if (!$this->pad) {
|
||||||
foreach ($result as &$entry) {
|
foreach ($result as &$entry) {
|
||||||
// remove leading zeros
|
// remove leading zeros
|
||||||
$entry = (string)(int)$entry;
|
$entry = (string)(int)$entry;
|
||||||
@ -101,8 +104,8 @@ class DateTimeToArrayTransformer extends BaseDateTimeTransformer
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$inputTimezone = $this->getOption('input_timezone');
|
$inputTimezone = $this->inputTimezone;
|
||||||
$outputTimezone = $this->getOption('output_timezone');
|
$outputTimezone = $this->outputTimezone;
|
||||||
|
|
||||||
if (!is_array($value)) {
|
if (!is_array($value)) {
|
||||||
throw new UnexpectedTypeException($value, 'array');
|
throw new UnexpectedTypeException($value, 'array');
|
||||||
@ -112,6 +115,19 @@ class DateTimeToArrayTransformer extends BaseDateTimeTransformer
|
|||||||
return null;
|
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 {
|
try {
|
||||||
$dateTime = new \DateTime(sprintf(
|
$dateTime = new \DateTime(sprintf(
|
||||||
'%s-%s-%s %s:%s:%s %s',
|
'%s-%s-%s %s:%s:%s %s',
|
@ -9,7 +9,7 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Component\Form\ValueTransformer;
|
namespace Symfony\Component\Form\DataTransformer;
|
||||||
|
|
||||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||||
|
|
||||||
@ -27,25 +27,32 @@ use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
|||||||
*/
|
*/
|
||||||
class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
|
class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
|
||||||
{
|
{
|
||||||
/**
|
private $dateFormat;
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
private $timeFormat;
|
||||||
protected function configure()
|
|
||||||
|
public function __construct($inputTimezone = null, $outputTimezone = null, $dateFormat = null, $timeFormat = null)
|
||||||
{
|
{
|
||||||
$this->addOption('date_format', self::MEDIUM);
|
parent::__construct($inputTimezone, $outputTimezone);
|
||||||
$this->addOption('time_format', self::SHORT);
|
|
||||||
$this->addOption('input_timezone', 'UTC');
|
|
||||||
$this->addOption('output_timezone', 'UTC');
|
|
||||||
|
|
||||||
if (!in_array($this->getOption('date_format'), self::$formats, true)) {
|
if (is_null($dateFormat)) {
|
||||||
throw new \InvalidArgumentException(sprintf('The option "date_format" is expected to be one of "%s". Is "%s"', implode('", "', self::$formats), $this->getOption('time_format')));
|
$dateFormat = \IntlDateFormatter::MEDIUM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!in_array($this->getOption('time_format'), self::$formats, true)) {
|
if (is_null($timeFormat)) {
|
||||||
throw new \InvalidArgumentException(sprintf('The option "time_format" is expected to be one of "%s". Is "%s"', implode('", "', self::$formats), $this->getOption('time_format')));
|
$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');
|
throw new UnexpectedTypeException($dateTime, '\DateTime');
|
||||||
}
|
}
|
||||||
|
|
||||||
$inputTimezone = $this->getOption('input_timezone');
|
$inputTimezone = $this->inputTimezone;
|
||||||
|
|
||||||
// convert time to UTC before passing it to the formatter
|
// convert time to UTC before passing it to the formatter
|
||||||
if ('UTC' != $inputTimezone) {
|
if ('UTC' != $inputTimezone) {
|
||||||
@ -88,7 +95,7 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
|
|||||||
*/
|
*/
|
||||||
public function reverseTransform($value)
|
public function reverseTransform($value)
|
||||||
{
|
{
|
||||||
$inputTimezone = $this->getOption('input_timezone');
|
$inputTimezone = $this->inputTimezone;
|
||||||
|
|
||||||
if (!is_string($value)) {
|
if (!is_string($value)) {
|
||||||
throw new UnexpectedTypeException($value, 'string');
|
throw new UnexpectedTypeException($value, 'string');
|
||||||
@ -121,9 +128,9 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
|
|||||||
*/
|
*/
|
||||||
protected function getIntlDateFormatter()
|
protected function getIntlDateFormatter()
|
||||||
{
|
{
|
||||||
$dateFormat = $this->getIntlFormatConstant($this->getOption('date_format'));
|
$dateFormat = $this->dateFormat;
|
||||||
$timeFormat = $this->getIntlFormatConstant($this->getOption('time_format'));
|
$timeFormat = $this->timeFormat;
|
||||||
$timezone = $this->getOption('output_timezone');
|
$timezone = $this->outputTimezone;
|
||||||
|
|
||||||
return new \IntlDateFormatter(\Locale::getDefault(), $dateFormat, $timeFormat, $timezone);
|
return new \IntlDateFormatter(\Locale::getDefault(), $dateFormat, $timeFormat, $timezone);
|
||||||
}
|
}
|
@ -9,9 +9,8 @@
|
|||||||
* file that was distributed with this source code.
|
* 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;
|
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,18 +19,15 @@ use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
|||||||
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
||||||
* @author Florian Eckerstorfer <florian@eckerstorfer.org>
|
* @author Florian Eckerstorfer <florian@eckerstorfer.org>
|
||||||
*/
|
*/
|
||||||
class DateTimeToStringTransformer extends Configurable implements ValueTransformerInterface
|
class DateTimeToStringTransformer extends BaseDateTimeTransformer
|
||||||
{
|
{
|
||||||
/**
|
private $format;
|
||||||
* {@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');
|
|
||||||
|
|
||||||
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');
|
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');
|
throw new UnexpectedTypeException($value, 'string');
|
||||||
}
|
}
|
||||||
|
|
||||||
$outputTimezone = $this->getOption('output_timezone');
|
$outputTimezone = $this->outputTimezone;
|
||||||
$inputTimezone = $this->getOption('input_timezone');
|
$inputTimezone = $this->inputTimezone;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$dateTime = new \DateTime("$value $outputTimezone");
|
$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