Writing session variable using method in UsersTable, called by UsersController

I’m converting an ancient Cake 2.0 project into Cake 4, and I’m having a challenge writing some session variables. I want to pull my logged in user’s group memberships from a table and store them in a session variable. In my 2.0 app, this was as simple as calling $this->Session->write('groups', $this->User->getGroups(AuthComponent::user('id'))); from my UsersController after a successful login.

Here’s how I’m trying to do it in Cake 4. First, my method call from UsersTable::login:

// Session request
$session = $this->getRequest()->getSession();

// Login successful, grab the user's attributes
$user = $this->request->getAttribute('identity');
// Get the user's group associations and write them to a session var for 
// use throughout the site
$session->write('groups', $this->Users->getGroups($user->id)); 

Now my UsersTable::getGroups method:

public function getGroups($user_id, $keys = true) 
    $groups = [];
    foreach ($this->UserGroups->find('all', ['conditions' => compact('user_id')]) as $data) {
        $groups[$data['Groups']['id']] = $data['Groups']['key'];
    return ($keys) ? $groups : array_keys($groups);

Debugs show groups is null after all this. My app doesn’t break, but getGroups() doesn’t seem to be yielding anything. getGroups() is an adaptation of my old 2.0 code, so I’m guessing I’ve missed something 4.0-specific there.

I think that if you debug($data) inside your foreach loop, you’ll find that it doesn’t look like what you think it does.

No, it doesn’t, for sure.

I’ve got the foreach started correctly:

public function getGroups($user_id, $keys = true)
    foreach ($this->UserGroups->find('all')->where (['user_id' => $user_id]) as $data) {
        // Here's my question mark
    return key => value pair of group id and group key

When I debug $data immediately, I see the group_id field from my user_groups table (which is a reference table) and the 13 id's of group_id I expect to see for my account’s group memberships. What I’m having trouble with now is retrieving the key field from the groups table so that the user’s group memberships can be assigned to a session variable. The key field is just the group name, abbreviated, like so:

id                name                key
--                ----                ---
1           Site Administrator     site_admin
2           NC Group Leader        nc_group_lead
3           NC User                nc_user

So what I need to yield is the following key => value pairing for my $groups session variable (assuming the user in question has these three group memberships):

1 => site_admin
2 => nc_group_lead
3 = > nc_user

Again, this is from an old Cake 2.x app, and it works there; I’m just a bit challenged on how to get the same result in Cake 4.

If the associations are set up correctly, you should be able to just do
$user = $this->get($user_id, ['contain' => ['Groups']])
in your getGroups function, and then $user->groups will be the actual group records (as entities).

There should be an even easier way, in fact, where you can set up a custom finder for your identity, and that finder would just do something along the lines of return $q->contain(['Groups']);, and then your getAttribute('identity') calls will always include the group list. I don’t have that at hand, but some quick Googling should turn it up; I think it’s been asked and answered both here and on Stack Overflow.

1 Like

Tweaking your advice gave me what I need. The ‘contains’ piece is what was ultimately missing, along with the formatting lesson in my foreach:

$groups = [];
    foreach($this->UserGroups->find('all', ['contain' => ['Groups']])->where (['user_id' => $user_id]) as $data) {
        $groups[$data->group->id] = $data->group->key;
    return ($keys) ? $groups : array_keys($groups);

This yields exactly the key => value pairs I need. That said, I really like the idea of making this data part of the user’s identity attribute, so I’ll be exploring that further. Thanks for the nudge in the right direction!

How about:

$user = $this->get($user_id, ['contain' => ['Groups']]);
$groups = collection($user->groups)->combine('id', 'key')->toArray();

That is definitely food for thought. I think I need to play with my associations to get it to work, since groups is only indirectly related to users via user_groups. If I change contain to UserGroups, I get back the associations I need, but I don’t think that’s where you’re steering me. :wink:

At any rate, I’ve got a solution to my original problem and something new to workshop. Thanks again!