Easy Edit Form
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
When you get to the form, it's completely blank. How could we add default data to the form?
Well, it turns out answering that question is exactly the same as answering the question
How do we create an edit form?
Let's tackle that.
In GenusAdminController
, I'm going to be lazy: copy the entire newAction()
and
update the URL to /genus/{id}/edit
. Give it a different route name: admin_genus_edit
and call it editAction()
:
// ... lines 1 - 13 | |
class GenusAdminController extends Controller | |
{ | |
// ... lines 16 - 55 | |
/** | |
* @Route("/genus/{id}/edit", name="admin_genus_edit") | |
*/ | |
public function editAction(Request $request, Genus $genus) | |
{ | |
// ... lines 61 - 79 | |
} | |
} |
Our first job should be to query for a Genus
object. I'll be lazy again and just
type-hint an argument with Genus
:
// ... lines 1 - 4 | |
use AppBundle\Entity\Genus; | |
// ... lines 6 - 8 | |
use Symfony\Component\HttpFoundation\Request; | |
// ... lines 10 - 13 | |
class GenusAdminController extends Controller | |
{ | |
// ... lines 16 - 58 | |
public function editAction(Request $request, Genus $genus) | |
{ | |
// ... lines 61 - 79 | |
} | |
} |
Thanks to the param converter from SensioFrameworkExtraBundle
, this will automatically
query for Genus
by using the {id}
value.
Passing in Default Data
This form needs to be pre-filled with all of the data from the database. So again,
how can I pass default data to a form? It's as simple as this: the second argument
to createForm
is the default data. Pass it the entire $genus
object:
// ... lines 1 - 13 | |
class GenusAdminController extends Controller | |
{ | |
// ... lines 16 - 58 | |
public function editAction(Request $request, Genus $genus) | |
{ | |
$form = $this->createForm(GenusFormType::class, $genus); | |
// ... lines 62 - 79 | |
} | |
} |
Why the entire object? Because remember: our form is bound to the Genus
class:
// ... lines 1 - 13 | |
class GenusFormType extends AbstractType | |
{ | |
// ... lines 16 - 42 | |
public function configureOptions(OptionsResolver $resolver) | |
{ | |
$resolver->setDefaults([ | |
'data_class' => 'AppBundle\Entity\Genus' | |
]); | |
} | |
} |
That means that its output will be a Genus
object, but its input should also
be a Genus
object.
Behind the scenes, it will use the getter functions on Genus
to pre-fill the form:
like getName()
. And everything else is exactly the same. Well, I'll tweak the flash
message but you get the idea:
// ... lines 1 - 13 | |
class GenusAdminController extends Controller | |
{ | |
// ... lines 16 - 58 | |
public function editAction(Request $request, Genus $genus) | |
{ | |
// ... lines 61 - 64 | |
if ($form->isSubmitted() && $form->isValid()) { | |
// ... lines 66 - 71 | |
$this->addFlash('success', 'Genus updated!'); | |
// ... lines 73 - 74 | |
} | |
// ... lines 76 - 79 | |
} | |
} |
Rendering the Edit Form
Update the template to edit.html.twig
:
// ... lines 1 - 13 | |
class GenusAdminController extends Controller | |
{ | |
// ... lines 16 - 58 | |
public function editAction(Request $request, Genus $genus) | |
{ | |
// ... lines 61 - 76 | |
return $this->render('admin/genus/edit.html.twig', [ | |
'genusForm' => $form->createView() | |
]); | |
} | |
} |
I'm still feeling lazy, so I'll completely duplicate the new template and update the h1
to say "Edit Genus":
// ... lines 1 - 22 | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-xs-12"> | |
<h1>Edit Genus</h1> | |
// ... lines 28 - 40 | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Don't worry, this duplication is temporary.
Finally, in the admin list template, I already have a spot ready for the edit link.
Fill that in with path('admin_genus_edit')
and pass it the single wildcard value:
id: genus.id
:
// ... lines 1 - 2 | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-xs-12"> | |
// ... lines 8 - 13 | |
<table class="table table-striped"> | |
// ... lines 15 - 19 | |
{% for genus in genuses %} | |
<tr> | |
// ... lines 22 - 23 | |
<td> | |
<a href="{{ path('admin_genus_edit', {'id': genus.id}) }}" class="btn btn-xs btn-success"><span class="fa fa-pencil"></span></a> | |
</td> | |
</tr> | |
{% endfor %} | |
</table> | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
LOVE it. Open up /admin/genus
in your browser.
Ah good, an explosion - I felt like things were going too well today:
Method
id
for objectGenus
does not exist in list.html.twig at line 25.
So apparently I do not have a getId()
function on Genus
. Let's check it out.
And indeed, when I created this class, I did not add a getter for ID. I'll use command
+N
,
or the "Code"->"Generate" menu to add it:
// ... lines 1 - 12 | |
class Genus | |
{ | |
// ... lines 15 - 68 | |
public function getId() | |
{ | |
return $this->id; | |
} | |
// ... lines 73 - 148 | |
} |
All right. Let's try it again. Refresh. No errors!
Edit the first genus. Check that out: it completely pre-filled the form for us.
In fact, check out this weird "TEST" text inside of funFact
. That is left over from
an earlier tutorial. I hacked in this TEST string temporarily in getFunFact()
so
we could play with markdown. This proves that the form is using the getter functions
to pre-fill things.
So, that's really interesting but let's take it out:
// ... lines 1 - 12 | |
class Genus | |
{ | |
// ... lines 15 - 106 | |
public function getFunFact() | |
{ | |
return $this->funFact; | |
} | |
// ... lines 111 - 148 | |
} |
Refresh. Change the "Fun fact" to be even more exciting, hit enter, and there it is:
Genus updated - you are amazing!
Edit that Genus again: there's the new fun fact. This is a really cool thing about the form framework: the new and edit endpoints are identical. The only difference is that one is passed default data.
So this is great! Except for the template duplication. That's not great still.
Hi! Very helpful tutorial. Had a question on form edit. I know we pass entity to the form class to pre-populate all field for us. I am in a situation, one of the field is a json string stored in database and it needs to be extracted in array before populating for Choice field(multi select)
(data_class with entity is optional as mentioned in one of the tutorial)
Wanted to see an example how we can manually pre-populate the edit form fields without passing entity class . Thanks