-
-
Save nclavaud/1386997 to your computer and use it in GitHub Desktop.
<?php | |
/** | |
* @author Nicolas Clavaud <[email protected]> | |
* http://n.clavaud.free.fr/blog/index.php?article31/symfony2-standalone-form-component-tutorial | |
*/ | |
namespace Me\MyApp; | |
use Symfony\Component\ClassLoader\UniversalClassLoader; | |
use Symfony\Component\Form\AbstractType; | |
use Symfony\Component\Form\FormBuilder; | |
use Symfony\Component\Form\FormFactory; | |
use Symfony\Component\Form\Extension\Core\CoreExtension; | |
use Symfony\Component\EventDispatcher\EventDispatcher; | |
use Symfony\Component\Templating\TemplateNameParser; | |
use Symfony\Component\Templating\TemplateNameParserInterface; | |
use Symfony\Component\Templating\TemplateReference; | |
use Symfony\Component\Templating\Loader\FilesystemLoader; | |
use Symfony\Component\Templating\PhpEngine; | |
use Symfony\Component\Translation\MessageSelector; | |
use Symfony\Component\Translation\Translator; | |
use Symfony\Component\Translation\TranslatorInterface; | |
use Symfony\Bundle\FrameworkBundle\Templating\Helper\FormHelper; | |
use Symfony\Bundle\FrameworkBundle\Templating\Helper\TranslatorHelper; | |
// validation | |
use Symfony\Component\Form\Extension\Validator\ValidatorExtension; | |
use Symfony\Component\Validator\Validator; | |
use Symfony\Component\Validator\Mapping\ClassMetadata; | |
use Symfony\Component\Validator\Mapping\ClassMetadataFactory; | |
use Symfony\Component\Validator\Mapping\Loader\StaticMethodLoader; | |
use Symfony\Component\Validator\ConstraintValidatorFactory; | |
use Symfony\Component\Validator\Constraints as Constraints; | |
/** | |
* Load and configure autoloader | |
* @see http://symfony.com/doc/2.0/cookbook/tools/autoloader.html | |
*/ | |
require_once 'Symfony/Component/ClassLoader/UniversalClassLoader.php'; | |
$loader = new UniversalClassLoader(); | |
$loader->register(); | |
$loader->registerNamespace('Symfony', __DIR__.'/'); | |
/** | |
* Parameters | |
*/ | |
$locale = null; | |
/** | |
* Entity | |
*/ | |
class Message | |
{ | |
public $sender; | |
public $recipient; | |
public $message; | |
// validation | |
public static function loadValidatorMetadata(ClassMetadata $metadata) | |
{ | |
$metadata->addPropertyConstraint('sender', new Constraints\NotBlank()); | |
$metadata->addPropertyConstraint('sender', new Constraints\Email()); | |
$metadata->addPropertyConstraint('recipient', new Constraints\NotBlank()); | |
$metadata->addPropertyConstraint('recipient', new Constraints\Email()); | |
$metadata->addPropertyConstraint('message', new Constraints\NotBlank()); | |
$metadata->addPropertyConstraint('message', new Constraints\MinLength(10)); | |
} | |
} | |
/** | |
* Form Class | |
* @see http://symfony.com/doc/2.0/book/forms.html#creating-form-classes | |
*/ | |
class MessageType extends AbstractType | |
{ | |
public function buildForm(FormBuilder $builder, array $options) | |
{ | |
$builder | |
->add('sender', 'email') | |
->add('recipient', 'email') | |
->add('message', 'textarea'); | |
} | |
public function getName() | |
{ | |
return 'message'; | |
} | |
public function getDefaultOptions(array $options) | |
{ | |
return array( | |
'data_class' => 'Me\MyApp\Message', | |
); | |
} | |
} | |
/** | |
* Template name parser | |
* @see Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTemplateNameParser | |
* | |
* Needed to load the templates used for rendering form items. | |
*/ | |
class StubTemplateNameParser implements TemplateNameParserInterface | |
{ | |
private $root; | |
private $rootTheme; | |
public function __construct($root, $rootTheme) | |
{ | |
$this->root = $root; | |
$this->rootTheme = $rootTheme; | |
} | |
public function parse($name) | |
{ | |
list($bundle, $controller, $template) = explode(':', $name); | |
if ($template[0] == '_') { | |
$path = $this->rootTheme.'/Custom/'.$template; | |
} elseif ($bundle === 'TestBundle') { | |
$path = $this->rootTheme.'/'.$controller.'/'.$template; | |
} else { | |
$path = $this->root.'/'.$controller.'/'.$template; | |
} | |
return new TemplateReference($path, 'php'); | |
} | |
} | |
/** | |
* Create an entity | |
*/ | |
$message = new Message(); | |
$message->sender = '[email protected]'; | |
/** | |
* Build a form from a form factory | |
*/ | |
$form_factory = new FormFactory(array( | |
new CoreExtension(), | |
// validation | |
new ValidatorExtension( | |
new Validator( | |
new ClassMetadataFactory( | |
new StaticMethodLoader() | |
), | |
new ConstraintValidatorFactory() | |
) | |
) | |
)); | |
$form = $form_factory->create(new MessageType(), $message); | |
/** | |
* Create a PHP template engine | |
*/ | |
$root = realpath(__DIR__ . '/Symfony/Bundle/FrameworkBundle/Resources/views'); | |
$rootTheme = realpath(__DIR__ . '/Symfony/Bundle/FrameworkBundle/Resources'); | |
$templateNameParser = new StubTemplateNameParser($root, $rootTheme); | |
$loader = new FilesystemLoader(array()); | |
$engine = new PhpEngine($templateNameParser, $loader); | |
/** | |
* This helper will help rendering form items | |
*/ | |
$form_helper = new FormHelper($engine, array( | |
'FrameworkBundle:Form', | |
)); | |
/** | |
* Bind it to the engine | |
*/ | |
$engine->setHelpers(array( | |
$form_helper, | |
new TranslatorHelper(new Translator($locale, new MessageSelector())), | |
)); | |
/** | |
* Bind submitted data | |
*/ | |
$submitted = false; | |
$valid = null; | |
if (isset($_POST[$form->getName()])) { | |
$form->bind($_POST[$form->getName()]); | |
$submitted = true; | |
// validation | |
if ($valid = $form->isValid()) { | |
// you may want to redirect at this state | |
} | |
} | |
/** | |
* Create the form view | |
*/ | |
$form_view = $form->createView(); | |
/** | |
* Now, it's time to render HTML! | |
*/ | |
header('Content-type: text/html; charset=utf-8'); | |
?> | |
<!DOCTYPE html> | |
<html> | |
<body> | |
<form action="" method="post" | |
<?php print $form_helper->enctype($form_view) ?> | |
novalidate="novalidate"> | |
<?php print $form_helper->widget($form_view) ?></div> | |
<input type="submit" /> | |
</form> | |
<?php if ($submitted && $valid) : ?> | |
<p><strong>Submitted form is valid.</strong></p> | |
<?php endif; ?> | |
<p><em>Message object:</em></p> | |
<pre><?php print print_r($message, true); ?></pre> | |
</body> | |
</html> |
Thanks for your feedback @michael-bouvy. I've tried to update the code so it could work with Symfony Components in their latest version, but validation fails (actually it always validate). Need to dig in a bit...
I tried this example with the latest version of the form component ("symfony/form": "2.3.*@dev"). Unfortunately this example didn't work, specifically the form creation fails because the arguments for the constructors have changed compare the old code vs the new one.
Any ideas on how to get the latest code of the forms component working outside of symfony?
http://api.symfony.com/2.3/Symfony/Component/Form/Forms.html
Here there's an explanation of how to add all needed Extensions to the Form Component, so it can be rendered in a template, validated, Csrf Protected, Translated, etc...
protected function getFormExtensions()
{
// should be moved to the Form component once absolute file paths are supported
// by the default name parser in the Templating component
$reflClass = new \ReflectionClass('Symfony\Bundle\FrameworkBundle\FrameworkBundle');
$root = realpath(dirname($reflClass->getFileName()).'/Resources/views');
$rootTheme = realpath(DIR.'/Resources');
$templateNameParser = new FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTemplateNameParser($root, $rootTheme);
$loader = new \Symfony\Component\Templating\Loader\FilesystemLoader(array());
$this->_TemplateEngine = new \Symfony\Component\Templating\PhpEngine(
$templateNameParser, $loader
);
$this->_TemplateEngine->addGlobal('global', '');
$this->_TemplateEngine->setHelpers(array(
new FrameworkBundle\Templating\Helper\TranslatorHelper(
new FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTranslator()
),
));
$secret = 'SomeSecreteSecret';
// $session = new \Symfony\Component\HttpFoundation\Session\Session();
$validator = \Symfony\Component\Validator\Validation::createValidator();
$csrfProvider = new \Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider(
$secret
);
return array(
new \Symfony\Component\Form\Extension\Validator\ValidatorExtension($validator),
new \Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationExtension(),
new \Symfony\Component\Form\Extension\Csrf\CsrfExtension(
$csrfProvider
),
new \Symfony\Component\Form\Extension\Templating\TemplatingExtension(
$this->_TemplateEngine, $csrfProvider,
array('FrameworkBundle:Form')
),
);
}
// Then in your home made Framework __constructor
$this->_formFactory = \Symfony\Component\Form\Forms::createFormFactoryBuilder()
->addExtensions($this->getFormExtensions())
->getFormFactory();
Then you just need acces to the PhpEngine->get('form') helper's form() method with an instance of a $formView
My compser.json
"require": {
"symfony/form": "_",
"symfony/validator": "_",
"symfony/http-foundation": "_",
"symfony/framework-bundle": "_",
"symfony/routing": "_",
"symfony/http-kernel": "_",
"symfony/event-dispatcher": "*"
},
Thanks nclavaud for this example, it's been very useful for me!
Big thanks!
Thanks Nicolas for this great example, it's been very useful and works great !
There's only 1 tiny mistake line #178, as FormHelper expects an instance of a class implementing CsrfProviderInterface (or null) as second argument.
Replacing lines #178-180 by the following ones works fine :