If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
Adding edit was quick! But the entire template is now duplicated. This includes the code to render the form and the other blocks that include the needed CSS and JS files.
First, copy the form rendering code and move that into a new file: _form.html.twig
:
{{ form_start(genusForm) }} | |
{{ form_row(genusForm.name) }} | |
{{ form_row(genusForm.subFamily) }} | |
{{ form_row(genusForm.speciesCount, { | |
'label': 'Number of Species' | |
}) }} | |
{{ form_row(genusForm.funFact) }} | |
{{ form_row(genusForm.isPublished) }} | |
{{ form_row(genusForm.firstDiscoveredAt) }} | |
<button type="submit" class="btn btn-primary" formnovalidate>Save</button> | |
{{ form_end(genusForm) }} |
Paste it here.
In edit, just include that template: include('admin/genus/_form.html.twig')
:
... lines 1 - 22 | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-xs-12"> | |
<h1>Edit Genus</h1> | |
{{ include('admin/genus/_form.html.twig') }} | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Copy that, open new.html.twig
, and paste it there:
... lines 1 - 22 | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-xs-12"> | |
<h1>New Genus</h1> | |
{{ include('admin/genus/_form.html.twig') }} | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Ok, I'm feeling better. Refresh now: everything still looks good.
And by the way, if there were any customizations you needed to make between
new and edit, I would pass a variable in through the second argument of the include
function and use that to control the differences.
So let's fix the last problem: the duplicated block overrides.
To solve this, we'll need a shared layout between these two templates. Create a new
file called formLayout.html.twig
. This will just be used by these two templates.
Copy the extends
code all the way through the javascripts
block and delete it
from edit.html.twig
:
... lines 1 - 2 | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-xs-12"> | |
<h1>Edit Genus</h1> | |
{{ include('admin/genus/_form.html.twig') }} | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Paste it in formLayout.html.twig
:
{% extends 'base.html.twig' %} | |
{% block stylesheets %} | |
{{ parent() }} | |
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.6.0/css/bootstrap-datepicker.css"> | |
{% endblock %} | |
{% block javascripts %} | |
{{ parent() }} | |
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.6.0/js/bootstrap-datepicker.min.js"></script> | |
<script> | |
jQuery(document).ready(function() { | |
$('.js-datepicker').datepicker({ | |
format: 'yyyy-mm-dd' | |
}); | |
}); | |
</script> | |
{% endblock %} |
So this template itself will extend base.html.twig
, but not before adding some
stylesheets and some JavaScripts. In edit, re-add the extends
to use this template:
admin/genus/formLayout.html.twig
:
{% extends 'admin/genus/formLayout.html.twig' %} | |
... lines 2 - 14 |
Copy that, open new.html.twig
and repeat: delete the javascripts
and stylesheets
and paste in the new extends:
{% extends 'admin/genus/formLayout.html.twig' %} | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-xs-12"> | |
<h1>New Genus</h1> | |
{{ include('admin/genus/_form.html.twig') }} | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Try it! Cool! We're using our Twig tools to get rid of duplication!
Congrats team - that's it for our first form episode. You should feel dangerous. Most of the time, forms are easy, and amazing! They do a lot of work for you.
Let me give you one last word of warning: because this is how I see people get into trouble.
Right now, our form is bound to our entity and that makes this form super easy to use. But eventually, you'll need to build a form that does not look exactly like your entity: perhaps it has a few extra fields or is a combination of fields from several entities.
When you run into this: here's what I want you to do. Don't bind your form to your
entity class. Instead, create a brand new class: I usually put these classes inside
my Form
directory. For example, GenusModel
. This class will have the exact
properties that your form needs.
Bind this class to your form and add all your validation rules like normal.
After you submit your form, $form->getData()
will return this other object.
Then, it'll be your job to write a little bit of extra code that reads this data,
updates your entities - or whatever else you need that data for - and saves things.
If you have questions, let me know in the comments.
There's certainly more to learn, but don't wait! Get out there and build something crazy cool!
Seeya guys next time!
// composer.json
{
"require": {
"php": ">=5.5.9",
"symfony/symfony": "3.1.*", // v3.1.4
"doctrine/orm": "^2.5", // v2.7.2
"doctrine/doctrine-bundle": "^1.6", // 1.6.4
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
"symfony/swiftmailer-bundle": "^2.3", // v2.3.11
"symfony/monolog-bundle": "^2.8", // 2.11.1
"symfony/polyfill-apcu": "^1.0", // v1.2.0
"sensio/distribution-bundle": "^5.0", // v5.0.22
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
"doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.0.7
"symfony/phpunit-bridge": "^3.0", // v3.1.3
"nelmio/alice": "^2.1", // 2.1.4
"doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
}
}