A Slim Flash component that will survive a double redirect

Has anyone seen a Slim Flash component that will survive a double redirect?

The slim/flash component seems to work fine for a single redirect, but it auto acknowledges messages from the previous request as a part of the Messages constructor.

But if you have a double redirect (perhaps an error on the initial request, add a message, redirect to /support, but that will redirect to /support/contact), the message you initially add gets swallowed and not displayed.

Whilst the better solution is to make sure all redirects go to an actual page, I’d rather have a robust component that doesn’t drop messages before they’ve been rendered to a page.

Looking Slim 2 and the keep() method on the Flash middleware, a solution might be to create a Keep Flash middleware component that will re-queue all messages (via Messages::getMessages(), or extend the Messages class to provide something more suited to addMessage en masse from an associative array) and attach this to any route level redirects, and if required, be a part of any action level redirects.

Hi @reuben.helms I’m not sure, but maybe there is already an open issue on Github #36 with a similar (same?) issue.

1 Like

how do you handle the double redirect?

in route handler 1 you redirect to handler 2 and then redirect to final? if so cant you check if flash is set before every redirect and if it is reset it?

not sure how the flash thing works, but maybe you are executing a getMessages() or some such before routing on the middle page. if so you could try moving the re route part above the execution stack if possible. (my thinking since php requests dont have shared resources other than cookies / sessions / query string etc, theres no way for the flash system to know that it was 1 hop or 30 unless a “clear” or a “get” was executed on it. in which case it retrieves it from wherever its storing it and does something with it. if you cant move the order then you could try reset it before redirecting.

1 Like

I did see that one.

I think the issue for the OP was that he wanted to inspect the Flash messages queued for the next display as a part of his validation. In the current Messages class there’s a $forNext defined as a protected member, but it’s never used.

My morning thought is to put something on the return side where I set up the Flash in the middleware. For me, that’s a part of a SessionMiddleware component, as I tend not to start the session until the 4th middleware item.

I haven’t implemented it yet, but I will be today (AEST) but my approach will be to detect if the response is a redirect (check for Response status of 301 or 302, and maybe check for AJAX responses, too) and if there are any messages in $forNow or $fromPrevious (same as the getMessages response), then I’ll want to re-queue those back into storage, ready to be popped and displayed on the next call, or have the same thing happen again on the redirect.

And thank you, @odan, for your Slim 4 articles and forum contributions. Migrating from Slim 2 to Slim 4 has been a learning experience made easier from having practical examples. I have yet to get to a deeper understanding of middleware ordering, particularly it’s LIFO nature and why many examples add the Routing Middleware where they do. i.e. added so that it would execute first. But that’s a topic for another question.


Updated and implemented.

This is the snippet of code I use in my SessionMiddleware. Seems fitting because it’s the same location that I use to assign the $_SESSION to the Flash storage.

    $response = $handler->handle($request);

    // Any messages intended to displayed on a redirect or AJAX response will be swallowed if not re-queued
    // Assumes that Flash responses are not rendered into the AJAX response
    $statusCode = $response->getStatusCode();
    if (($statusCode >= 300 and $statusCode < 400) or
            $response->getHeaderLine('X-Requested-With') === 'XMLHttpRequest') {
        $messages = $this->flash->getMessages();
        if (!empty($messages) and is_array($messages)) {
            foreach ($messages as $key => $values) {
                foreach ($values as $value) {
                    $this->flash->addMessage($key, $value);
                }
            }
        }
    }

    return $response;

There are likely faster ways to merge the current messages in the component with messages that might have been queued for the next render, but this method doesn’t make too many assumptions about what is going on under the hood.

Ok good. An alternative approach would be to change the flash message storage inside a middleware to $_SESSION only when the request is not an HTTP redirect.

Use Symfony Session.
It has Flash that stay alive untill you call it :slight_smile:, it doesn’t matter how many times you redirect.