Variables set in AppController not handed out to ErrorController

I have a few settings set up in the AppController in the initialize function:

// src/Controller/AppController.php
public function initialize(): void
{
    parent::initialize();

    $settings = \Cake\Core\Configure::read('Settings');
    $nav = \Cake\Core\Configure::read('Navigation');
    $debug = \Cake\Core\Configure::read('debug');
    $this->set(compact('settings', 'nav', 'debug'));
}

Those variables are accessible from all other controllers/views. My problem is, when an error page is displayed (ErrorController.php & templates/layout/error.php), those three variables are not handed over and my layout is not displaying anything (because I need the $settings and $nav variables).

In my ErrorController.php I set a standard initialize:

// src/Controller/ErrorController.php
public function initialize(): void
{
    parent::initialize();
    $this->viewBuilder()->setTemplatePath('Error');
}

When I add the same variables to the ErrorController/initialize, the variables are not handed over, or only, when I remove the parent::initialize();.
What am I missing?

Does ErrorController actually extend AppController?

Yes it does.
Here is the full controller:

namespace App\Controller;

use Cake\Event\EventInterface;
use App\Controller\AppController;

/**
 * Error Handling Controller
 *
 * Controller used by ExceptionRenderer to render error responses.
 */
class ErrorController extends AppController
{
    public function initialize(): void
    {
        parent::initialize();
        $this->viewBuilder()->setTemplatePath('Error');
    }
}

I can’t replicate your problem in v4. What version are you using?

Personally, to debug this I would start by checking the existence of the vars are a couple of points and see where the failure is happening…

// src/Controller/AppController.php
public function initialize(): void
{
    parent::initialize();

    $settings = \Cake\Core\Configure::read('Settings');
    $nav = \Cake\Core\Configure::read('Navigation');
    $debug = \Cake\Core\Configure::read('debug');
    $this->set(compact('settings', 'nav', 'debug'));

    debug($this->viewBuilder()->getVars());
}

then

// src/Controller/ErrorController.php
public function initialize(): void
{
    parent::initialize();

    debug($this->viewBuilder()->getVars());

    $this->viewBuilder()->setTemplatePath('Error');

    debug($this->viewBuilder()->getVars());
}

This would tell

  • if AppController was getting called and properly setting the values
  • if the values were surviving back in the controller
  • if the template path call was destroying them somehow.

Okay, found the issue.

I didn’t post following part of my initialize function:

if($this->Authentication->getResult()->isValid()) {
    $authUser = $this->Authentication->getIdentity()->getOriginalData();

    // fetch notifications & messages
    $notificationsBar = $this->Notification->fetch($authUser->id);
    $messagesBar = $this->Message->fetch($authUser->id);

    $this->set(compact('authUser', 'notificationsBar', 'messagesBar'));
}

The if($this->Authentication->getResult()->isValid()) fails in AppController when ErrorController is called. But how to fix this?

Why isn’t $this->Authentication not valid (and getting no logs), when ErrorController is called?

Edit

In AppController:initialize() doing following:

debug($this->viewBuilder()->getVars()); // returns output
debug($this->Authentication); // no output

Weird…

is AppController extending Controller? Probably, but still have to ask

Just so there is no misunterstanding…

Here is my full AppController.php. I just removed the /***/ doc comments.

<?php
declare(strict_types=1);
namespace App\Controller;

use Cake\Controller\Controller;
use Cake\Event\Event;
use Cake\Datasource\ConnectionManager;
use Cake\Core\Configure;

class AppController extends Controller
{
    public function initialize(): void
    {
        parent::initialize();

        $this->loadComponent('RequestHandler');
        $this->loadComponent('Flash');
        $this->loadComponent('Toast');
        $this->loadComponent('Notification');
        $this->loadComponent('Message');
        $this->loadComponent('Authentication.Authentication', [
            'logoutRedirect' => '/users/login'
        ]);
        // $this->viewBuilder()->setHelpers(['AssetCompress.AssetCompress']);

        // load settings, navigation and debug state
        $settings = \Cake\Core\Configure::read('Settings');
        $nav = \Cake\Core\Configure::read('Navigation');
        $debug = \Cake\Core\Configure::read('debug');
        $this->set(compact('settings', 'nav', 'debug'));

        // debug($this->viewBuilder()->getVars());
        debug($this->Authentication);

        // authenticated user
        if($this->Authentication->getResult()->isValid()) {
            $authUser = $this->Authentication->getIdentity()->getOriginalData();

            // fetch notifications & messages
            $notificationsBar = $this->Notification->fetch($authUser->id);
            $messagesBar = $this->Message->fetch($authUser->id);

            $this->set(compact('authUser', 'notificationsBar', 'messagesBar'));
        }
    }

    /**
     * beforeFilter method
     * @param  \Cake\Event\EventInterface $event
     * @return \Cake\Http\Response|null
     */
    public function beforeFilter(\Cake\Event\EventInterface $event)
    {
        parent::beforeFilter($event);
        $this->Authentication->addUnauthenticatedActions(['login', 'register', 'logout', 'activate', 'forgot']);
    }
}

And here the full ErrorController.php

<?php
declare(strict_types=1);
namespace App\Controller;

use Cake\Event\EventInterface;
use App\Controller\AppController;

class ErrorController extends AppController
{
    public function initialize(): void
    {
        parent::initialize();
        $this->viewBuilder()->setTemplatePath('Error');
    }

    public function beforeRender(\Cake\Event\EventInterface $event)
    {
        parent::beforeRender($event);
    }

    public function beforeFilter(\Cake\Event\EventInterface $event)
    {
        parent::beforeFilter($event);
    }

    public function afterFilter(\Cake\Event\EventInterface $event)
    {
        parent::beforeFilter($event);
    }
}

It must be that the user is not being authenticated in that case.

You can see some of what is happening with authentication if, just before your conditional statement in AppController you take a peak at the data in the Authentication object:

 debug($this->Authentication->getResult()->getData());
if($this->Authentication->getResult()->isValid()) {
   // your code
}

Probably, for your successful pages you will see an entity for your logged in user. And it seems like you are going to see NULL for your ErrorController.

If so, this will confirm that your user is not being authenticated in the ErrorController case.

I don’t know how you have authentication configured, but probably you have a login page, then you rely on the Session to carry the proof of login. Is there something leading up to the ErrorController that could be killing your session?