<p>When you save a value to the session using an existing key, Craft (or rather, Yii) will assume that the new value (an array, in your case) should replace any existing value, for that key. I.e. there's no built-in behaviour to automatically merge the new and existing values.</p>
<p>To work around that, you'll need to make sure that the new and existing values (i.e. arrays) are merged yourself.</p>
<p><em>Note that the <code>Craft::$app-&gt;session-&gt;get()</code> method takes a second parameter <code>$defaultValue</code>, which you can use to pass an empty array. This makes sure that in the event that there is no existing value for the <code>'usersession'</code> key, Craft will return that empty array, avoiding syntax errors that you would otherwise run into if attempting to <code>array_merge()</code> a <code>null</code> value.</em></p>
<p>Something like this should do it:</p>
<pre class="lang-php prettyprint-override"><code>$session = Craft::$app-&gt;session;

$session-&gt;set('userdata', array_merge($session-&gt;get('userdata'), array('agreement' =&gt; true)));
</code></pre>
<p>To make this a bit more robust and readable, you might consider creating a small helper function in your plugin or custom module, i.e. something like this:</p>
<pre class="lang-php prettyprint-override"><code>public function addToSessionUserdata(array $values): void
{
    $sessionKey = 'userdata';
    // Get the existing session value
    $existingValue = Craft::$app-&gt;session-&gt;get($sessionKey, []);
    // Create the new session value by merging in the new values
    $newValue = array_merge($existingValue, $values);
    // Save the new values to the session; overwriting the old value
    Craft::$app-&gt;session-&gt;set($sessionKey, $newValue);
}
</code></pre>
<p>Then, you can simply call that method when needing to add something to the <code>'userdata'</code> session object, without worrying about existing values being removed:</p>
<pre class="lang-php prettyprint-override"><code>$this-&gt;addToSessionUserdata(array('agreement' =&gt; true));
</code></pre>

CC BY-SA 4.0
2023-01-09T19:24:06.987Z
Thank you for your detailed answer - very helpful! If I may ask 3 subquestions: 1) Would the helper update a value if I call the function with an existing sub-key-value item? 2)In a module: where would I place the helper (it's my first CRAFT CMS project.. I have module/src/ and then controller/services subfolders. 3) Would you recommend to go that way or rather use prefixes in the main-key like the YII docs suggests here: https://www.yiiframework.com/doc/guide/2.0/en/runtime-sessions-cookies
Luckyfella
CC BY-SA 4.0
2023-01-09T19:34:42.020Z
For the helper file I guess I could just create any additional folder and put a helperClass there and load it like the services in the module entry file `$this->setComponents` - just wanted to know if there is some kind of best practice for doing that.
Luckyfella
CC BY-SA 4.0
2023-01-09T20:06:14.250Z
@Luckyfella _Would the helper update a value if I call the function with an existing sub-key-value item?_ No, if the values you store are multi-dimensional arrays you might want to use [`array_merge_recursive()`](https://www.php.net/manual/en/function.array-merge-recursive.php) instead of the simple `array_merge()`.
Mats Mikkel Rummelhoff
CC BY-SA 4.0
2023-01-09T20:11:10.497Z
@Luckyfella _In a module: where would I place the helper_ The easiest way would be to add a "helper" class; anywhere in your module but typically somewhere like `src/modules/mymodule/helpers/MyModuleHelper.php`. Declare the `addToSessionUserdata()` method as `public static`, and then you can use it anywhere in your module by importing the helper class and then doing `MyModuleHelper::addToSessionUserData(['agreement' => true])`. How to structure a custom module is a big topic though – if you have specific questions to that end you should probably create a new question (or join the Discord).
Mats Mikkel Rummelhoff
CC BY-SA 4.0
2023-01-09T20:13:36.277Z
@Luckyfella _Would you recommend to go that way or rather use prefixes in the main-key like the YII docs suggests_ The performance aspect is probably theoretical in most cases and likely not an issue unless you're storing massive amounts of data or traffic is very high. I'd say the differences between the key prefix and array merging workarounds will - in most cases - mostly boil down to cosmetics/a matter of preference.
Mats Mikkel Rummelhoff
CC BY-SA 4.0
2023-01-09T20:18:12.493Z
Again - thank you so much for clarifying that to me! If it was the Eurovision Song Contest - 12 points to our neighbors :) Appreciate you took the time!
Luckyfella
CC BY-SA 4.0
2023-01-09T16:52:23.233Z
2023-01-09T16:52:23.233Z
Mats Mikkel Rummelhoff
<p>In my Craft service class I need to set SESSION values. Within a method there can be multiple values to be set and depending on what a request to an API returns, there are different keys to save. My problem is that if I set the SESSION values one by one, only the last one set gets saved. I'm trying to set values like this - my goal is to group multiple values within the <code>userdata</code> key:</p>
<pre class="lang-php prettyprint-override"><code>$session = Craft::$app-&gt;session;
    
$session-&gt;set('userdata', array('agreement' =&gt; true));
$session-&gt;set('userdata', array('access_token' =&gt; $access_token));
</code></pre>
<pre class="lang-php prettyprint-override"><code># trying to get the values in another method
$user = $session-&gt;get('userdata');
echo $user['agreement'];  // does not exists anymore
echo $user['access_token'];  // is saved
</code></pre>
<p>If I pass everything at once like this:</p>
<pre class="lang-php prettyprint-override"><code>$session = Craft::$app-&gt;session;

$data = array(
    'agreement' =&gt; true,
    'access_token' =&gt; true,
);

$session-&gt;set('userdata', $data);
</code></pre>
<p>everything is saved correctly but I have several variations and I don't want to set all possible SESSION vars grouped every time I need a new value inside that SESSION index. Is it just like SESSIONS work or is there a better way to solve that?</p>
<p>Should I save the values on-dimensional and prefix them like this?</p>
<pre class="lang-php prettyprint-override"><code>$session = Craft::$app-&gt;session;
    
$session-&gt;set('userdata.agreement', true);
$session-&gt;set('userdata.access_token', $access_token);
</code></pre>
<hr />
<p><strong>Edit</strong> after getting massive help from @Mats Mikkel Rummelhoff</p>
<p>Just in case someone is interested to group session values by a prefix like me, I post my (basic) helper class. Added a <code>remove</code> and <code>get</code> method as well. Method names and prefix have to be edited of course - like namespace and ... .. Does not so much but it's maybe a good starting point if you want to do something similar.</p>
<pre class="lang-php prettyprint-override"><code>&lt;?php
/**
 * Helper class for Craft CMS 4.x
 *
 * Handels session values grouped by a session-index
 * Helper methods suppost to be static / independent of
 * class-properties, so you have to set the desired
 * session-key where you want to group the items under
 * hardcoded inside the methods
 *
 */

namespace modules\membermodule\src\helpers; // adjust to your needs

use Craft;

class SessionHelper
{
    /**
     * Adds the passed array to the session grouped by the session-key 'sessionprefix'
     *
     * Passed array has to be an associative and one-dimensional array
     * i.e.: array('keyName' =&gt; 'itemValue')
     *
     * @param Array $values
     *
     * @return Void
     */
    public static function addToSessionUserdata(array $values): void
    {
        $sessionKey = 'sessionprefix';  // adjust to your needs

        // Get the existing session value
        $existingItems = Craft::$app-&gt;session-&gt;get($sessionKey, []);

        // Create the new session value by merging in the new values
        $newValue = array_merge($existingItems, $values);

        Craft::$app-&gt;session-&gt;set($sessionKey, $newValue);
    }

    /**
     * Removes an array-item from the session grouped by the session-key 'sessionprefix'
     *
     * Passed string has to be an array-key of an existing array of the grouped session item.
     * i.e.: to remove `array('keyName' =&gt; 'itemValue')` pass: `'keyName'`
     *
     * @param String $key
     *
     * @return Void
     */
    public static function removeFromSessionUserdata(string $key): void
    {
        $sessionKey = 'sessionprefix';  // adjust to your needs

        // Get the existing session values
        $existingItems = Craft::$app-&gt;session-&gt;get($sessionKey, []);

        if( array_key_exists($key, $existingItems) ) {
            // Delete the passed item from array
            unset($existingItems[$key]);
        }

        Craft::$app-&gt;session-&gt;set($sessionKey, $existingItems);
    }

    /**
     * Returns the value from the session grouped by the session-key 'sessionprefix'
     * having the array-key of the passed param
     *
     * Passed param has to be a previously added array-key
     *
     * @param String $key
     *
     * @return String|Null
     */
    public static function getFromSessionUserdata(string $key)
    {
        $sessionKey = 'sessionprefix';  // adjust to your needs
        $response = null;

        // Get the existing session values
        $sessionItems = Craft::$app-&gt;session-&gt;get($sessionKey, []);

        if( array_key_exists($key, $sessionItems) ) {
            $response = $sessionItems[$key];
        }

        return $response;
    }
}

</code></pre>
<pre class="lang-php prettyprint-override"><code># Usage

// Add item example:
SessionHelper::addToSessionUserData(['arrayKey' =&gt; 'arrayValue']);

// Remove item example:
SessionHelper::removeFromSessionUserData('arrayKey');

// Get item:
SessionHelper::getFromSessionUserData('arrayKey');
</code></pre>

<session>
How to group SESSION values?
# How to group SESSION values?

(Asked by: username_0 on 2023-01-09)

In my Craft service class I need to set SESSION values. Within a method there can be multiple values to be set and depending on what a request to an API returns, there are different keys to save. My problem is that if I set the SESSION values one by one, only the last one set gets saved. I'm trying to set values like this - my goal is to group multiple values within the userdata key:
$session = Craft::$app->session;
    
$session->set('userdata', array('agreement' => true));
$session->set('userdata', array('access_token' => $access_token));

# trying to get the values in another method
$user = $session->get('userdata');
echo $user['agreement'];  // does not exists anymore
echo $user['access_token'];  // is saved

If I pass everything at once like this:
$session = Craft::$app->session;

$data = array(
    'agreement' => true,
    'access_token' => true,
);

$session->set('userdata', $data);

everything is saved correctly but I have several variations and I don't want to set all possible SESSION vars grouped every time I need a new value inside that SESSION index. Is it just like SESSIONS work or is there a better way to solve that?
Should I save the values on-dimensional and prefix them like this?
$session = Craft::$app->session;
    
$session->set('userdata.agreement', true);
$session->set('userdata.access_token', $access_token);


Edit after getting massive help from @username_1
Just in case someone is interested to group session values by a prefix like me, I post my (basic) helper class. Added a remove and get method as well. Method names and prefix have to be edited of course - like namespace and ... .. Does not so much but it's maybe a good starting point if you want to do something similar.
 'itemValue')
     *
     * @param Array $values
     *
     * @return Void
     */
    public static function addToSessionUserdata(array $values): void
    {
        $sessionKey = 'sessionprefix';  // adjust to your needs

        // Get the existing session value
        $existingItems = Craft::$app->session->get($sessionKey, []);

        // Create the new session value by merging in the new values
        $newValue = array_merge($existingItems, $values);

        Craft::$app->session->set($sessionKey, $newValue);
    }

    /**
     * Removes an array-item from the session grouped by the session-key 'sessionprefix'
     *
     * Passed string has to be an array-key of an existing array of the grouped session item.
     * i.e.: to remove `array('keyName' => 'itemValue')` pass: `'keyName'`
     *
     * @param String $key
     *
     * @return Void
     */
    public static function removeFromSessionUserdata(string $key): void
    {
        $sessionKey = 'sessionprefix';  // adjust to your needs

        // Get the existing session values
        $existingItems = Craft::$app->session->get($sessionKey, []);

        if( array_key_exists($key, $existingItems) ) {
            // Delete the passed item from array
            unset($existingItems[$key]);
        }

        Craft::$app->session->set($sessionKey, $existingItems);
    }

    /**
     * Returns the value from the session grouped by the session-key 'sessionprefix'
     * having the array-key of the passed param
     *
     * Passed param has to be a previously added array-key
     *
     * @param String $key
     *
     * @return String|Null
     */
    public static function getFromSessionUserdata(string $key)
    {
        $sessionKey = 'sessionprefix';  // adjust to your needs
        $response = null;

        // Get the existing session values
        $sessionItems = Craft::$app->session->get($sessionKey, []);

        if( array_key_exists($key, $sessionItems) ) {
            $response = $sessionItems[$key];
        }

        return $response;
    }
}


# Usage

// Add item example:
SessionHelper::addToSessionUserData(['arrayKey' => 'arrayValue']);

// Remove item example:
SessionHelper::removeFromSessionUserData('arrayKey');

// Get item:
SessionHelper::getFromSessionUserData('arrayKey');

**Tags:** session


---

## 1 answers

### Answer by username_1 on 2023-01-09. Score: 2 | Accepted answer

When you save a value to the session using an existing key, Craft (or rather, Yii) will assume that the new value (an array, in your case) should replace any existing value, for that key. I.e. there's no built-in behaviour to automatically merge the new and existing values.
To work around that, you'll need to make sure that the new and existing values (i.e. arrays) are merged yourself.
Note that the Craft::$app->session->get() method takes a second parameter $defaultValue, which you can use to pass an empty array. This makes sure that in the event that there is no existing value for the 'usersession' key, Craft will return that empty array, avoiding syntax errors that you would otherwise run into if attempting to array_merge() a null value.
Something like this should do it:
$session = Craft::$app->session;

$session->set('userdata', array_merge($session->get('userdata'), array('agreement' => true)));

To make this a bit more robust and readable, you might consider creating a small helper function in your plugin or custom module, i.e. something like this:
public function addToSessionUserdata(array $values): void
{
    $sessionKey = 'userdata';
    // Get the existing session value
    $existingValue = Craft::$app->session->get($sessionKey, []);
    // Create the new session value by merging in the new values
    $newValue = array_merge($existingValue, $values);
    // Save the new values to the session; overwriting the old value
    Craft::$app->session->set($sessionKey, $newValue);
}

Then, you can simply call that method when needing to add something to the 'userdata' session object, without worrying about existing values being removed:
$this->addToSessionUserdata(array('agreement' => true));

> **Comments:**
> **username_0**: Thank you for your detailed answer - very helpful! If I may ask 3 subquestions: 1) Would the helper update a value if I call the function with an existing sub-key-value item? 2)In a module: where would I place the helper (it's my first CRAFT CMS project.. I have module/src/ and then controller/services subfolders. 3) Would you recommend to go that way or rather use prefixes in the main-key like the YII docs suggests here: https://www.yiiframework.com/doc/guide/2.0/en/runtime-sessions-cookies  
> **username_0**: For the helper file I guess I could just create any additional folder and put a helperClass there and load it like the services in the module entry file `$this->setComponents` - just wanted to know if there is some kind of best practice for doing that.  
> **username_1**: @username_0 _Would the helper update a value if I call the function with an existing sub-key-value item?_ No, if the values you store are multi-dimensional arrays you might want to use [`array_merge_recursive()`](https://www.php.net/manual/en/function.array-merge-recursive.php) instead of the simple `array_merge()`.  
> **username_1**: @username_0 _In a module: where would I place the helper_ The easiest way would be to add a "helper" class; anywhere in your module but typically somewhere like `src/modules/mymodule/helpers/MyModuleHelper.php`. Declare the `addToSessionUserdata()` method as `public static`, and then you can use it anywhere in your module by importing the helper class and then doing `MyModuleHelper::addToSessionUserData(['agreement' => true])`. How to structure a custom module is a big topic though – if you have specific questions to that end you should probably create a new question (or join the Discord).  
> **username_1**: @username_0 _Would you recommend to go that way or rather use prefixes in the main-key like the YII docs suggests_ The performance aspect is probably theoretical in most cases and likely not an issue unless you're storing massive amounts of data or traffic is very high. I'd say the differences between the key prefix and array merging workarounds will - in most cases - mostly boil down to cosmetics/a matter of preference.  
> **username_0**: Again - thank you so much for clarifying that to me! If it was the Eurovision Song Contest - 12 points to our neighbors :) Appreciate you took the time!  

---

