2011-02-19 12:52:37 +00:00
< ? php
/*
* This file is part of the Symfony package .
*
2011-04-24 12:59:46 +01:00
* ( c ) Fabien Potencier < fabien @ symfony . com >
2011-02-19 12:52:37 +00:00
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace Symfony\Component\Form ;
2011-03-25 14:30:33 +00:00
use Symfony\Component\Form\Exception\FormException ;
2011-04-24 20:56:06 +01:00
use Symfony\Component\Form\Exception\UnexpectedTypeException ;
2011-05-13 17:29:18 +01:00
use Symfony\Component\Form\Exception\TypeDefinitionException ;
2012-05-10 15:37:03 +01:00
use Symfony\Component\OptionsResolver\OptionsResolver ;
2011-02-19 12:52:37 +00:00
2011-03-02 11:20:30 +00:00
class FormFactory implements FormFactoryInterface
2011-02-19 12:52:37 +00:00
{
2011-05-13 17:29:18 +01:00
private static $requiredOptions = array (
'data' ,
'required' ,
'max_length' ,
);
2011-05-10 17:23:58 +01:00
/**
* Extensions
* @ var array An array of FormExtensionInterface
*/
2011-04-22 16:41:21 +01:00
private $extensions = array ();
2011-03-02 13:58:19 +00:00
2011-05-10 17:23:58 +01:00
/**
* All known types ( cache )
* @ var array An array of FormTypeInterface
*/
2011-04-22 18:22:26 +01:00
private $types = array ();
2011-05-10 17:23:58 +01:00
/**
* The guesser chain
* @ var FormTypeGuesserChain
*/
2011-04-22 16:41:21 +01:00
private $guesser ;
2011-03-02 13:58:19 +00:00
2011-05-10 14:32:14 +01:00
/**
* Constructor .
*
* @ param array $extensions An array of FormExtensionInterface
*
* @ throws UnexpectedTypeException if any extension does not implement FormExtensionInterface
*/
2011-04-24 20:56:06 +01:00
public function __construct ( array $extensions )
2011-02-19 12:52:37 +00:00
{
2011-04-22 16:41:21 +01:00
foreach ( $extensions as $extension ) {
2011-04-24 20:56:06 +01:00
if ( ! $extension instanceof FormExtensionInterface ) {
throw new UnexpectedTypeException ( $extension , 'Symfony\Component\Form\FormExtensionInterface' );
}
2011-03-25 01:26:14 +00:00
}
2011-04-22 16:41:21 +01:00
2011-04-24 20:56:06 +01:00
$this -> extensions = $extensions ;
2011-04-22 16:41:21 +01:00
}
2011-05-15 18:24:09 +01:00
/**
2012-05-21 03:26:59 +01:00
* { @ inheritdoc }
2011-05-15 18:24:09 +01:00
*/
2011-05-13 17:29:18 +01:00
public function hasType ( $name )
2011-04-22 18:22:26 +01:00
{
2011-05-13 17:29:18 +01:00
if ( isset ( $this -> types [ $name ])) {
return true ;
}
2011-04-22 18:22:26 +01:00
2011-05-13 17:29:18 +01:00
try {
$this -> loadType ( $name );
} catch ( FormException $e ) {
return false ;
2011-04-22 18:22:26 +01:00
}
2011-05-13 17:29:18 +01:00
return true ;
}
2011-04-22 18:22:26 +01:00
2011-05-15 18:24:09 +01:00
/**
2012-05-21 03:26:59 +01:00
* { @ inheritdoc }
2011-05-15 18:24:09 +01:00
*/
2011-05-13 17:29:18 +01:00
public function addType ( FormTypeInterface $type )
{
$this -> loadTypeExtensions ( $type );
2011-04-22 18:22:26 +01:00
2011-07-07 08:16:13 +01:00
$this -> validateFormTypeName ( $type );
2011-05-13 17:29:18 +01:00
$this -> types [ $type -> getName ()] = $type ;
}
2011-04-22 18:22:26 +01:00
2011-05-10 17:23:58 +01:00
/**
2012-05-21 03:26:59 +01:00
* { @ inheritdoc }
2011-05-10 17:23:58 +01:00
*/
2011-04-22 18:22:26 +01:00
public function getType ( $name )
{
2011-05-13 17:29:18 +01:00
if ( ! is_string ( $name )) {
throw new UnexpectedTypeException ( $name , 'string' );
2011-04-22 18:22:26 +01:00
}
if ( ! isset ( $this -> types [ $name ])) {
2011-05-13 17:29:18 +01:00
$this -> loadType ( $name );
2011-04-22 18:22:26 +01:00
}
return $this -> types [ $name ];
}
2011-05-10 17:23:58 +01:00
/**
2012-05-21 03:26:59 +01:00
* { @ inheritdoc }
2011-05-10 17:23:58 +01:00
*/
2011-12-14 14:01:09 +00:00
public function create ( $type , $data = null , array $options = array (), FormBuilder $parent = null )
2011-02-19 14:26:07 +00:00
{
2011-12-14 14:01:09 +00:00
return $this -> createBuilder ( $type , $data , $options , $parent ) -> getForm ();
2011-04-22 09:42:21 +01:00
}
2011-05-10 17:23:58 +01:00
/**
2012-05-21 03:26:59 +01:00
* { @ inheritdoc }
2011-05-10 17:23:58 +01:00
*/
2011-12-14 14:01:09 +00:00
public function createNamed ( $type , $name , $data = null , array $options = array (), FormBuilder $parent = null )
2011-04-22 09:42:21 +01:00
{
2011-12-14 14:01:09 +00:00
return $this -> createNamedBuilder ( $type , $name , $data , $options , $parent ) -> getForm ();
2011-04-22 09:42:21 +01:00
}
/**
2012-05-21 03:26:59 +01:00
* { @ inheritdoc }
2011-04-22 09:42:21 +01:00
*/
2011-12-14 14:01:09 +00:00
public function createForProperty ( $class , $property , $data = null , array $options = array (), FormBuilder $parent = null )
2011-04-22 09:42:21 +01:00
{
2011-12-14 14:01:09 +00:00
return $this -> createBuilderForProperty ( $class , $property , $data , $options , $parent ) -> getForm ();
2011-04-22 09:42:21 +01:00
}
2011-05-10 17:23:58 +01:00
/**
2012-05-21 03:26:59 +01:00
* { @ inheritdoc }
2011-05-10 17:23:58 +01:00
*/
2011-12-14 14:01:09 +00:00
public function createBuilder ( $type , $data = null , array $options = array (), FormBuilder $parent = null )
2011-04-22 09:42:21 +01:00
{
$name = is_object ( $type ) ? $type -> getName () : $type ;
2011-12-14 14:01:09 +00:00
return $this -> createNamedBuilder ( $type , $name , $data , $options , $parent );
2011-04-22 09:42:21 +01:00
}
2011-03-04 15:01:50 +00:00
2011-05-10 17:23:58 +01:00
/**
2012-05-21 03:26:59 +01:00
* { @ inheritdoc }
2011-05-10 17:23:58 +01:00
*/
2011-12-14 14:01:09 +00:00
public function createNamedBuilder ( $type , $name , $data = null , array $options = array (), FormBuilder $parent = null )
2011-04-22 09:42:21 +01:00
{
2011-04-24 12:32:29 +01:00
if ( ! array_key_exists ( 'data' , $options )) {
$options [ 'data' ] = $data ;
}
2012-02-07 09:51:21 +00:00
$builder = null ;
$types = array ();
2012-04-05 09:16:23 +01:00
$knownOptions = array ();
2012-05-10 15:37:03 +01:00
$optionsResolver = new OptionsResolver ();
2012-02-07 09:51:21 +00:00
// Bottom-up determination of the type hierarchy
// Start with the actual type and look for the parent type
// The complete hierarchy is saved in $types, the first entry being
// the root and the last entry being the leaf (the concrete type)
2011-03-18 15:20:00 +00:00
while ( null !== $type ) {
2011-05-13 17:29:18 +01:00
if ( $type instanceof FormTypeInterface ) {
2011-11-18 13:23:22 +00:00
if ( $type -> getName () == $type -> getParent ( $options )) {
2011-11-22 09:24:03 +00:00
throw new FormException ( sprintf ( 'The form type name "%s" for class "%s" cannot be the same as the parent type.' , $type -> getName (), get_class ( $type )));
2011-11-18 13:23:22 +00:00
}
2011-05-13 17:29:18 +01:00
$this -> addType ( $type );
2011-12-13 10:53:49 +00:00
} elseif ( is_string ( $type )) {
2011-05-13 17:29:18 +01:00
$type = $this -> getType ( $type );
2011-12-13 10:53:49 +00:00
} else {
throw new UnexpectedTypeException ( $type , 'string or Symfony\Component\Form\FormTypeInterface' );
2011-05-13 17:29:18 +01:00
}
2011-04-22 16:41:21 +01:00
2012-02-07 09:51:21 +00:00
array_unshift ( $types , $type );
// getParent() cannot see default options set by this type nor
// default options set by parent types
// As a result, the options always have to be checked for
// existence with isset() before using them in this method.
$type = $type -> getParent ( $options );
}
2012-04-05 09:16:23 +01:00
// Top-down determination of the default options
2012-02-07 09:51:21 +00:00
foreach ( $types as $type ) {
// Merge the default options of all types to an array of default
// options. Default options of children override default options
// of parents.
2012-05-23 16:29:07 +01:00
/* @var FormTypeInterface $type */
$type -> setDefaultOptions ( $optionsResolver );
2011-04-22 18:22:26 +01:00
foreach ( $type -> getExtensions () as $typeExtension ) {
2012-05-23 16:29:07 +01:00
/* @var FormTypeExtensionInterface $typeExtension */
$typeExtension -> setDefaultOptions ( $optionsResolver );
2011-03-26 17:48:55 +00:00
}
2011-02-21 22:40:12 +00:00
}
2011-02-19 12:52:37 +00:00
2012-04-05 09:16:23 +01:00
// Resolve concrete type
2011-05-13 17:29:18 +01:00
$type = end ( $types );
2012-04-05 09:16:23 +01:00
// Validate options required by the factory
2012-05-23 16:29:07 +01:00
$diff = array ();
foreach ( self :: $requiredOptions as $requiredOption ) {
if ( ! $optionsResolver -> isKnown ( $requiredOption )) {
$diff [] = $requiredOption ;
}
}
2011-03-25 14:30:33 +00:00
2011-05-13 17:29:18 +01:00
if ( count ( $diff ) > 0 ) {
throw new TypeDefinitionException ( sprintf ( 'Type "%s" should support the option(s) "%s"' , $type -> getName (), implode ( '", "' , $diff )));
}
2012-04-05 09:16:23 +01:00
// Resolve options
2012-05-10 15:37:03 +01:00
$options = $optionsResolver -> resolve ( $options );
2011-03-25 14:30:33 +00:00
for ( $i = 0 , $l = count ( $types ); $i < $l && ! $builder ; ++ $i ) {
2011-04-14 14:25:30 +01:00
$builder = $types [ $i ] -> createBuilder ( $name , $this , $options );
2011-03-25 14:30:33 +00:00
}
2011-05-13 17:29:18 +01:00
if ( ! $builder ) {
throw new TypeDefinitionException ( sprintf ( 'Type "%s" or any of its parents should return a FormBuilder instance from createBuilder()' , $type -> getName ()));
}
2011-02-19 16:30:19 +00:00
2011-03-24 15:16:31 +00:00
$builder -> setTypes ( $types );
2011-06-09 16:10:34 +01:00
$builder -> setCurrentLoadingType ( $type -> getName ());
2011-12-14 14:01:09 +00:00
$builder -> setParent ( $parent );
2011-03-18 11:50:26 +00:00
2011-03-24 15:16:31 +00:00
foreach ( $types as $type ) {
2011-03-26 17:52:24 +00:00
$type -> buildForm ( $builder , $options );
2011-04-22 18:22:26 +01:00
foreach ( $type -> getExtensions () as $typeExtension ) {
$typeExtension -> buildForm ( $builder , $options );
}
2011-02-19 14:26:07 +00:00
}
2011-06-09 16:10:34 +01:00
$builder -> setCurrentLoadingType ( null );
2011-02-19 14:26:07 +00:00
2011-04-22 09:42:21 +01:00
return $builder ;
2011-03-18 11:50:26 +00:00
}
2011-05-10 17:23:58 +01:00
/**
2012-05-21 03:26:59 +01:00
* { @ inheritdoc }
2011-05-10 17:23:58 +01:00
*/
2011-12-14 14:01:09 +00:00
public function createBuilderForProperty ( $class , $property , $data = null , array $options = array (), FormBuilder $parent = null )
2011-03-02 13:58:19 +00:00
{
2011-04-22 16:41:21 +01:00
if ( ! $this -> guesser ) {
$this -> loadGuesser ();
}
$typeGuess = $this -> guesser -> guessType ( $class , $property );
$maxLengthGuess = $this -> guesser -> guessMaxLength ( $class , $property );
2012-04-23 14:55:54 +01:00
// Keep $minLengthGuess for BC until Symfony 2.3
$minLengthGuess = $this -> guesser -> guessMinLength ( $class , $property );
2011-04-22 16:41:21 +01:00
$requiredGuess = $this -> guesser -> guessRequired ( $class , $property );
2012-04-13 16:07:09 +01:00
$patternGuess = $this -> guesser -> guessPattern ( $class , $property );
2011-04-22 16:41:21 +01:00
2011-03-18 15:20:00 +00:00
$type = $typeGuess ? $typeGuess -> getType () : 'text' ;
2011-03-02 13:58:19 +00:00
2012-03-19 22:57:21 +00:00
$maxLength = $maxLengthGuess ? $maxLengthGuess -> getValue () : null ;
2012-04-23 14:55:54 +01:00
$minLength = $minLengthGuess ? $minLengthGuess -> getValue () : null ;
2012-04-13 16:07:09 +01:00
$pattern = $patternGuess ? $patternGuess -> getValue () : null ;
2012-03-19 22:57:21 +00:00
2012-04-23 14:55:54 +01:00
// overrides $minLength, if set
if ( null !== $pattern ) {
$options = array_merge ( array ( 'pattern' => $pattern ), $options );
}
2012-03-19 22:57:21 +00:00
if ( null !== $maxLength ) {
$options = array_merge ( array ( 'max_length' => $maxLength ), $options );
2011-03-02 13:58:19 +00:00
}
2012-04-23 14:55:54 +01:00
if ( null !== $minLength && $minLength > 0 ) {
$options = array_merge ( array ( 'pattern' => '.{' . $minLength . ',' . $maxLength . '}' ), $options );
2011-03-02 13:58:19 +00:00
}
2012-04-23 14:55:54 +01:00
if ( $requiredGuess ) {
$options = array_merge ( array ( 'required' => $requiredGuess -> getValue ()), $options );
2012-04-13 16:07:09 +01:00
}
2011-03-02 13:58:19 +00:00
// user options may override guessed options
2011-03-18 15:20:00 +00:00
if ( $typeGuess ) {
$options = array_merge ( $typeGuess -> getOptions (), $options );
2011-03-18 13:37:54 +00:00
}
2011-03-02 13:58:19 +00:00
2011-12-14 14:01:09 +00:00
return $this -> createNamedBuilder ( $type , $property , $data , $options , $parent );
2011-03-02 13:58:19 +00:00
}
2011-05-06 11:48:43 +01:00
2011-05-10 17:23:58 +01:00
/**
* Initializes the guesser chain .
*/
2011-05-06 11:48:43 +01:00
private function loadGuesser ()
{
$guessers = array ();
foreach ( $this -> extensions as $extension ) {
$guesser = $extension -> getTypeGuesser ();
if ( $guesser ) {
$guessers [] = $guesser ;
}
}
$this -> guesser = new FormTypeGuesserChain ( $guessers );
}
2011-05-13 17:29:18 +01:00
2011-05-15 18:24:09 +01:00
/**
* Loads a type .
*
* @ param string $name The type name
*
* @ throws FormException if the type is not provided by any registered extension
*/
2011-05-13 17:29:18 +01:00
private function loadType ( $name )
{
$type = null ;
foreach ( $this -> extensions as $extension ) {
if ( $extension -> hasType ( $name )) {
$type = $extension -> getType ( $name );
break ;
}
}
if ( ! $type ) {
throw new FormException ( sprintf ( 'Could not load type "%s"' , $name ));
}
$this -> loadTypeExtensions ( $type );
2011-07-07 08:16:13 +01:00
$this -> validateFormTypeName ( $type );
2011-05-13 17:29:18 +01:00
$this -> types [ $name ] = $type ;
}
2011-05-15 18:24:09 +01:00
/**
* Loads the extensions for a given type .
*
* @ param FormTypeInterface $type The type
*/
2011-05-13 17:29:18 +01:00
private function loadTypeExtensions ( FormTypeInterface $type )
{
$typeExtensions = array ();
foreach ( $this -> extensions as $extension ) {
$typeExtensions = array_merge (
$typeExtensions ,
$extension -> getTypeExtensions ( $type -> getName ())
);
}
$type -> setExtensions ( $typeExtensions );
}
2011-07-07 08:16:13 +01:00
private function validateFormTypeName ( FormTypeInterface $type )
{
2011-12-21 14:50:59 +00:00
if ( ! preg_match ( '/^[a-z0-9_]*$/i' , $type -> getName ())) {
2011-07-07 08:16:13 +01:00
throw new FormException ( sprintf ( 'The "%s" form type name ("%s") is not valid. Names must only contain letters, numbers, and "_".' , get_class ( $type ), $type -> getName ()));
}
}
2011-03-24 21:20:54 +00:00
}