0

Scenario:

  • I have no AJAX.
  • I have a search form with GET method and csrf_protection disabled that filters the entities showed and paginated in myEntityListAction, reachable at the uri /myapp/myentity/list
  • The user opens /myapp/myentity/list, submit the filter form and goes to page 2. He is now on /myapp/myentity/list?entity_filter[search]=something&page=2
  • The user open another page (let's say the detail of one of the shown entities) and then clicks on /myapp/myentity/list

I want him to see the page /myapp/myentity//list?entity_filter[search]=something&page=2

I'm trying to make the filters sticky to the user's session, so that when the user re-opens /myapp/myentity/list without any parameter he gets the last seen result, with the same filters and page number.

I followed this approach but I think it's highly deprecated because i am modifying the $request object and the $_GET superglobal (due to the pagination library that uses directly $_GET), despite it works.

/**
 * @Route("/myapp/myentity/list",  name="entity_list")
 * @Method({"GET"})
 */
public function myEntityListAction(Request $request) {
    if (0===$request->query->count()) { //querystring is empty
        $prev_query=$request->getSession()->get('my_entity_list', null);
        if ($prev_query) { //is there something in session?
            $_GET=$prev_query;
            $original_request=$request;
            $request=$request->createFromGlobals();
            $request->setSession($original_request->getSession());
        }
    } else { //i have something in my querystring
        $request->getSession()->set('my_entity_list', $request->query->all()); //i store it in session
    }
    //...do the other stuff (form, filters, pagination, etc)
}

I think that i will follow another approach, redirecting the user to /myapp/myentity/list?entity_filter[search]=something&page=2 (reading from the session) so that i don't have to modify either $_GET or $request.

Here comes the question: How could I safely edit the $request to inject or change something I need without doing a redirect? Could I safely do it with a subrequest?

ste
  • 1,479
  • 10
  • 19

2 Answers2

2

Re-explaining the setup

You cannot safely edit a request. Request should not be modified inside a controller because it's the "last place" you will use it (even if you can use it in views too). It's not really safe.

Redirects are based on responses, not requests.

(Bonus: Something good to let the user reset (or "clear") this information in the session. I suggest you to create a button for that.)

Let's re-explain your (already good) scenarios:

  • Step 1:
    User goes to /myapp/myentity/list
    Nothing in query nor in session.
    List shown as it should
  • Step 2:
    User submits search form by clicking Page 2 for example
    Form submission makes the user go to
    /myapp/myentity/list?entity_filter[search]=something&page=2
    Once this request is made, the controller saves the query string in session as "last seen query string", the my_entity_list session parameter you already are using.
    Show list as it should with the filters
  • Step 3:
    User goes back to /myapp/myentity/list by clicking a logo for example
    Nothing in query. But having something in session. User is redirected to /myapp/myentity/list?{last_query_string}
    And then go back to step 2
  • Step 4:
    User clicks on "Clear filters" button.
    AJAX or plain request, doesn't matter: the my_entity_list is unset from the session by any controller.
    Redirect the user to /myapp/myentity/list.
    Back to step 1.

Now, the code

Your code must be also reorganized with something similar to this:

/**
 * @Route("/myapp/myentity/list",  name="entity_list")
 * @Method({"GET"})
 */
public function myEntityListAction(Request $request) {

    // First, get query string
    $queryStringArguments = $request->query->all();

    if (0 === count($queryStringArguments)) {
        $lastQueryStringArguments = $request->getSession()->get('my_entity_list', null);

        if ($lastQueryStringArguments) {
            // Just replace the var, don't manipulate the request
            $queryStringArguments = $lastQueryStringArguments;
        }
    }

    // Whatever the var is retrieved from request or session,
    // this will always be saved in session.
    if (count($queryStringArguments)) {
        $request->getSession()->set('my_entity_list', $queryStringArguments); //i store it in session
    }

    //...do the other stuff (form, filters, pagination, etc)
}

And finally, best practices

In your case, what you are doing is absolutely not safe from injections.

You should validate the query string and retrieve only data you need.

This can be done with a FormType or simple manual validations, and you should unset all undesirable/unused query string arguments to avoid potential injections.

Using a variable like $allowedParams = ['page', 'search_query', 'filter_by_id']; or something similar is very simple to implement and will help you reset all keys that are not in this array (and therefore not add them in session nor manipulate your forms with html/javascript injections)

Alex Rock
  • 831
  • 6
  • 26
  • The need to edit `$_GET` and subsequently `$request` came from the need to write something general that can be easily (without other edits) and safely reused in other controllers. Thus such a way with either approaches I can use my FormEntities and all other code in the controller transparently. Anyway thanks for your response. – ste Feb 22 '17 at 17:10
1

You could use Before filter for that. In the listener, check your session, extract saved path (or filters) and just redirect (if you want) using something like

$event->setController(function() use ($redirectUrl) {
    return new RedirectResponse($redirectUrl);
});

Also you can get request using $event->getRequest() and change it as you want.

E.K.
  • 1,045
  • 6
  • 10