2
votes

Basically it breaks down like this:

  • A master template page is loaded when one visits the application.
  • Links are handled by an onclick handler, which reload relevant portions of the master via a jQuery ajax call, trigger other events, etc.
  • After the initial onclick handler completes, it then calls two or more ajax callbacks which update other portions of the page.

The problem:

  • The final callback reads CakePHP's flash message command $session->flash(), and displays the result in a simple javascript popup, who's div tag is created via $('body').prepend(). Once the popup is closed, the element that was created is .remove()'ed from the DOM.
  • When the user clicks a link and the flash message callback is ran for the next page, the CakePHP flash message data never got deleted, and it redisplays the message. This will repeat anywhere from 2-5 times as one continues to click pages, or until a little time has passed, and then finally delete the message from the PHP session.
  • When visiting the ajax link directly, the flash data is displayed once, and then deleted after the page is reloaded.

This has been boggling my mind for the last week. I've trimmed out every other unrelated thing except the callback itself, and it still displays the same behavior. Frustrating.

The code is quite simple, and looks like so:

The layout page which gets handed back after an onclick call.

<?php echo $content_for_layout ?>

<script>
  $(window).ready(function() {
    page_load_callback();
  });
</script>

The javascript.

function page_load_callback() {

  $.ajaxSetup({
        cache: true // We want our client to cache stuff, and override this in PHP.
  });

  $.ajax({
        cache: false, // Force this to not cache via jQuery
        beforeSend: function() {
            $('body').prepend('<div id="flash_message_popup"></div>');
        },
        url: flash_message_popup_link, // global variable set in default.ctp
        success: function(data) {
            $('#flash_message_popup').html(data);
            if ($('#flash_message_popup').is(':empty')) {
                $('#flash_message_popup').remove();
            } else {
                create_popup('flash_message_popup');
            }
        },
        error: function(a, b, c) {
            $('#flash_message_popup').remove();
        }
    });
}

function create_popup(tag) {
    if (editing_hall == true) {
        return false;
    }
    var selector = '#' + tag;
    $(selector).attr('style', 'position: absolute; display: none; top: 5; left: 5; z-index: 100;');
    $(selector).slideDown('slow');
}

The *.ctp file.

<?php if ($message = $session->flash()): ?>
<span id="flash_data">
    <div class="flash_message"><?php echo $message; ?></div>
    <div class="btn_large bottom_round"><a name="" onclick="$('#flash_message_popup').slideUp('slow', function() { $('#flash_message_popup').remove(); });">close</a></div>
</span>
<?php endif; ?>

The controller method.

function flash_message_popup() {
    $expires = 60*60*24*30*7;
    header('Expires: '.gmdate('D, d M Y H:i:s', time()-$expires) . ' JST'); // minus for past  
    $this->layout = 'ajax';

    $this->render('../elements/flash_message_popup');
}

Sessions get stored in a database, and haven't proven to be a problem up until this specific instance. I'm not sure if this is a browser caching problem (I shouldn't be? I am specifically not caching via both PHP and jQuery), a quirky session problem, a timing issue, something going haywire in my javascript, or what. I've tried adding a redundant callback which deletes the session in question with $this->Session->delete('Messages.flash'); with no luck as well.

If anyone has any advice I'd love to hear it. I am plum out of ideas.

Edit: I've also checked the Apache logs, and the callback is definitely getting called by the ajax query, but not deleting the session message. Only after I manually call it does it delete.

2
I've currently returned to printing the flash message inline in the /controller/action view that gets returned. However, this is not what I want to do. We are specifically designing pages so that the html itself can be cached, and the relevant portions of the page updated via ajax calls. If the message changes the client will unfortunately read a prior cached page, which is undesirable. I'm still banging my head as to why a callback after the primary page loads isn't deleting the session data. Argh. - bojo
I've slowed the callback down with a jQuery timer library, which helps alleviate the problem, but doesn't eliminate it entirely. Still thinking on it. - bojo

2 Answers

0
votes

I think what's happening is that AJAX is doing stuff with the Session flash before the PHP has reset it. Normally, it is set on success or failure in the CRUD methods, e.g.

    if ($this->Invoice->save($this->data)) {
        $this->Session->setFlash(__('The Invoice has been saved', true));
        $this->redirect(array('action' => 'index'));
    } else {
        $this->Session->setFlash(__('The Invoice could not be saved. Please, try again.', true));
    }

Because you are doing AJAX stuff, it may not be reaching that code before the flash message is displayed. In this situation, I would either reset it on entry to the method or use an AJAX call to reset it after you have displayed it.

0
votes

The problem appears to be too many AJAX requests conflicting with database sessions, so the timing is completely off, and the database data seems to be getting overwritten with older session data. I will leave it at that and close this question.