Browse Source

Implements multiple Flash messages

The FlashComponent now stores Flash messages as a stack, meaning that if
you make successive calls to FlashComponent::set() or to the magic call,
each messages will be appended to be rendered with one call to the
FlashHelper::render() method.
Yves P 11 years ago
parent
commit
e73e974ce8

+ 17 - 3
src/Controller/Component/FlashComponent.php

@@ -42,7 +42,8 @@ class FlashComponent extends Component
     protected $_defaultConfig = [
         'key' => 'flash',
         'element' => 'default',
-        'params' => []
+        'params' => [],
+        'clear' => false
     ];
 
     /**
@@ -59,6 +60,8 @@ class FlashComponent extends Component
 
     /**
      * Used to set a session variable that can be used to output messages in the view.
+     * If you make consecutive calls to this method, the messages will stack (if they are
+     * set with the same flash key)
      *
      * In your controller: $this->Flash->set('This has been saved');
      *
@@ -67,6 +70,7 @@ class FlashComponent extends Component
      * - `key` The key to set under the session's Flash key
      * - `element` The element used to render the flash message. Default to 'default'.
      * - `params` An array of variables to make available when using an element
+     * - `clear` A bool stating if the current stack should be cleared to start a new one
      *
      * @param string|\Exception $message Message to be flashed. If an instance
      *   of \Exception the exception message will be used and code will be set
@@ -91,12 +95,19 @@ class FlashComponent extends Component
             $options['element'] = 'Flash/' . $element;
         }
 
-        $this->_session->write('Flash.' . $options['key'], [
+        $messages = [];
+        if ($options['clear'] === false) {
+            $messages = $this->_session->read('Flash.' . $options['key']);
+        }
+
+        $messages[] = [
             'message' => $message,
             'key' => $options['key'],
             'element' => $options['element'],
             'params' => $options['params']
-        ]);
+        ];
+
+        $this->_session->write('Flash.' . $options['key'], $messages);
     }
 
     /**
@@ -106,6 +117,9 @@ class FlashComponent extends Component
      * success.ctp element under `src/Template/Element/Flash` for rendering the
      * flash message.
      *
+     * If you make consecutive calls to this method, the messages will stack (if they are
+     * set with the same flash key)
+     *
      * Note that the parameter `element` will be always overridden. In order to call a
      * specific element from a plugin, you should set the `plugin` option in $args.
      *

+ 10 - 2
src/View/Helper/FlashHelper.php

@@ -57,6 +57,9 @@ class FlashHelper extends Helper
      * ]);
      * ```
      *
+     * If you have several messages stored in the Session, each message will be rendered in its own
+     * element.
+     *
      * @param string $key The [Flash.]key you are rendering in the view.
      * @param array $options Additional options to use for the creation of this flash message.
      *    Supports the 'params', and 'element' keys that are used in the helper.
@@ -77,10 +80,15 @@ class FlashHelper extends Helper
                 $key
             ));
         }
-        $flash = $options + $flash;
         $this->request->session()->delete("Flash.$key");
 
-        return $this->_View->element($flash['element'], $flash);
+        $out = '';
+        foreach ($flash as $message) {
+            $message = $options + $message;
+            $out .= $this->_View->element($message['element'], $message);
+        }
+
+        return $out;
     }
 
     /**

+ 102 - 24
tests/TestCase/Controller/Component/FlashComponentTest.php

@@ -67,16 +67,18 @@ class FlashComponentTest extends TestCase
 
         $this->Flash->set('This is a test message');
         $expected = [
-            'message' => 'This is a test message',
-            'key' => 'flash',
-            'element' => 'Flash/default',
-            'params' => []
+            [
+                'message' => 'This is a test message',
+                'key' => 'flash',
+                'element' => 'Flash/default',
+                'params' => []
+            ]
         ];
         $result = $this->Session->read('Flash.flash');
         $this->assertEquals($expected, $result);
 
         $this->Flash->set('This is a test message', ['element' => 'test', 'params' => ['foo' => 'bar']]);
-        $expected = [
+        $expected[] = [
             'message' => 'This is a test message',
             'key' => 'flash',
             'element' => 'Flash/test',
@@ -86,7 +88,7 @@ class FlashComponentTest extends TestCase
         $this->assertEquals($expected, $result);
 
         $this->Flash->set('This is a test message', ['element' => 'MyPlugin.alert']);
-        $expected = [
+        $expected[] = [
             'message' => 'This is a test message',
             'key' => 'flash',
             'element' => 'MyPlugin.Flash/alert',
@@ -97,16 +99,53 @@ class FlashComponentTest extends TestCase
 
         $this->Flash->set('This is a test message', ['key' => 'foobar']);
         $expected = [
-            'message' => 'This is a test message',
-            'key' => 'foobar',
-            'element' => 'Flash/default',
-            'params' => []
+            [
+                'message' => 'This is a test message',
+                'key' => 'foobar',
+                'element' => 'Flash/default',
+                'params' => []
+            ]
         ];
         $result = $this->Session->read('Flash.foobar');
         $this->assertEquals($expected, $result);
     }
 
     /**
+     * test setting messages with using the clear option
+     *
+     * @return void
+     * @covers \Cake\Controller\Component\FlashComponent::set
+     */
+    public function testSetWithClear()
+    {
+        $this->assertNull($this->Session->read('Flash.flash'));
+
+        $this->Flash->set('This is a test message');
+        $expected = [
+            [
+                'message' => 'This is a test message',
+                'key' => 'flash',
+                'element' => 'Flash/default',
+                'params' => []
+            ]
+        ];
+        $result = $this->Session->read('Flash.flash');
+        $this->assertEquals($expected, $result);
+
+        $this->Flash->set('This is another test message', ['clear' => true]);
+        $expected = [
+            [
+                'message' => 'This is another test message',
+                'key' => 'flash',
+                'element' => 'Flash/default',
+                'params' => []
+            ]
+        ];
+        $result = $this->Session->read('Flash.flash');
+        $this->assertEquals($expected, $result);
+    }
+
+    /**
      * testSetWithException method
      *
      * @return void
@@ -118,10 +157,12 @@ class FlashComponentTest extends TestCase
 
         $this->Flash->set(new \Exception('This is a test message', 404));
         $expected = [
-            'message' => 'This is a test message',
-            'key' => 'flash',
-            'element' => 'Flash/default',
-            'params' => ['code' => 404]
+            [
+                'message' => 'This is a test message',
+                'key' => 'flash',
+                'element' => 'Flash/default',
+                'params' => ['code' => 404]
+            ]
         ];
         $result = $this->Session->read('Flash.flash');
         $this->assertEquals($expected, $result);
@@ -139,10 +180,12 @@ class FlashComponentTest extends TestCase
         $this->Controller->loadComponent('Flash', ['element' => 'test']);
         $this->Controller->Flash->set('This is a test message');
         $expected = [
-            'message' => 'This is a test message',
-            'key' => 'flash',
-            'element' => 'Flash/test',
-            'params' => []
+            [
+                'message' => 'This is a test message',
+                'key' => 'flash',
+                'element' => 'Flash/test',
+                'params' => []
+            ]
         ];
         $result = $this->Session->read('Flash.flash');
         $this->assertEquals($expected, $result);
@@ -160,17 +203,19 @@ class FlashComponentTest extends TestCase
 
         $this->Flash->success('It worked');
         $expected = [
-            'message' => 'It worked',
-            'key' => 'flash',
-            'element' => 'Flash/success',
-            'params' => []
+            [
+                'message' => 'It worked',
+                'key' => 'flash',
+                'element' => 'Flash/success',
+                'params' => []
+            ]
         ];
         $result = $this->Session->read('Flash.flash');
         $this->assertEquals($expected, $result);
 
         $this->Flash->error('It did not work', ['element' => 'error_thing']);
 
-        $expected = [
+        $expected[] = [
             'message' => 'It did not work',
             'key' => 'flash',
             'element' => 'Flash/error',
@@ -181,7 +226,7 @@ class FlashComponentTest extends TestCase
         
         $this->Flash->success('It worked', ['plugin' => 'MyPlugin']);
 
-        $expected = [
+        $expected[] = [
             'message' => 'It worked',
             'key' => 'flash',
             'element' => 'MyPlugin.Flash/success',
@@ -190,4 +235,37 @@ class FlashComponentTest extends TestCase
         $result = $this->Session->read('Flash.flash');
         $this->assertEquals($expected, $result);
     }
+
+    /**
+     * Test a magic call with the "clear" flag to true
+     *
+     * @return void
+     * @covers \Cake\Controller\Component\FlashComponent::set
+     */
+    public function testCallWithClear()
+    {
+        $this->assertNull($this->Session->read('Flash.flash'));
+        $this->Flash->success('It worked');
+        $expected = [
+            [
+                'message' => 'It worked',
+                'key' => 'flash',
+                'element' => 'Flash/success',
+                'params' => []
+            ]
+        ];
+        $result = $this->Session->read('Flash.flash');
+        $this->assertEquals($expected, $result);
+        $this->Flash->success('It worked too', ['clear' => true]);
+        $expected = [
+            [
+                'message' => 'It worked too',
+                'key' => 'flash',
+                'element' => 'Flash/success',
+                'params' => []
+            ]
+        ];
+        $result = $this->Session->read('Flash.flash');
+        $this->assertEquals($expected, $result);
+    }
 }

+ 1 - 1
tests/TestCase/TestSuite/IntegrationTestCaseTest.php

@@ -118,7 +118,7 @@ class IntegrationTestCaseTest extends IntegrationTestCase
     {
         $this->post('/posts/index');
 
-        $this->assertSession('An error message', 'Flash.flash.message');
+        $this->assertSession('An error message', 'Flash.flash.0.message');
         $this->assertCookie(1, 'remember_me');
     }
 

+ 65 - 14
tests/TestCase/View/Helper/FlashHelperTest.php

@@ -46,25 +46,54 @@ class FlashHelperTest extends TestCase
         $session->write([
             'Flash' => [
                 'flash' => [
-                    'key' => 'flash',
-                    'message' => 'This is a calling',
-                    'element' => 'Flash/default',
-                    'params' => []
+                    [
+                        'key' => 'flash',
+                        'message' => 'This is a calling',
+                        'element' => 'Flash/default',
+                        'params' => []
+                    ]
                 ],
                 'notification' => [
-                    'key' => 'notification',
-                    'message' => 'This is a test of the emergency broadcasting system',
-                    'element' => 'flash_helper',
-                    'params' => [
-                        'title' => 'Notice!',
-                        'name' => 'Alert!'
+                    [
+                        'key' => 'notification',
+                        'message' => 'This is a test of the emergency broadcasting system',
+                        'element' => 'flash_helper',
+                        'params' => [
+                            'title' => 'Notice!',
+                            'name' => 'Alert!'
+                        ]
                     ]
                 ],
                 'classy' => [
-                    'key' => 'classy',
-                    'message' => 'Recorded',
-                    'element' => 'flash_classy',
-                    'params' => []
+                    [
+                        'key' => 'classy',
+                        'message' => 'Recorded',
+                        'element' => 'flash_classy',
+                        'params' => []
+                    ]
+                ],
+                'stack' => [
+                    [
+                        'key' => 'flash',
+                        'message' => 'This is a calling',
+                        'element' => 'Flash/default',
+                        'params' => []
+                    ],
+                    [
+                        'key' => 'notification',
+                        'message' => 'This is a test of the emergency broadcasting system',
+                        'element' => 'flash_helper',
+                        'params' => [
+                            'title' => 'Notice!',
+                            'name' => 'Alert!'
+                        ]
+                    ],
+                    [
+                        'key' => 'classy',
+                        'message' => 'Recorded',
+                        'element' => 'flash_classy',
+                        'params' => []
+                    ]
                 ]
             ]
         ]);
@@ -169,4 +198,26 @@ class FlashHelperTest extends TestCase
         $expected = 'flash element from TestTheme';
         $this->assertContains($expected, $result);
     }
+
+    /**
+     * Test that when rendering a stack, messages are displayed in their
+     * respective element, in the order they were added in the stack
+     *
+     * @return void
+     */
+    public function testFlashWithStack()
+    {
+        $result = $this->Flash->render('stack');
+        $expected = [
+            ['div' => ['class' => 'message']], 'This is a calling', '/div',
+            ['div' => ['id' => 'notificationLayout']],
+            '<h1', 'Alert!', '/h1',
+            '<h3', 'Notice!', '/h3',
+            '<p', 'This is a test of the emergency broadcasting system', '/p',
+            '/div',
+            ['div' => ['id' => 'classy-message']], 'Recorded', '/div'
+        ];
+        $this->assertHtml($expected, $result);
+        $this->assertNull($this->View->request->session()->read('Flash.stack'));
+    }
 }