We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

getRender() and dispatcher->forward problem

Hello, I'm trying to figure out what could be happening when using getRender() to render an email template (as Vökuró tutorial does). The method works as expected, but I have a dispatcher->forward after the email is sended, and this dispatcher no longer works (blank page) if I use the getRender() method to get the content for the email.

The email is sended allright with the template I provide to the getRender() method, but someting is happening between because instead of forwarding to other action I get an blank page, only with a direct flash messages I created before.

As soon as I substitute getRender() in the code with a string, the dispatcher starts to work.

If instead of using the dispatcher->forward I use a response->redirect, It works. But, of course, I can't use Phalcon\Flash\Direct.

I used to create this project a similar way as Vökuró tutorial does (In Vökuró works fine), but using modules, this is:

A SignUp method inside a SessionController:

app\modules\auth\controllers\SessionController.php

namespace MyApp\Modules\Auth\Controllers;

class SessionController extends ControllerBase
{
    public function signupAction()
    {
    ...
                if ($user->save()) {
                    return $this->dispatcher->forward([
                        'namespace'     => 'MyApp\Modules\Auth\Controllers',
                        'controller'    => 'index',
                        'action'        => 'index'
                    ]);
                }
    ....
    }
}

An afterSave() method inside User class to create an EmailConfirmation:

app\models\User.php

namespace Vcloud\Models;
...

class User extends \Phalcon\Mvc\Model
{
    ...
    public function afterSave()
    {
        $emailConfirmation = new EmailConfirmation();

        $emailConfirmation->user_id = $this->id;

        if ($emailConfirmation->save()) {
            $this->getDI()
                ->getFlash()
                ->notice('A confirmation mail has been sent to ' . $this->email);
        }
    }
    ...
}

An afterCreate() method inside EmailConfirmation to send the email:

app\models\EmailConfirmation.php

class EmailConfirmation extends \Phalcon\Mvc\Model
{
    ...
    public function afterCreate()
    {
        $this->getDI()
        ->getMail()
        ->send([$this->user->email => $this->user->username],
            "Please confirm your email",
            'confirmation',
            ['confirmUrl' => '/auth/confirm/' . $this->code . '/' . $this->user->email]
            );
    }
    ...
}

And a Mail library to get the corresponding template and send the email.

app\common\library\Mail.php

namespace MyApp\Mail;

use Phalcon\Mvc\User\Component;
use Swift_Message as Message;
use Swift_SmtpTransport as Smtp;
use Phalcon\Mvc\View;

class Mail extends Component
{

    protected $transport;

    public function getTemplate($name, $params)
    {
        $parameters = array_merge([
            'publicUrl' => $this->config->application->publicUrl
        ], $params);

        //If instead of returning getRender() I return "S" the dispatcher->forward works
        //return ("S");  

        return $this->view->getRender('emailTemplates', $name, $parameters, function ($view) {
            $view->setRenderLevel(View::LEVEL_LAYOUT);
        });

        return $view->getContent();
    }

    public function send($to, $subject, $name, $params)
    {
        $mailSettings = $this->config->mail;

        $template = $this->getTemplate($name, $params);

        // Create the message
        $message = Message::newInstance()
            ->setSubject($subject)
            ->setTo($to)
            ->setFrom([
                $mailSettings->fromEmail => $mailSettings->fromName
            ])
            ->setBody($template, 'text/html');

        if (isset($mailSettings) && isset($mailSettings->smtp)) {

            if (!$this->transport) {
                $this->transport = Smtp::newInstance(
                    $mailSettings->smtp->server,
                    $mailSettings->smtp->port,
                    $mailSettings->smtp->security
                )
                ->setUsername($mailSettings->smtp->username)
                ->setPassword($mailSettings->smtp->password);
            }

            // Create the Mailer using your created Transport
            $mailer = \Swift_Mailer::newInstance($this->transport);

            return $mailer->send($message);
        }
    }
}

After Signing Up, the user is recorded, the emailConfirmation record is created also in the database and the email is sended correctly.

But, instead of forwarding to other action, I get a blank page only with the flash message: "A confirmation mail has been sent to [email protected]". I get no Errors or Exceptions.

Does somebody know what could be happening?

Is this code in if is happening ? Add some event listener to dispatcher and log all beforeExecuteRoute and check if forwarding is happening



3.9k

Thanks Wojciech! I'll go that way.

But I'm trying now to log this and can't find a way of doing it.

I know how to catch Exceptions in the dispatcher (dispatch:beforeException) and treat them, but logging All beforeExecuteRoute it's hard for me.

I'm trying this:

$di->setShared('dispatcher', function() use ($di) {
    $eventsManager = new EventsManager();
    $eventsManager->attach('dispatch:beforeExecuteRoute', function($event, $dispatcher) use ($di) {
        // Then I don't know what to include here
        // I can't find info about doing this in documentation
    });
    $dispatcher = new Dispatcher();
    $dispatcher->setEventsManager($eventsManager);
    return $dispatcher;
});

But I can't find info about doing that in Phalcon :(

Please, can you tell me how to do it? so I will go a step foward in order to debug my own projects properly

Many Thanks!

edited Oct '16

Just for example use file logger :) https://docs.phalcon.io/pl/latest/api/Phalcon_Logger_Adapter_File.htm

I myself using queue for adding messages + cli task for writing into file



3.9k

Thanks Wojciech!

So now I have a simple file logger:

$di->setShared('dispatcher', function() use ($di) {

    $eventsManager = new EventsManager();
    $eventsManager->attach('dispatch:beforeExecuteRoute', function($event, $dispatcher) use ($di) {
        $logger = new \Phalcon\Logger\Adapter\File(APP_PATH . "/logs/beforeExecuteRoute.log");
        $logger->log("This is a message");
        $logger->error("This is an error");
        $logger->close();
    });

    $dispatcher = new Dispatcher();
    $dispatcher->setEventsManager($eventsManager);
    $dispatcher->setDefaultNamespace('Vcloud\Modules\Profile\Controllers');
    return $dispatcher;
});

How can I get the messages from the dispatcher to log them into the file?

With Exceptions, for instance, we have $message = $exception->getMessage(); to get the Exception Message

But what is the way to get All the Messages from the dispatcher

I'm trying this, but get some errors (Error: Call to a member function getMessage() on null)

    $eventsManager->attach('dispatch:beforeExecuteRoute', function($event, $dispatcher, $message) use ($di) {
        $msg  = $message->getMessage();
        $logger = new \Phalcon\Logger\Adapter\File(APP_PATH . "/logs/beforeExecuteRoute.log");
        $logger->log($msg);
        $logger->close();
    });

I'm very lost :(

There is nothing like message passed. What messages ? Dispatcher doesn't populate any message itself :) I just have message like this:

 PHP_EOL.
                'URL: '.$this->di->get('request')->getURI().PHP_EOL.
                'Module name:'.$dispatcher->getModuleName().PHP_EOL.
                'Controller name:'.$dispatcher->getControllerName().PHP_EOL.
                'Action name:'.$dispatcher->getActionName().PHP_EOL.
                'Params:'.json_encode($dispatcher->getParams()).PHP_EOL.
                'POST:'.json_encode($_POST).PHP_EOL.
                'JSON:'.json_encode($this->di->get('request')->getJsonRawBody(true)).PHP_EOL.
                'User id:'.$userId.PHP_EOL.
                'IP:'.$this->di->get('request')->getClientAddress().PHP_EOL.
                '=============================================================',


3.9k

Yeah! the forwarding is happening

[Fri, 07 Oct 16 14:49:53 +0200][DEBUG] 
URL: /auth/session/signup
Module name:auth
Controller name:session
Action name:signup
======================================
[Fri, 07 Oct 16 14:49:54 +0200][DEBUG] 
URL: /auth/session/signup
Module name:auth
Controller name:index
Action name:index

So, what could be the blank page error?

If I change the getRender() to a string the problem dissapears

I'm learning a lot with you :)



145.0k
Accepted
answer

Hmmm, im using mail rendering like this:

        $view->start();
        $view->render('mail', 'forgot');
        $view->finish();
        $html = $view->getContent();

but never checked if there is gonna be some problem with forward. Maybe try to do reset somewhere ? How exactly this action looks like ? Did you checked php logs ? You have any errors or something ? Also the page is blank, what is in source ? Maybe it stops rendering because of some error.



3.9k

Thanks!

I'm almost there:


    public function getTemplate($name)
    {
        $view = $this->view;

        $view->start();
        $view->render('emailTemplates', $name);
        $view->finish();

        return $view->getContent();
    }

But I'ts applying also the template I have defined in SessionController.php. And, inside this template ('public'), it's rendered the 'emailTemplates'

class SessionController extends ControllerBase
{
    public function initialize()
    {
        $this->view->setTemplateBefore('public');
    }

This is the Controller I use to signUp (www.domain.com/auth/session/signup). From where It's called the user->save() and then the confirmation email is sended.

This thing doesn't happens when using:

return $this->view->getRender('emailTemplates','forgot')

getRender() only renders the 'emailTemplates' controller. But render() applies also the template ('public') defined in the other controller avobe the 'emailTemplates' template.

It's all very strange to me, I can't understand what's happening

Did you checked logs ? Maybe try to reset view in action where you forwarind ? I don't really know what's happening here. Did you checked what i wrote what exactly you have in source ? Maybe rendering is stopped because of some exception.



3.9k

Yes,with getRender() I checked that no errors are thrown by php, in php_error.log and in the source code of the returned page (wich is completly blank)

Anyway, Yeah!!!! with $view->reset() now the problem I described before is gone, above template no longer renders. THANKS!

But now I can't send some parameters to the view. Just like this:


    $name = "confirmation";

    public function getViewTemplate($name, $params)
    {
        $parameters = array_merge([
            'publicUrl' => $this->config->application->publicUrl
        ], $params);

        $view = $this->view;
        $view->reset();  // YEAH!!!

        $view->start();
        $view->render('emailTemplates', $name, $parameters);
        $view->finish();

        return $view->getContent();
    }

I get some errors:

Notice: Undefined variable: publicUrl in /xxxxx/app/modules/auth/views/layouts/emailTemplates.volt.php on line 28
Notice: Undefined variable: publicUrl in /xxxxx/app/modules/auth/views/emailTemplates/confirmation.volt.php on line 14
Notice: Undefined variable: confirmUrl in /xxxxx/app/modules/auth/views/emailTemplates/confirmation.volt.php on line 14

I have tried setting the array directly also:

$parameters = ['publicUrl' => "https://www.domain.com", 'confirmUrl' => "/auth/confirm/"];

Getting the same errors. render is not acepting these parameters

I have tested the template, passing some parameters in the controller (www.domain.com/auth/emailtemplates/confirmation) and works fine

Why is not getting these parameters?

Render method doesn't accept parameters i think, try setVars.



3.9k

Well, in the documentacion says that it acccepts parameters:

Class Phalcon\Mvc\View
public render (string $controllerName, string $actionName, [array $params])
// Executes render process from dispatching data

But it seems it doesn't. With setVars() works fine ;)

Anyway, Many Many Thanks Wojciech for your invaluable support.

You have helped me so much with your answers, also I've learn a lot! Thanks



207

I have same problems, anyone have a solution for that? thanks