Skip to content

Instantly share code, notes, and snippets.

@nclavaud
Created November 22, 2011 21:16
Show Gist options
  • Save nclavaud/1386997 to your computer and use it in GitHub Desktop.
Save nclavaud/1386997 to your computer and use it in GitHub Desktop.
Symfony2 standalone Form component example
<?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>
@michael-bouvy
Copy link

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 :

$form_helper = new FormHelper($engine, null, array(
    'FrameworkBundle:Form',
));

@nclavaud
Copy link
Author

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...

@onema
Copy link

onema commented May 8, 2013

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?

@juanmf
Copy link

juanmf commented Jun 23, 2013

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": "*" },

@ivanchuryumov
Copy link

Thanks nclavaud for this example, it's been very useful for me!

@MacDada
Copy link

MacDada commented Sep 9, 2014

Big thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment