Displaying logged-in user in header

After completing the authentication tutorial I want to see whether and who is logged in.
Only the following from the authentication tutorial gave me a clue where the current user is stored:

$this->request->getAttribute('identity')->getIdentifier();

However, this is within the ArticleController object.

How to access the identifier globally and how to echo this into the header continuously?
From this follows the desire to have a log-out link referring to the logout() function in the UserController. How can this be accessed from “the general header” (which includes the home logo, API and Documentation)?

I quite doubt whether this basic question is not asked before, but I couldn’t find it.

You can use Router::getRequest()->getAttribute('identity') anywhere in the system. Or, maybe better, have a universal beforeRender function in your base controller class which uses $this->request->getAttribute('identity') to set what you need as a view variable.

Thanks . I assume ‘base controller class’ refers to AppController.php? And does that beforeRender needs the same setup as beforeFilter?
public function beforeRender (\Cake\Event\EventInterface $event)
{
parent::beforeRender ($event);
$this->request->getAttribute(‘identity’)
}
Echo’ing the identity doesn’t work since it is not convertible to a string. I don’t understand how to setup the view of the common header. The example of set()->view is in e.g. ArticlesController.php where functions/controllers refer to a specific URL view.

From debugging I learned that the to-be-printed email is stored like this:
object(App\Controller\ArticlesController){
‘identity’ => object(Authorization\Identity) {
[protected] attributes =>
[protected] identity => object(Authentication\Identity) {

				'config' => [
					'fieldMap' => [
						'id' => 'id'
					]
				],    ['data' => object(App\Model\Entity\User) {

						'id' => (int) 3,
						'email' => 'x@y.nl', ...

So I have to access at a deeper level than identity? Which is not echo($this->request->getAttribute('identity')->'data'->'email'); :sweat_smile:

Correct.

The various request life cycle callbacks are all well documented, no need to guess about this or just hope for the best.

You don’t want to set the view with this, you want to set a variable for the view. For example,

$this->set('identity', $this->request->getAttribute('identity'));

The variable $identity will now be available in any view that gets rendered by this request, including the specific view for the action being run, any elements it might call, and, most importantly for the purpose at hand, your layout.

Documentation of the identity object states that “The identity object provides ArrayAccess but as well a get() method to access data. It is strongly recommended to use the get() method over array access”, so getting the email address in a view should be as simple as $identity->get('email'). Do remember that getAttribute('identity') may return null if the user is not logged in, so be sure to check for that scenario, e.g. along these lines:

echo $identity ? $identity->get('email') : 'Not logged in';
1 Like

That is a clear explanation, thank you very much. I don’t understand the need for the range of event handlers, but the basic understanding is more important for now. However, the set variable is unknown in the view ( [ Notice (8)](javascript:void(0);): Undefined variable: identity [ APP/View\AppView.php)

AppController.php
public function beforeRender(\Cake\Event\EventInterface $event){
parent::beforeRender($event);
$this->set(‘identity’, $this->request->getAttribute(‘identity’));
}

AppView.php
public function initialize(): void
{
echo $identity ? $identity->get(‘email’) : ‘Not logged in’;
#echo(‘a la playa’);
}

Is this due to the difference between Event $event and the way I used it ? (which was necessary to comply with a Controller file, probably by using the initial bake all command)

Have you confirmed that the beforeRender function is actually being called? There are various scenarios which might be preventing that, and troubleshooting those is very different from troubleshooting why the set doesn’t appear to work.

I thought the beforeRender to be automatically triggered. I called it explicitly in the initialize of AppController by $this->render(); which is obviously not the right way, based on the occurring errors.

Yes, the beforeRender event should be automatically triggered and sent to your controller at the appropriate time in the request lifecycle, which is not during initialization. You should only need to create it, not call it.

The question is whether your function (which you’ve included above) is being called. There are, as I said, things that might stop that from happening. I don’t want to dive into theoretical possibilities related to that if it’s not the actual problem, though, as it only muddies the waters. So, first check whether your function is being called, yes or no, and we proceed from there.

Based on an executed echo inside beforeRender(), it does.

Oh, I see the problem! You’re trying to reference the $identity variable in a function inside AppView. That’s not so much “a view” as “the class the builds the view”. It works like any other class and function, so you’re trying to reference a variable that wasn’t declared, with the expected result. Where $identity will be available to you is in places like src/Template/Users/view.ctp or, since you want it everywhere, src/Template/Layout/default.ctp. Those files are executed by the View class, in a context that’s been specially set up with all the variables that you’ve set.

1 Like

The default.php template is indeed the file I was looking for, containing the lay-out of the ‘Documentation’ and ‘API’. It’s hard to see the relation how the $this->set() in a Controller.php is enabling us through the View.php to have it available in the templates. The template comment gives a clue: * @var \App\View\AppView $this

Anyway, I got it working, thanks again!

To conclude for anyone that wants to do the same:
AppController.php :
public function beforeRender(\Cake\Event\EventInterface $event){
parent::beforeRender($event);
$this->set(‘identity’, $this->request->getAttribute(‘identity’));
}

templates/layout/default.php :
in top PHP code section :
$logged_label = $identity ? $identity->get('email') : 'Not logged in';
and in div:
<div class="top-nav-links"> <?= $logged_label ?> </div>

(Direct PHP in the div does not seem to work:
<div> <?= echo $identity ? $identity->get('email') : 'Not logged in'; ?> </div> )

Not sure what’s not clear about controllers setting view variables. It’s documented here. The documentation is short, because it’s a pretty simple concept, I think.

Direct PHP should work fine. The problem is that <?= and echo are redundant. Either <?php echo $identity... or <?= $identity...

1 Like

To make it more complete, I would like to let $logged_lable refer to either the users view page or the login page.

I tried:

$logged_ref = $identity ? $this->[‘action’ => ‘view’,$user->($identity->get(‘id’)]) : $user->login() ;
<a target="_blank" href=<?= $logged_ref ?>> <?= $logged_label ?> </a>

since in UserController.php the view controller/function takes input ($id =null), I thought the following would make even less sense:
$logged_ref = $identity ? $this->['action' => 'view',$user->($identity->get('id')]) : $user->login() ;

The error remains nevertheless Record not found in table "users" with primary key [NULL]

I don’t know if the HTML helper is a better solution, but since the Documentation and API are also designed by <a> I thought of keeping that consistent for layout.

$user->login() will call the login function, not give you the login URL.

Legit point. That made me solve it through “pure reference”, although I expected to make use of cakePHP automation methodology.

$logged_label = $identity ? $identity->get('email') : 'Not logged in';
$logged_ref=$identity ? "/users/view/".$identity->get('id') : "/users/login";

<a target="_blank" href=<?= $logged_ref ?>> <?= $logged_label ?> </a>

Your actual code is now looking too complex to be well-served by the ternary operator. How about this:

if ($identity) {
    echo $this->Html->link($identity->get('email'),
        ['controller' => 'Users', 'action' => 'view', $identity->get('id')],
        ['target' => '_blank']
    );
} else {
    echo $this->Html->link('Not logged in',
        ['controller' => 'Users', 'action' => 'login'],
        ['target' => '_blank']
    );
}
1 Like

I have no knowledge about ternary operator performance, but your solution was indeed what I meant by using CakePHP methodology. I stored this code in a variable such that the code doesn’t need to be inside the html.

However, I don’t understand why this works and does not result in the same error of Record not found in table "users" with primary key [NULL] since I thought this was due to the view($id=null) in UserController.php.

So I see from this that the current user and the Users functions can be accessed through controller (whereas $user is unknown). I made a new topic since I can’t apply similar access in the index View of Articles.

It’s not about the performance of the ternary operator, it’s about readability and maintainability of the code. This if is quite possible to write with the ternary operator, and the performance would likely be identical, but this version is much easier to understand at a glance.

You never said what specific line of code was causing the primary key error, so I can’t really comment on where that was coming from.

I mean this post above, mentioning the Record not found in table "users" with primary key [NULL] caused by intial try: