diff --git a/sfcasts/handle-form.md b/sfcasts/handle-form.md new file mode 100644 index 0000000..3d2ad40 --- /dev/null +++ b/sfcasts/handle-form.md @@ -0,0 +1,110 @@ +# Handling the Submitted Form in the Controller + +Alright, we've built, created, rendered, and styled our form (I swear, I’ve +done my best so far!). I've given it my all and now our form is ready for +submission. Now, as any seasoned backend developer will tell you, the real +fun begins when we start dealing with that submitted data. Let's jump back +into our controller and make this form functional. + +## Updating Controller to Handle Form Data + +Open up `src/Controller/AdminController.php`, and in the `newStarshipPart()` +method, right below the form object, add `$form->handleRequest()`. + +To pull this off, we need to pass the current request object to this +method. You're familiar with this by now. Inject a `$request` from +HTTP Foundation as a method argument and pass the `$request` to this +method. You might be wondering what this `handleRequest()` is all about. +It simply grabs the submitted data from the request, applies that data to +your form, and now your form contains the user's submitted values. + +## Checking Form Submission + +Next, we want to know whether the form has actually been submitted or we just +load the form page. That's a breeze — just write `if ($form->isSubmitted())`. +Then within that `if`, we can retrieve the submitted data with `$form->getData()`. + +Since our form type has a `data_class` option set to `StarshipPart::class`, +the data you retrieve here isn't a straightforward PHP array. Instead, it's a +`StarshipPart` entity instance with the fields already filled in for us. +Skeptical? Go ahead and check it yourself. Let's assign it to a `$part` +variable and below `dd($part)`. + +## Testing Our Form + +Back in the browser, I'll quickly fill in the form. Hit create to submit it +and voila, a shiny new `StarshipPart` object with the data we sent. Notice +that no ID is set because Doctrine hasn't saved it in the database yet. +I'll quickly add a PHPDoc above the variable to make PhpStorm's autocomplete +happier, and delete the `dd()` statement. + +## Saving Data with Doctrine's EntityManager + +To save the new part, we need Doctrine's `EntityManager`. Inject it with +`EntityManagerInterface $entityManager` in the method signature. Then, back +in the `if`, add `$entityManager->persist($part)`, passing the `$part` +object and next `$entityManager->flush()`. + +Back to the browser, I'll set the name to: "Legacy Hyperdrive". +Give it a fair price, and don’t forget about important note: + +> Be careful with high revs! + +Now, hit the submit button again. Did it finally work? Well, at least there are +no errors. Head to the `$part` page and search for "legacy hyperdrive". +There it is, your new shiny Starship part, ready for sale. + +## Celebrating Success with Flash Messages + +Let's celebrate this moment properly. I'm going to add a successful flash +message. Flash messages are temporary messages stored in the session and +shown exactly once. They are perfect for things like: + +> Your part was created successfully! + +If you peek into `templates/base.html.twig` You'll see we already have code +that loops over flash messages and renders them with nice styling depending +on the type: success, warning, error, default. After saving the entity to the +database, let's write this: `$this->addFlash()` + +First argument: the message “type” - it helps to control styling. Write +`success` here. Second argument: the content of the message. How about +`sprintf('The part "%s" was created.', $part->getName())`. + +## Avoiding Duplication and Redirecting User + +To avoid duplicate form submissions when the user refreshes the page - which +might lead to unwanted parts floating around in space - let's finish the +process with a redirect. This is a classic best practice for POST forms. +Let's return `$this->redirectToRoute()`. + +We can redirect anywhere, but I'll send users back to the part list for +convenience. It should be `app_part_index` route name. + +## Testing the Overall Flow + +Alright, let's create a new part again. How about a quantum reactor for the +name, set a price, and for notes, I'll say: + +> Do not exceed 120% core flux + +OK, submit the form again, and there it is. Our success flash message, +announcing that the part quantum reactor was successfully created. And if I +try to refresh the page, the message is gone, so it was shown only once, +and Chrome does not ask me if I want to resubmit the form again, so it was +redirected properly too. Sweet! + +## Adding a Second Submit Button + +Right now we only have one submit button: Create. But imagine this, if +you're feeling productive, caffeinated, on a roll, and you want to create +multiple parts quickly, one after the other, you could do this with a few +extra clicks each time, clicking on the link to return to the form. But +wouldn't it be so much faster to have a second submit button, that, instead +of creating and going back to the list, creates and stays on this page with an +empty form open, so you can immediately create another part? + +Well, it might not be much faster, but it still could save someone a few hours +of their life over the course of many years of adding those parts. +You might be wondering, is that even possible? Absolutely! And we'll figure out +how in the next chapter. diff --git a/sfcasts/metadata.yml b/sfcasts/metadata.yml index c2b4b20..6566d5b 100644 --- a/sfcasts/metadata.yml +++ b/sfcasts/metadata.yml @@ -5,3 +5,6 @@ chapters: render-form: tutsFirstStep: render-form-render-the-whole-object tutsLastStep: handle-form-handle-request + handle-form: + tutsFirstStep: handle-form-check-if-submitted + tutsLastStep: multiple-submits-rename-to-create-and-close