gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
Tip
This screencast shows the old, 2.7 and earlier form syntax. But, the code blocks below have been updated to show the new syntax!
Now that we've got our entity let's create a form! Click on AppBundle, press our handy
shortcut command+n
, and if you search form you'll find that option in the menu!
It's all coming together!
We'll call this MovieType
:
namespace AppBundle\Form; | |
use Symfony\Component\Form\AbstractType; | |
use Symfony\Component\Form\FormBuilderInterface; | |
use Symfony\Component\OptionsResolver\OptionsResolver; | |
class MovieType extends AbstractType | |
{ | |
public function buildForm(FormBuilderInterface $builder, array $options) | |
{ | |
} | |
public function configureOptions(OptionsResolver $resolver) | |
{ | |
} | |
} |
Notice it was actually smart enough to put that inside of a Form
directory and
build out the whole structure that we'll need. All I need next is for it to pour me
a cup of coffee.
The first thing we always do inside of here is call $resolver->setDefaults(array())
and pass the data_class
option so that it binds it to our Movie
entity. Conveniently,
this gives us more autocomplete: we can just type Movie
and it adds the rest:
... lines 1 - 8 | |
class MovieType extends AbstractType | |
{ | |
... lines 11 - 15 | |
public function configureOptions(OptionsResolver $resolver) | |
{ | |
$resolver->setDefaults(array( | |
'data_class' => 'AppBundle\Entity\Movie' | |
)); | |
} | |
} |
This will even help us build our fields. If we type $builder->add('')
here, because
this is bound to our Movie
entity, it knows all the properties we have there. So
let's plug in our property of title
which should be a text
field, samsCharacterName
which is probably a text field as well and isMainCharacter
which will be a checkbox.
We'll want to make sure that we prevent html5 validation on that. A third argument of
'required' => false
will take care of that for us and even that has autocomplete.
It's madness!
... lines 1 - 5 | |
use Symfony\Component\Form\Extension\Core\Type\CheckboxType; | |
... lines 7 - 8 | |
use Symfony\Component\Form\Extension\Core\Type\TextType; | |
... lines 10 - 12 | |
class MovieType extends AbstractType | |
{ | |
public function buildForm(FormBuilderInterface $builder, array $options) | |
{ | |
$builder->add('title', TextType::class) | |
->add('samsCharacterName', TextType::class) | |
->add('isMainCharacter', CheckboxType::class, array( | |
'required' => false, | |
)) | |
... lines 22 - 25 | |
} | |
... lines 27 - 33 | |
} |
Let's also include rating
as an integer field and lastly, releasedAt
which is a
date field:
... lines 1 - 6 | |
use Symfony\Component\Form\Extension\Core\Type\DateType; | |
use Symfony\Component\Form\Extension\Core\Type\IntegerType; | |
use Symfony\Component\Form\Extension\Core\Type\TextType; | |
... lines 10 - 12 | |
class MovieType extends AbstractType | |
{ | |
public function buildForm(FormBuilderInterface $builder, array $options) | |
{ | |
$builder->add('title', TextType::class) | |
... lines 18 - 21 | |
->add('rating', IntegerType::class) | |
->add('releasedAt', DateType::class, array( | |
... line 24 | |
)); | |
} | |
... lines 27 - 33 | |
} |
We can control how this date field renders: by default with Symfony it would be 3
select boxes. I'll set a widget
option, except I don't remember what value to set
that to. No worries, I'll just hold the command key over the widget
option and
it will take me straight to where that is setup inside of the core code:
... lines 1 - 11 | |
namespace Symfony\Component\Form\Extension\Core\Type; | |
... lines 13 - 26 | |
class DateType extends AbstractType | |
{ | |
const DEFAULT_FORMAT = \IntlDateFormatter::MEDIUM; | |
... lines 30 - 167 | |
public function configureOptions(OptionsResolver $resolver) | |
{ | |
... lines 170 - 202 | |
$resolver->setDefaults(array( | |
... lines 204 - 206 | |
'widget' => 'choice', | |
... lines 208 - 224 | |
)); | |
... lines 226 - 235 | |
$resolver->setAllowedValues('widget', array( | |
'single_text', | |
'text', | |
'choice', | |
)); | |
... lines 241 - 245 | |
} | |
... lines 247 - 321 | |
} |
Why is that awesome you ask? Because I can search for what I need inside of here
and boom setAllowedValues
single_text
, text
and choice
. So let's paste
single_text
back into our file.
... lines 1 - 6 | |
use Symfony\Component\Form\Extension\Core\Type\DateType; | |
... line 8 | |
use Symfony\Component\Form\Extension\Core\Type\TextType; | |
... lines 10 - 12 | |
class MovieType extends AbstractType | |
{ | |
public function buildForm(FormBuilderInterface $builder, array $options) | |
{ | |
$builder->add('title', TextType::class) | |
... lines 18 - 22 | |
->add('releasedAt', DateType::class, array( | |
'widget' => 'single_text' | |
)); | |
} | |
... lines 27 - 33 | |
} |
That trick of holding command and clicking works for the field types too: command+click
integer
and suddenly you're inside the class that provides this!
... lines 1 - 11 | |
namespace Symfony\Component\Form\Extension\Core\Type; | |
... lines 13 - 19 | |
class IntegerType extends AbstractType | |
{ | |
... lines 22 - 37 | |
public function configureOptions(OptionsResolver $resolver) | |
{ | |
... lines 40 - 47 | |
$resolver->setDefaults(array( | |
// deprecated as of Symfony 2.7, to be removed in Symfony 3.0. | |
'precision' => null, | |
// default scale is locale specific (usually around 3) | |
'scale' => $scale, | |
'grouping' => false, | |
// Integer cast rounds towards 0, so do the same when displaying fractions | |
'rounding_mode' => IntegerToLocalizedStringTransformer::ROUND_DOWN, | |
'compound' => false, | |
)); | |
... lines 58 - 69 | |
} | |
... lines 71 - 78 | |
} |
It's like being teleported but, you know, without any risk to your atoms. You can
even use this to take you to the property inside of Movie
for that specific field.
Our form is setup so let's go ahead and create this inside of our controller,
$form = $this->createForm(new MovieType(), $movie);
. Like always, we need to pass
our form back into our template with $form->createView()
:
... lines 1 - 5 | |
use AppBundle\Form\MovieType; | |
... lines 7 - 10 | |
class MovieController extends Controller | |
{ | |
... lines 13 - 15 | |
public function newAction() | |
{ | |
$movie = new Movie(); | |
$form = $this->createForm(MovieType::class, $movie); | |
return $this->render('movie/new.html.twig', array( | |
'quote' => 'If my answers frighten you then you should cease asking scary questions. (Pulp Fiction)', | |
'form' => $form->createView() | |
)); | |
} | |
... lines 27 - 34 | |
} |
Time to render this! Click into the new.html.twig
template, ah that's right my
form is actually going to be over here in _form.html.twig
. It shouldn't surprise
you that you'll get autocomplete here on things like form_start
and form_end
:
{{ form_start(form) }} | |
... lines 2 - 8 | |
{{ form_end(form) }} |
Want more autocomplete awesomeness you say? You're mad! But ok: type {{ form_row(form) }}
and it'll auto-complete the title
field for you. So we'll plug in all of our fields
here:
{{ form_start(form) }} | |
{{ form_row(form.title) }} | |
{{ form_row(form.samsCharacterName) }} | |
{{ form_row(form.isMainCharacter) }} | |
... lines 5 - 8 | |
{{ form_end(form) }} |
And if I forget one of them, I can hit control+space
to bring up all of my options.
This will also show you some other methods that exist on that FormView
object.
Ah ok so we still have rating
and releasedAt
to add here. Clean up a bit of
indentation here and perfect!
{{ form_start(form) }} | |
{{ form_row(form.title) }} | |
{{ form_row(form.samsCharacterName) }} | |
{{ form_row(form.isMainCharacter) }} | |
{{ form_row(form.rating) }} | |
{{ form_row(form.releasedAt) }} | |
<button type="submit" class="btn btn-primary">Save</button> | |
{{ form_end(form) }} |
Time to try this out: back to our new movies page, refresh and there we go! It renders
with no problems other than the fact that this is a form only it's developer could
love. And well, maybe not even that: I'm going to make it prettier. In config.yml
,
down in the twig
key add form_themes
:
... lines 1 - 34 | |
twig: | |
... lines 36 - 37 | |
form_themes: | |
... lines 39 - 83 |
Now this should autocomplete, but for whatever reason this one key is not doing that. But for the most part, you will see autocompletion inside of your configuration files.
Right here let's plug in the bootstrap form theme: and the plugin isn't perfect because
we don't get autocomplete on this either. But I do know that there is a file inside
the project for bootstrap, so I'll go to find, file and start typing in bootstrap.
We want the one called bootstrap_3_layout.html.twig
. To cheat, I'll just copy
that file name and paste that in here:
... lines 1 - 34 | |
twig: | |
... lines 36 - 37 | |
form_themes: | |
- bootstrap_3_layout.html.twig | |
... lines 40 - 83 |
Refresh with our new form theme and .... Awesome!
// composer.json
{
"require": {
"php": ">=5.3.9, <7.3.0",
"symfony/symfony": "2.8.*", // v2.8.15
"doctrine/orm": "^2.4.8", // v2.4.8
"doctrine/dbal": "<2.5", // v2.4.5
"doctrine/doctrine-bundle": "~1.4", // 1.6.4
"symfony/assetic-bundle": "~2.3", // v2.8.1
"symfony/swiftmailer-bundle": "~2.3,>=2.3.10", // v2.4.2
"symfony/monolog-bundle": "^3.0.2", // v3.0.2
"sensio/distribution-bundle": "~5.0", // v5.0.17
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.18
"incenteev/composer-parameter-handler": "~2.0" // v2.1.2
},
"require-dev": {
"sensio/generator-bundle": "~3.0", // v3.1.2
"symfony/phpunit-bridge": "~2.7" // v2.8.15
}
}