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 ;
use Symfony\Component\Form\Exception\CreationException ;
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
/**
* Returns whether the given type is supported .
*
* @ param string $name The name of the type
*
* @ return Boolean Whether the type is supported
*/
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
/**
* Add a type .
*
* @ param FormTypeInterface $type The type
*/
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-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
/**
* Returns a type by name .
*
* This methods registers the type extensions from the form extensions .
*
* @ param string | FormTypeInterface $name The name of the type or a type instance
*
* @ return FormTypeInterface The type
*
* @ throws FormException if the type can not be retrieved from any extension
*/
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
/**
* Returns a form .
*
* @ see createBuilder ()
*
* @ param string | FormTypeInterface $type The type of the form
* @ param mixed $data The initial data
* @ param array $options The options
*
* @ return Form The form named after the type
*
* @ throws FormException if any given option is not applicable to the given type
*/
2011-04-22 09:42:21 +01:00
public function create ( $type , $data = null , array $options = array ())
2011-02-19 14:26:07 +00:00
{
2011-04-22 09:42:21 +01:00
return $this -> createBuilder ( $type , $data , $options ) -> getForm ();
}
2011-05-10 17:23:58 +01:00
/**
* Returns a form .
*
* @ see createNamedBuilder ()
*
* @ param string | FormTypeInterface $type The type of the form
* @ param string $name The name of the form
* @ param mixed $data The initial data
* @ param array $options The options
*
* @ return Form The form
*
* @ throws FormException if any given option is not applicable to the given type
*/
2011-04-22 09:42:21 +01:00
public function createNamed ( $type , $name , $data = null , array $options = array ())
{
return $this -> createNamedBuilder ( $type , $name , $data , $options ) -> getForm ();
}
/**
2011-05-10 17:23:58 +01:00
* Returns a form for a property of a class .
*
* @ see createBuilderForProperty ()
*
* @ param string $class The fully qualified class name
* @ param string $property The name of the property to guess for
* @ param mixed $data The initial data
* @ param array $options The options for the builder
*
* @ return Form The form named after the property
*
* @ throws FormException if any given option is not applicable to the form type
2011-04-22 09:42:21 +01:00
*/
public function createForProperty ( $class , $property , $data = null , array $options = array ())
{
return $this -> createBuilderForProperty ( $class , $property , $data , $options ) -> getForm ();
}
2011-05-10 17:23:58 +01:00
/**
* Returns a form builder
*
* @ param string | FormTypeInterface $type The type of the form
* @ param mixed $data The initial data
* @ param array $options The options
*
* @ return FormBuilder The form builder
*
* @ throws FormException if any given option is not applicable to the given type
*/
2011-04-22 09:42:21 +01:00
public function createBuilder ( $type , $data = null , array $options = array ())
{
$name = is_object ( $type ) ? $type -> getName () : $type ;
return $this -> createNamedBuilder ( $type , $name , $data , $options );
}
2011-03-04 15:01:50 +00:00
2011-05-10 17:23:58 +01:00
/**
* Returns a form builder .
*
* @ param string | FormTypeInterface $type The type of the form
* @ param string $name The name of the form
* @ param mixed $data The initial data
* @ param array $options The options
*
* @ return FormBuilder The form builder
*
* @ throws FormException if any given option is not applicable to the given type
*/
2011-04-22 09:42:21 +01:00
public function createNamedBuilder ( $type , $name , $data = null , array $options = array ())
{
2011-03-18 11:50:26 +00:00
$builder = null ;
2011-03-24 15:16:31 +00:00
$types = array ();
2011-03-25 14:30:33 +00:00
$knownOptions = array ();
$passedOptions = array_keys ( $options );
2011-05-13 17:41:23 +01:00
$optionValues = array ();
2011-02-21 22:40:12 +00:00
2011-04-24 12:32:29 +01:00
if ( ! array_key_exists ( 'data' , $options )) {
$options [ 'data' ] = $data ;
}
2011-03-18 15:20:00 +00:00
while ( null !== $type ) {
2011-05-13 17:29:18 +01:00
if ( $type instanceof FormTypeInterface ) {
$this -> addType ( $type );
} else {
$type = $this -> getType ( $type );
}
2011-04-22 16:41:21 +01:00
2011-04-22 18:22:26 +01:00
$defaultOptions = $type -> getDefaultOptions ( $options );
2011-05-13 17:41:23 +01:00
$optionValues = array_merge_recursive ( $optionValues , $type -> getAllowedOptionValues ( $options ));
2011-04-22 18:22:26 +01:00
foreach ( $type -> getExtensions () as $typeExtension ) {
2011-05-13 17:41:23 +01:00
$defaultOptions = array_replace ( $defaultOptions , $typeExtension -> getDefaultOptions ( $options ));
$optionValues = array_merge_recursive ( $optionValues , $typeExtension -> getAllowedOptionValues ( $options ));
2011-03-26 17:48:55 +00:00
}
2011-05-13 17:41:23 +01:00
$options = array_replace ( $defaultOptions , $options );
2011-03-25 14:30:33 +00:00
$knownOptions = array_merge ( $knownOptions , array_keys ( $defaultOptions ));
2011-04-22 18:22:26 +01:00
array_unshift ( $types , $type );
2011-03-18 15:20:00 +00:00
$type = $type -> getParent ( $options );
2011-02-21 22:40:12 +00:00
}
2011-02-19 12:52:37 +00:00
2011-05-13 17:29:18 +01:00
$type = end ( $types );
$diff = array_diff ( self :: $requiredOptions , $knownOptions );
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 )));
}
2011-03-25 14:30:33 +00:00
$diff = array_diff ( $passedOptions , $knownOptions );
2011-05-13 17:29:18 +01:00
if ( count ( $diff ) > 1 ) {
throw new CreationException ( sprintf ( 'The options "%s" do not exist' , implode ( '", "' , $diff )));
}
2011-03-25 14:30:33 +00:00
if ( count ( $diff ) > 0 ) {
2011-05-23 07:06:43 +01:00
throw new CreationException ( sprintf ( 'The option "%s" does not exist' , current ( $diff )));
2011-03-25 14:30:33 +00:00
}
2011-05-13 17:41:23 +01:00
foreach ( $optionValues as $option => $allowedValues ) {
if ( ! in_array ( $options [ $option ], $allowedValues , true )) {
throw new CreationException ( sprintf ( 'The option "%s" has the value "%s", but is expected to be one of "%s"' , $option , $options [ $option ], implode ( '", "' , $allowedValues )));
}
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-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
/**
* Returns a form builder for a property of a class .
*
* If any of the 'max_length' , 'required' and type options can be guessed ,
* and are not provided in the options argument , the guessed value is used .
*
* @ param string $class The fully qualified class name
* @ param string $property The name of the property to guess for
* @ param mixed $data The initial data
* @ param array $options The options for the builder
*
* @ return FormBuilder The form builder named after the property
*
* @ throws FormException if any given option is not applicable to the form type
*/
2011-04-22 09:42:21 +01:00
public function createBuilderForProperty ( $class , $property , $data = null , array $options = array ())
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 );
2011-05-04 22:16:41 +01:00
$minLengthGuess = $this -> guesser -> guessMinLength ( $class , $property );
2011-04-22 16:41:21 +01:00
$requiredGuess = $this -> guesser -> guessRequired ( $class , $property );
2011-03-18 15:20:00 +00:00
$type = $typeGuess ? $typeGuess -> getType () : 'text' ;
2011-03-02 13:58:19 +00:00
2011-03-18 13:37:54 +00:00
if ( $maxLengthGuess ) {
$options = array_merge ( array ( 'max_length' => $maxLengthGuess -> getValue ()), $options );
2011-03-02 13:58:19 +00:00
}
2011-05-04 22:16:41 +01:00
if ( $minLengthGuess ) {
if ( $maxLengthGuess ) {
$options = array_merge ( array ( 'pattern' => '.{' . $minLengthGuess -> getValue () . ',' . $maxLengthGuess -> getValue () . '}' ), $options );
} else {
$options = array_merge ( array ( 'pattern' => '.{' . $minLengthGuess -> getValue () . ',}' ), $options );
}
}
2011-03-02 13:58:19 +00:00
if ( $requiredGuess ) {
$options = array_merge ( array ( 'required' => $requiredGuess -> getValue ()), $options );
}
// 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-04-22 09:42:21 +01:00
return $this -> createNamedBuilder ( $type , $property , $data , $options );
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 );
$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-03-24 21:20:54 +00:00
}