Buy

Handling Form Submissions

Ok, let’s get this form to actually submit! Since we’re submitting back to the same route and controller, we want to process things only if the request is a POST.

Getting the Request object

First, we’ll need Symfony’s Request object. To get this in a controller, add a $request argument that’s type-hinted with Symfony’s Request class:

// src/Yoda/UserBundle/Controller/RegisterController.php
// ...

use Symfony\Component\HttpFoundation\Request;

class RegisterController extends Controller
{
    // ...
    public function registerAction(Request $request)
    {
        // ...
    }
}

Normally, if you have an argument here, Symfony tries to populate it from a routing wildcard with the same name as the variable. If it doesn’t find one, it throws a giant error. The only exception to that rule is this: if you type-hint an argument with the Request class, Symfony will give you that object. This doesn’t work for everything, only the Request class.

Using handleRequest

Use the form’s handleRequest method to actually process the data. Next, add an if statement that checks to see if the form was submitted and if all of the data is valid:

// src/Yoda/UserBundle/Controller/RegisterController.php

use Symfony\Component\HttpFoundation\Request;
// ...

public function registerAction(Request $request)
{
    $form = $this->createFormBuilder()
        // ...
        ->getForm()
    ;

    $form->handleRequest($request);
    if ($form->isSubmitted() && $form->isValid()) {

        // do something in a moment
    }

    return array('form' => $form);
}

The handleRequest method grabs the POST’ed data from the request, processes it, and runs any validation. And actually, it only does this for POST requests so on a GET request, $form->isSubmitted() returns false.

Tip

If you have a form that’s submitted via a different HTTP method, set the method.

If the form is submitted because it’s a POST request and passes validation, let’s just print the submitted data for now. If the form is invalid, or if this is a GET request, it’ll just skip this block and re-render the form with errors if there are any:

if ($form->isSubmitted() && $form->isValid()) {
    var_dump($form->getData());die;
}

Time to test it! We haven’t added validation yet, but the password fields have built-in validation if the values don’t match. When I submit, the form is re-rendered, meaning there was an error.

In fact, there’s now a little red box on the web debug toolbar. If we click it, we can see details about the form: what was submitted and options for each field.

Head back and fill in the form correctly. Now we see our dumped data:

array(
    'username' => string 'foo' (length=3),
    'email' => string 'foo@foo.com' (length=11),
    'password' => string 'foo' (length=3),
)

Using the Submitted Data Array

Notice that the data is an array with a key and value for each field. Let’s take this data and build a new User object from it. There is an easier way to do this, and I’ll show you in a second:

// src/Yoda/UserBundle/Controller/RegisterController.php

use Yoda\UserBundle\Entity\User;
// ...

$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
    $data = $form->getData();

    $user = new User();
    $user->setUsername($data['username']);
    $user->setEmail($data['email']);
}

Encoding the User’s Password

We still need to encode and set the password. For now, let’s copy in some code from our user fixtures to help with this. We’ll make this much more awesome in the next screencast:

// src/Yoda/UserBundle/Controller/RegisterController.php
// ...

private function encodePassword(User $user, $plainPassword)
{
    $encoder = $this->container->get('security.encoder_factory')
        ->getEncoder($user)
    ;

    return $encoder->encodePassword($plainPassword, $user->getSalt());
}

Use this function, then finally persist and flush the new User:

// src/Yoda/UserBundle/Controller/RegisterController.php
// ...

if ($form->isValid()) {
    $data = $form->getData();

    $user = new User();
    $user->setUsername($data['username']);
    $user->setEmail($data['email']);
    $user->setPassword($this->encodePassword($user, $data['password']));

    $em = $this->getDoctrine()->getManager();
    $em->persist($user);
    $em->flush();

    // we'll redirect the user next...
}

Redirecting after Success

The last step of any successful form submit is to redirect - we’ll redirect to the homepage. First, we need to generate a URL - just like we do with the path() function in Twig. In a controller, there’s a generateUrl function that works exactly the same way:

// src/Yoda/UserBundle/Controller/RegisterController.php
// ...

if ($form->isSubmitted() && $form->isValid()) {
    // ...

    $em->flush();

    $url = $this->generateUrl('event');
}

To redirect, use the redirect function and pass it the URL:

if ($form->isSubmitted() && $form->isValid()) {
    // ...
    $url = $this->generateUrl('event');

    return $this->redirect($url);
}

Tip

If you use Symfony 2.6 or newer, you have a redirectToRoute method that allows you to redirect based on the route name instead of having to generate the URL first. It’s a shortcut!

Remember that a controller always returns a Response object. redirect is just a shortcut to create a Response that’s all setup to redirect to this URL.

Ok, time to kick this proton torpedo! As expected, we end up on the homepage. We can even login as the new user!

You Don’t Need isSubmitted

Head back to the controller and remove the isSubmitted() call in the if statement:

// src/Yoda/UserBundle/Controller/RegisterController.php

$form->handleRequest($request);
if ($form->isValid()) {
    // ...
}

This actually doesn’t change anything because isValid() automatically returns false if the form wasn’t submitted - meaning, if the request isn’t a POST. So either just do this, or keep the isSubmitted part in there if you want - I find it adds some clarity.

Leave a comment!

  • 2015-10-06 Shairyar Baig

    Thanks Ryan, ya i am thinking the same. I will create a new form class and use that for sorting purpose.

    Thanks for the tips :)

  • 2015-10-05 weaverryan

    Hey Shairyar!

    Yes, this is tricky: creating new Goals and allowing them to be re-ordered all at once. Typically, I avoid the collection type when things get this complicated and instead save things via AJAX (e.g. the moment you create a new goal, I save it. The moment you re-order, I save that, etc). A few things:

    1) The getId/setId() error is coming directly from you having an "id" field on your GoalType form. You should remove this, which makes sense - you don't really want the id of the Goal being changed by the user :).

    2) Very clever solution to set the orderBy - that looks good to me. I'm not sure why setting that would suddenly cause issues with sorting, however. The collection type isn't really built to handle ordering, it's possible that you could find some weird behavior with this. For example, I *think* if it lists 3 goals on the page, and you submit 3 goals, it updates the first goal in the DB with the data from the first submitted goal, etc. In other words, I think if you had 2 goals and you re-ordered them, the first would be updated with the second's data and the second with the first's data. Does that makes sense? In other words, changing the order might be causing weird issues - which is why there's no native support with "ordering" things with the collection type.

    Let me know if this helps. You're in a complicated area, and my best advice is to consider simplifying by not using the collection type :).

    Cheers!

  • 2015-10-05 Shairyar Baig

    Hi Ryan,

    I am working with Symfony Forms and jQuery sortable and ran into a weird issue, I was hoping to get your expertise as I am out of all solutions.

    I have a form (it is a collection form) and inside that form a user can change the priority of their goals, to set the priority from top to bottom I am using jQuery sortable, i am able to send the sort order to controller just fine via ajax and I am able to save the data in db just fine as well. After completing the task of sorting and all, i reloaded the page and saw even though the sort order is saved in database but the goals are actually not being displayed in the correct order, I thought this should be easy, I already have relationship setup so an `orderBy` should solve this but this is where the problem started.

    Here is the link to the yml file where i added the `orderBy`
    https://gist.github.com/shairy...

    So after doing that as long as I do not sort the form, data gets saved just fine, but if i sort and then save i see the following error

    Neither the property "id" nor one of the methods "addId()"/"removeId()", "setId()", "id()", "__set()" or "__call()" exist and have public access in class "MyBundle\Entity\Goals".

    Why am I seeing this error? the only reason i am making use of Goal Entity Id is so that I know what sort order to assign to which id. https://gist.github.com/shairy...

    Is it possible to display the correct order from database without using orderBy in this case? At this point I am pretty clueless as to what I am doing wrong. Any help will be really appreciated.