CakePHPAs you know, CakePHP can manage flash messages, information messages for the user that are stored by and in session. They are particularly used to notify some operations (result of authentication operations, operations on the database, etc..).

Essentially, flash messages are used whenever you want to say to an user: “hey man, everything is fine, or something went wrong”.

As the CakePHP documentation explains:

Often in web applications, you will need to display a one-time notification message to the user after processing a form or acknowledging data. In CakePHP, these are referred to as “flash messages”. You can set flash messages with the SessionComponent and display them with the SessionHelper::flash(). To set a message, use setFlash.

To store a message inside a controller, you can use the setFlash() method (API) provided by SessionComponent; to show a message inside a view, you can instead use the flash() method (API) provided by SessionHelper.

For example, inside a controller like this:

public function myAction() {
   // do something...
   $this->Session->setFlash("it works!");
   // do something...
}

Inside a view:

Session->flash(); ?>

As my example says, this will work correctly and will produce this html:

<div id="flashMessage" class="message">it works!</div>

This is really nice, especially because flash messages are “one-time messages” and the developer doesn’t have to worry about manage directly the session (Cake’s core will take care of this, not you). But there are problems when you wanna store more messages in a single action of a controller. Indeed this one:

public function myAction() {
   // do something...
   $this->Session->setFlash("does it work?");
   $this->Session->setFlash("it works!");
   // do something...
}

will remember only the second flash message. And the first one? Another problem is: and if I wanted to differentiate between different types of messages (for example: warnings, errors, confirmations, etc..)?
As the documentation explains, “using the $key parameter you can store multiple messages, which can be output separately” (quoting the documentation example):

// set a bad message.
$this->Session->setFlash('Something bad.', 'default', array(), 'bad');
// set a good message.
$this->Session->setFlash('Something good.', 'default', array(), 'good');

These flash messages will be output as html and styled differently:

// in a view.
echo $this->Session->flash('good');
echo $this->Session->flash('bad');

You can also pass a class in $params array using again $this->Session->setFlash:

$this->Session->setFlash('Example message text', 'default', array('class' => 'example_class'));

Using $this->Session->flash(), the output will be:

<div id="flashMessage" class="example_class">Example message text</div>

But all this is very uncomfortable to use and is very difficult to develop using the proposal syntax.
So, how to solve? It’s simple: rewrite component and helper.
First, you need to copy

lib/Cake/Controller/Component/SessionComponent.php

in

app/Controller/Component/

and

lib/Cake/View/Helper/SessionHelper.php

in

app/View/Helper/

This allows you to override the classes of Cake’s core with yours, that will be used first (note: you must not edit directly Cake’s core, never! Just rewrite components, helpers, views, etc.).

Then, in app/Controller/Component/SessionComponent.php replace the setFlash() function with this one:

public function setFlash($message, $type="message") {
	//If flash messages are already stored in session, fetch array
	if(CakeSession::check('flashMessages'))
		$messages = CakeSession::read('flashMessages');
	//Else, create a new empty array
	else
		$messages = array();
	//If the flash message type is not supported (only "error", "notice", "success" and "warning"), set it to "notice"
	if(!in_array($type = strtolower($type), array("error", "notice", "success", "warning")))
		$type = "notice";
	//Add the passed flash message to the array
	$messages[$type][] = $message;
	//Store in sessione
	CakeSession::write('flashMessages', $messages);
}

With this, now you can store flash messages like this (it will store an error):

echo $this->Session->flash('something has gone wrong', 'error');

Again, in app/View/Helper/SessionHelper.php replace the flash() function with this one:

public function flash() {
	//Check if flash messages are stored in session
	if(CakeSession::check('flashMessages')) {
		//Retrieve saved flash messages and deletes them from the session
		$messages = CakeSession::read('flashMessages');
		CakeSession::delete('flashMessages');
		//Prepare the $out variable that will contain the final output
		$out = "";
		//Loop every flash message that is stored in session, appending to the variable $out
		foreach ($messages as $type => $message) {
			foreach ($message as $text)
				$out .= '<div class="'.$type.'">'.$text.'</div>';
		}
		//Return flash messages in html
		return $out;
	}
}

It will return flash messages as:

<div class="$type">$text</div>

Now a new example. Inside your controller:

$this->Session->setFlash("This is just a warning", "warning");
$this->Session->setFlash("It's ok!", "success");
$this->Session->setFlash("Something seems to have gone wrong", "error");
$this->Session->setFlash("A simple, innocent news.", "notice");

It will be displayed like this:

<div class="warning">This is just a warning</div>
<div class="success">It's ok!</div>
<div class="error">Something seems to have gone wrong</div>
<div class="notice">A simple, innocent news.</div>

Wonderful, is not it?

Since we are here, just add a little style for flash messages. In the view, you have to display messages like this:

<div id="flashMessages">
	<?php echo $this->Session->flash(); ?>
</div>

Download these files and move them to app/webroot/img/, then add to your style sheet:

#flashMessages div {
	background: no-repeat 5px 5px;
	border-radius: 5px;
	border-style: solid;
	border-width: 2px;
	box-shadow: 0 5px 5px -5px #555;
	margin-bottom: .5em;
	min-height: 24px;
	padding: 5px 10px 5px 35px;
}

#flashMessages .error {
	background-color: #FFCCCC;
    background-image: url("/img/error.png");
    border-color: #8F0000;
    color: #8F0000;
}

#flashMessages .notice {
	background-color: #e6ecf2;
	background-image: url("/img/notice.png");
	border-color: #314e6c;
	color: #314e6c;
}

#flashMessages .success {
    background-color: #DEFADE;
    background-image: url("/img/success.png");
    border-color: #267726;
    color: #267726;
}

#flashMessages .warning {
    background-color: #FFEEBF;
    background-image: url("/img/warning.png");
    border-color: #B87D00;
    color: #B87D00;
}

This is the result:

flash messages

A great work guys! ;-)

Espandi/comprimi Commenti
Carlos
(16/02/2014, 20:16)

This is awesome! Just needed to tweak some issues… but nothing serious. Really helpful… and stylish!! :D

XHTML - Puoi usare questi tag: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>