I am using FlashMessenger Controller/Plugin to show error messages, and that works fine. I am trying to show success messages by setting the namespace to success.
$this->flashMessenger()->addSuccessMessage('Success msg!');
$successMessages = $this->flashMessenger()->hasSuccessMessages();
print_r($successMessages);exit;
$view = new ViewModel(array('success' => $successMessages));
When i print $successMessages it shows Array() as there are no messages to show.
Same is if i try to set namespace like this.
$this->flashMessenger()->setNamespace('success')->addSuccessMessage('Success msg!');
$successMessages = $this->flashMessenger()->setNamespace('success')->getMessages();
Is this the propper way to set namespace and how to show the message on view?
I am using this to show error messages, but it doesn't work with success messages.
<?php if (!empty($this->messages)): ?>
<?php foreach ($this->messages as $msg) : ?>
<div class="error-box"><?php echo $this->translate($msg) ?></div>
<?php endforeach; ?>
<?php endif; ?>
This is my FlashMessenger class.
It looks like you are writing and reading the messages in the same request.
Think that flashMessenger is mainly meant to save messages for later, as you can read in the old but meaningful ZF1 docs:
The FlashMessenger helper allows you to pass messages that the user
may need to see on the next request.
For example, you are in a request that comes from a form that is meant to add records to a list, in the controller/action that receives the form you save a record, write the message to the flashmessenger, and redirect the user to the controller/action that show the list of records again.
Something that you can see in this example from the ZF2 mvc plugins docs:
public function processAction()
{
// ... do some work ...
$this->flashMessenger()->addMessage('You are now logged in.');
return $this->redirect()->toRoute('user-success');
}
public function successAction()
{
$return = array('success' => true);
$flashMessenger = $this->flashMessenger();
if ($flashMessenger->hasMessages()) {
$return['messages'] = $flashMessenger->getMessages();
}
return $return;
}
If what you need is to read the messages in the same request (something that is also really common) you have to use the "Current Messages" methods of the helper. So, instead of calling
$this->flashMessenger()->hasSuccessMessages()
you should call
$this->flashMessenger()->hasCurrentSuccessMessages()
and instead of calling
$this->flashMessenger()->getSuccessMessages()
you should call
$this->flashMessenger()->getCurrentSuccessMessages()
You can take a look at the full list of functions, if you go to the Zend.Mvc.Controller.Plugin.FlashMessenger API docs and you search for "Current"
UPDATE
To show the messages in the view, you can use 2 approaches:
Retrieve the messages in the controller and send it to the view
In the controller
public function yourAction() {
return array (
'$successMessages'
=> $this->flashMessenger()->getCurrentSuccessMessages()
);
}
In the view
//note that getCurrentSuccessMessages() returns an array,
//so in the vew $successMessages is an array
foreach ($successMessages as $m) {
echo $m;
}
Retrieving directly in the view. The first thing that comes to mind is to use the FlashMessenger view helper (Zend\View\Helper \FlashMessenger), but im afraid it doesnt have a function to get the current messages. So, you can extend it, or even more, you can write your own view helper. You have here a good tutorial on how to do exactly this.
I just tested the following in a Controller:
$this->flashMessenger()->addSuccessMessage('test');
if ($this->flashMessenger()->hasSuccessMessages()) {
print_r($this->flashMessenger()->getSuccessMessages());
}
This resulted in Array ( [0] => test )
Based on your question it appears you have not yet called on getSuccessMessages()
To get the messages into your ViewModel you could do something like this:
$viewModel = new ViewModel();
$viewModel->setVariable('messages', ($this->flashMessenger()->hasSuccessMessages()) ? $this->flashMessenger()->getSuccessMessages() : null);
And in your View you could do this:
if (!is_null($this->messages)) {
foreach ($this->messages as $message) {
echo $message . "\n";
}
}
Try reloading the page a few times to ensure that the message is visible. I personally use the EventManager (in my Controller) to check and handle messages stored in the flashMessenger(). For example:
public function setEventManager(EventManagerInterface $events)
{
parent::setEventManager($events);
$events->attach('dispatch', function () {
$this->getServiceLocator()->get('translator')->setLocale('en');
if ($this->flashMessenger()->hasMessages()) {
$this->Flash()->Display($this->flashMessenger()->getMessages()); // Custom Controller Plugin
}
}, 100);
}
Hope this helps you out!
Related
I need a clean solution to set data after submit a page from being populated by :
$form->loadDataFrom( $Page );
There is my code :
public function FormUpdate() {
$error="Required";
$fields = new FieldList(
TextField::create('Title', 'Title')->setCustomValidationMessage($error),
TextField::create('Description', 'Description')->setCustomValidationMessage($error),
TextField::create('Subject', 'Description')->setCustomValidationMessage($error),
);
$actions = new FieldList(
FormAction::create("FormUpdateSubmit")->setTitle('Update')
);
$Page=Versioned::get_by_stage('Page', 'Live')->filter( array('SecureCode' => $_REQUEST['id'] ))->First();
$fields->push( HiddenField::create('id','SecureCode', $Page->SecureCode ));
$fields->push( CheckboxField::create('Approbation', "Approbation")->setCustomValidationMessage($error) ); ),
$required = new RequiredFields(array(
'Title','Subject','Description'
));
$form = new Form($this, 'FormModifier', $fields, $actions, $required);
$form->loadDataFrom( $Page );
$form->setAttribute('novalidate', 'novalidate');
return $form;
}
The problem... If I change Title and Description and I empty Subject field, i'm redirected back to the form page with the error message below Subject but, All fields are reloaded from $form->loadDataFrom($Page); That wasn't good. I must prevent that data to be reloaded. In this case, datas posted must replace $Page. What I have missing?
I generally use loadDataFrom on the action that called the form (rather than inside the form function). So for example:
...
public function index()
{
$form =$this->Form();
$form->loadDataFrom($this);
$this->customise(array("Form" => $form));
return $this->renderWith("Page");
}
...
That way the function only returns the base form and you alter it as and when required.
Your form will be called once when adding it in the template, and once via request. Since all actions on a controller get the request as parameter, you can modify your form function like so:
public function FormUpdate($request = null) {
Then inside your function, only populate the form if it's not called via a request, eg.
if (!$request) {
$form->loadDataFrom($Page);
}
I have created a controller for an "applications" table. The web and REST interfaces are working but I think the add and edit functions should be better.
When I tested add and edit I found the data needed to be posted in web FORM format (not JSON).
I found I needed to use "$this->request->input('json_decode')" in the save to decode the JSON data. I thought this happened automagically.
This function now works for add (edit is similar) and displays my json/add.ctp so I can return the successful record to the user.
public function add() {
if ($this->request->is('post')) {
$this->Application->create();
//Is the request REST passing a JSON object?
if (preg_match('/\.json/', $this->request->here)){
//This is a REST call
$this->set('status', $this->Application->save($this->request->input('json_decode')));
} else {
//This is an interactive session
if ($this->Application->save($this->request->data)) {
$this->Session->setFlash(__('The application has been saved.'));
return $this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The application could not be saved. Please, try again.'));
}
}
}
}
I used the "$this->request->here" to see if it ends in ".json". Is this the "correct" way to process the REST call?
There is an entire section in the CakePHP Book for this. I think it will answer your question(s):
http://book.cakephp.org/2.0/en/development/rest.html
The question is, does your action accept JSON data & Form Data? or just JSON data?
The .json is purely for the output of your data, you are able to send JSON data with the .xml extension, the difference being once the data is sterilised, it will output in XML.
if($this->request->is('post')) {
if(empty($this->request->data)){
$data = $this->request->input('json_decode', TRUE);
} else {
$data = $this->request->data;
}
} else {
$data = $this->params['url'];
}
Above is kind of what you should be doing, check if the data comes from a form, if not, decode JSON, and if it is NOT a POST, save parameters that have been included into the URL.
I am not saying the above is the "right" way to do it, but thats probably what you are looking for.
I had a small test done in PHP for a Controller I had written in Symfony2:
class DepositControllerTest extends WebTestCase {
public function testDepositSucceeds() {
$this->crawler = self::$client->request(
'POST',
'/deposit',
array( "amount" => 23),
array(),
array()
);
$this->assertEquals(
"Deposit Confirmation",
$this->crawler->filter("title")->text());
}
}
Up to here, everything was great. Problem started when I realized I wanted to disable possible re-submissions while refreshing the page. So I added a small mechanism to send nonce on every submission.
It works something like this:
class ReplayManager {
public function getNonce() {
$uid = $this->getRandomUID();
$this->session->set("nonce", $uid);
return $uid;
}
public function checkNonce($cnonce) {
$nonce = $this->session->get("nonce");
if ($cnonce !== $nonce)
return false;
$this->session->set("nonce", null);
return true;
}
}
So I had to mofidy the controller to get the nonce when displaying the form, and consume it when submitting.
But now this introduces a problem. I cant make a request to POST /deposit because I dont know what nonce to send. I thought to requesting first GET /deposit to render the form, and setting one, to use it in the POST, but I suspect Symfony2 sessions are not working in PHPUnit.
How could I solve this issue? I would not want to go to Selenium tests, since they are significant slower, not to mention that I would have to rewrite A LOT of tests.
UPDATE: I add a very simplified version of the controller code by request.
class DepositController extends Controller{
public function formAction(Request $request){
$this->replayManager = $this->getReplayManager();
$context["nonce"] = $this->replayManager->getNonce();
return $this->renderTemplate("form.twig", $context);
}
protected function depositAction(){
$this->replayManager = $this->getReplayManager();
$nonce = $_POST["nonce"];
if (!$this->replayManager->checkNonce($nonce))
return $this->renderErrorTemplate("Nonce expired!");
deposit($_POST["amount"]);
return $this->renderTemplate('confirmation.twig');
}
protected function getSession() {
$session = $this->get('session');
$session->start();
return $session;
}
protected function getReplayManager() {
return new ReplayManager($this->getSession());
}
}
I'm not sure what ReplayManager does, but it looks to me as if it is not the right class to handle the 'nonce'. As the 'nonce' is ultimately stored in and retrieved from the session it should either be handled by the controller or abstracted out into its own class which is then passed in as a dependency. This will allow you to mock the nonce (sounds like a sitcom!) for testing.
In my experience problems in testing are actually problems with code design and should be considered a smell. In this case your problem stems from handling the nonce in the wrong place. A quick refactoring session should solve your testing problems.
It is possible to access the Symfony2 session from PHPUnit via the WebTestCase client. I think something like this should work:
public function testDepositSucceeds() {
$this->crawler = self::$client->request(
'GET',
'/deposit',
);
$session = $this->client->getContainer()->get('session');
$nonce = $session->get('nonce');
$this->crawler = self::$client->request(
'POST',
'/deposit',
array("amount" => 23, "nonce" => $nonce),
array(),
array()
);
$this->assertEquals(
"Deposit Confirmation",
$this->crawler->filter("title")->text());
}
EDIT:
Alternatively, if there is a problem getting the nonce value from the session, you could try replacing the two lines between the GET and POST requests above with:
$form = $crawler->selectButton('submit');
$nonce = $form->get('nonce')->getValue(); // replace 'nonce' with the actual name of the element
I'm trying to send email notification to some users via cron job in my app.
After a few hours of reading, I've understood that the best way to do that is using Shell.
Please can someone help me to understand how to do that, how can I use one myShell class's different actions to send different notifications? I mean that how can cron access to myShell different actions.
for example.
<?php
class MyShell extends Shell {
function send_task_notifications(){
.... //this must send email every day at 00:00 am
}
function send_new_post_notifications() {
.... //this must send email every week//
}
}
?>
Both of this actions are in MyShell class.
So how can I call one of them via Cron and is this MyShell class accessible by the URL?
Your shell need to change in the following way, you need to pass a parameters based on that parameters it will execute email notification/push notification. Move your functions to a component it will work
<?php
class MyShell extends Shell {
function main()
{
$option = !empty($this->args[0]) ? $this->args[0] : ”;
echo ‘Cron started without any issue.’;
App::import(‘Component’, 'MyOwnComponent');
$this->MyOwnComponent = &new MyOwnComponent();
switch ($option)
{
case 'task_notifications':
$this->MyOwnComponent->send_task_notifications();
break;
case 'post_notifications':
$this->MyOwnComponent->send_new_post_notifications();
break;
default:
echo 'No Parameters passed .';
}
}
}
?>
Your Component file as follows
<?php
class MyOwnComponent extends Object
{
function send_task_notifications(){
.... //this must send email every day at 00:00 am
}
function send_new_post_notifications() {
.... //this must send email every week//
}
}
?>
For more details refer the link http://cakephpsaint.wordpress.com/2013/05/15/6-steps-to-create-cron-jobs-in-cakephp/
I have a certain class which receives request from different views. It handles the request and should redirect the user to the view that they were "visiting". Is there any way I can identify this view so i can load it back to the browser?
Thank you.
Why not just redirect back the referring page? Here are some functions I usually put in a MY_url_helper.php file to accomplish this:
<?php
function redirect_back()
{
redirect($_SERVER['HTTP_REFERER']);
}
function save_next($next_url = NULL)
{
if (empty($next_url))
{
$next_url = current_url();
}
$CI =& get_instance();
$CI->session->set_userdata('next_url', $next_url);
}
function redirect_next($fallback = '')
{
$CI =& get_instance();
$next = $CI->session->userdata('next_url');
if ( !empty($next))
{
$CI->session->unset_userdata('next_url');
redirect($next);
}
redirect($fallback);
}
Couple ways to use these functions:
Call redirect_back() in the controller action that processes, say, form data
Call save_next() in the controller action that displays the view, then call redirect_next() in the controller action that processes the form data
It's not all too RESTlike, but it gets the job done, freeing you to focus on the more important things in life...