Home >Backend Development >PHP Tutorial >A Deep Dive into Sessions in Laravel

A Deep Dive into Sessions in Laravel

Karen Carpenter
Karen CarpenterOriginal
2025-03-06 02:32:13357browse

A Deep Dive into Sessions in Laravel

When building a Laravel application, you are almost certain that you will need to handle the session at some point. They are the fundamental part of web development.

This article will quickly explain what sessions are, how they work in Laravel, and how you use them in Laravel applications.

We will then go a step further and dive into how to interact with sessions using "session classes" to avoid the common pitfalls I often encounter when dealing with Laravel applications.

Finally, we will learn how to test session data in Laravel.

What is a conversation?


By default, web applications are stateless, meaning requests are usually not aware of each other. Therefore, we need a way to store data between requests. For example, when users log in to a website, we need to remember that they are logged in during their visit. This is where conversation comes in.

In short, a session is a safe way to persist data between multiple requests.

Session data may be used to store the following content:

  • User authentication status.
  • Temporary data accessible on another page.
  • Flash message displayed to the user.

Session data can be stored in various locations, such as:

  • Cookie
  • Database
  • Cache storage (e.g. Redis)

How does a session work in Laravel?


To understand what sessions are, let's see how they work in Laravel.

The following are some sample data you might find in a session in a Laravel application:

<code>[
  '_token' => 'bKmSfoegonZLeIe8B6TWvSm1dKwftKsvcT40xaaW'
  '_previous' => [
    'url' => 'https://my-app.com/users'
  ]
  '_flash' => [
    'old' => [
        'success',
    ],
    'new' => []
  ]
  'success' => 'User created successfully.'
  'current_team_id' => 123
]</code>

Let's break down what each key might represent.

The following keys are added by the Laravel framework itself:

    The
  • _token value is used to prevent CSRF attacks.
  • The
  • _previous.url value is used to store the URL requested previously.
  • _flash.old The value is used to store the keys of flash session data in a previous request. In this case, it means that the success value was flashed in a previous request.
  • _flash.new The value is used to store the keys of the flash session data in the current request.

The following keys are added by me:

  • success Value is used to store success messages that may be displayed to the user.
  • The
  • current_team_id value is used to store the ID of the current team the user is viewing.

By default, Laravel supports the following session drivers:

  • cookie - Session data is stored in secure and encrypted cookies.
  • database - Sessions are stored in your database (e.g. MySQL, PostgreSQL, SQLite).
  • memcached / redis - Session data is stored in these fast cache storage.
  • dynamodb - Session data is stored in AWS DynamoDB.
  • file - Session data is stored in storage/framework/sessions.
  • array - Session data is stored in PHP arrays in memory and is not persisted.

Some of these drivers have setup requirements. So, before using them, be sure to check the Laravel documentation to learn how to set them up.

Using sessions in Laravel


Laravel makes using sessions very simple. The documentation explains how to interact with a session well. But, let's take a quick look at the basics.

For our example, we will assume that we are building a step-by-step wizard spanning multiple pages. We will store the current step and the data entered in each step into the session. This way, when the user completes all the steps, we can read all submitted data at the end of the wizard.

To make the example simple, we will also use the session() helper function. But later we will discuss using the Session facade or request class to access session data.

# Read data from session

To read data from a session, you can use the get method as follows:

<code>[
  '_token' => 'bKmSfoegonZLeIe8B6TWvSm1dKwftKsvcT40xaaW'
  '_previous' => [
    'url' => 'https://my-app.com/users'
  ]
  '_flash' => [
    'old' => [
        'success',
    ],
    'new' => []
  ]
  'success' => 'User created successfully.'
  'current_team_id' => 123
]</code>

Running the above code will return the value stored in the session as the wizard:current_step key. If the key does not have a stored value in the session, it will return null.

This method also allows you to define a default value, which returns:

if the key does not exist:
<code>$currentStep = session()->get(key: 'wizard:current_step');</code>

Running the above code will return the value stored in the session as the wizard:current_step key. If the key does not have a stored value in the session, it will return 1.

There may also be times when you want to read data from the session and delete it at the same time (so it cannot be accessed again). You can use the pull function for this:

<code>$currentStep = session()->get(key: 'wizard:current_step', default: 1);</code>

Running the above code will return the value stored in the session as the wizard:current_step key and then delete it from the session.

# Write data to session

To write data to a session, you can use the put function as shown below:

<code>$currentStep = session()->pull(key: 'wizard:current_step');</code>

Running the above code will store the array (passed in the second parameter) as the value of the wizard:step_one:form_data key.

# Push data to an array in the session

Similarly, you can also use the push method to push data to an array in the session:

<code>session()->put(
    key: 'wizard:step_one:form_data',
    value: [
        'name' => 'Ash Allen',
        'email' => 'ash@example.com',
    ],
);</code>

Suppose the wizard:step_one:form_data:languages key has the following data:

<code>session()->push(
    key: 'wizard:step_one:form_data:languages',
    value: 'javascript',
);</code>

The above code (calling the push method) will update the session value:

<code>[
  '_token' => 'bKmSfoegonZLeIe8B6TWvSm1dKwftKsvcT40xaaW'
  '_previous' => [
    'url' => 'https://my-app.com/users'
  ]
  '_flash' => [
    'old' => [
        'success',
    ],
    'new' => []
  ]
  'success' => 'User created successfully.'
  'current_team_id' => 123
]</code>

If the wizard:step_one:form_data:languages value does not exist in the session yet, use push to create a session key and set the value to an array containing the values ​​you passed in.

# Increase and decrease data in session

Laravel also provides some handy helper methods that allow you to increase and decrease values ​​in a session:

You can increase the value in the session like this:

<code>$currentStep = session()->get(key: 'wizard:current_step');</code>

When we run the above code, if the wizard:current_step session value is 3, it will now increase to 4.

You can also reduce the value in the session like this:

<code>$currentStep = session()->get(key: 'wizard:current_step', default: 1);</code>

If the values ​​do not exist in the session yet, they are treated as 0. Therefore, calling increment on the empty session value sets the value to 1. Call decrement on empty session value Set the value to -1.

Both methods allow you to specify the number to be increased or decreased:

<code>$currentStep = session()->pull(key: 'wizard:current_step');</code>

# Delete data from session

You can also delete data from the session using the forget method:

<code>session()->put(
    key: 'wizard:step_one:form_data',
    value: [
        'name' => 'Ash Allen',
        'email' => 'ash@example.com',
    ],
);</code>

Running the above code will delete data belonging to the wizard:current_step key from the session.

If you want to delete multiple keys at once, you can pass the key array to the forget function:

<code>session()->push(
    key: 'wizard:step_one:form_data:languages',
    value: 'javascript',
);</code>

Or, if you want to delete all data from the session, you can use the flush function:

<code>[
    `php`,
]</code>

# Check whether there is data in the session

Laravel also provides some convenient helper functions to check whether data exists in the session.

You can use the has method to check whether there is a key in the session and whether its value is not null:

<code>[
    `php`,
    `javascript`,
]</code>

If the value exists and is not null, the above code will return true. If the value is null or the key does not exist, it will return false.

Similarly, you can also use the exists method to check whether the key exists in the session (regardless of the value being null):

<code>session()->increment(key: 'wizard:current_step');</code>

You can also check if the session does not exist at all:

<code>session()->decrement(key: 'wizard:current_step');</code>

# Flash data to session

Sometimes you want to persist some data in the session, but only for the next request. For example, you might want to show a success notification to the user after the user submits the form.

To do this, you can use flash method:

<code>session()->increment(key: 'wizard:current_step', amount: 2);
session()->decrement(key: 'wizard:current_step', amount: 2);</code>

If you want to run the above code, in the next request, you can read the value from the session (using something like session()->get('success')) for display. Then delete it so that it is unavailable in the next request.

It may be that sometimes you have some flash data (added in a previous request) and you want to keep it to the next request.

You can refresh all flash data using the reflash method:

<code>session()->forget(keys: 'wizard:current_step');</code>

Or, if you just want to keep some flash data, you can use keep Method:

<code>[
  '_token' => 'bKmSfoegonZLeIe8B6TWvSm1dKwftKsvcT40xaaW'
  '_previous' => [
    'url' => 'https://my-app.com/users'
  ]
  '_flash' => [
    'old' => [
        'success',
    ],
    'new' => []
  ]
  'success' => 'User created successfully.'
  'current_team_id' => 123
]</code>

Running the above code will retain the success and error flash session values, but will delete any other flash data for the next request.

Help function, facade or request class?


So far, we have only used the session() helper function in our examples.

But you can also use the IlluminateSupportFacadesSession facade or IlluminateHttpRequest class to interact with the session.

No matter which method you use, you can still use the same method described earlier in this article. These methods are just different ways to interact with session data.

To use the Session facade, you can call the method like this:

<code>$currentStep = session()->get(key: 'wizard:current_step');</code>

Alternatively, you can access the session by calling the IlluminateHttpRequest method injected into the controller method. Suppose you have the following controller method: session

<code>$currentStep = session()->get(key: 'wizard:current_step', default: 1);</code>
Each method is completely effective, so you can decide which one you and your team prefer.

Go a step further


For smaller projects, it is perfectly OK to interact with the session using the methods we discussed earlier. However, as the Laravel project grows, you may encounter some issues that can cause errors and make your code more difficult to maintain.

So, we will now cover some potential pitfalls and how to avoid them.

# Typo in session key

A common trap I see (I have experienced it many times myself) is a typo in the session key.

Stick with our wizard example, assuming we want to store the current step in the session. Therefore, our code might look like this:

<code>$currentStep = session()->pull(key: 'wizard:current_step');</code>
Then later, in different parts of the code base, we might want to read the current step from the session:

<code>session()->put(
    key: 'wizard:step_one:form_data',
    value: [
        'name' => 'Ash Allen',
        'email' => 'ash@example.com',
    ],
);</code>
Did you see the mistake I made just now? I accidentally tried reading the

key instead of the wizard:step key. wizard:current_step

This is a simple example, but in large code bases it is easy to make such mistakes. These obvious mistakes may also be the hardest to find.

Therefore, a useful way to avoid these typos is to use constants or methods to generate session keys.

For example, if the session key is static, you can define a constant (probably in the session class we will cover later) as follows:

<code>session()->push(
    key: 'wizard:step_one:form_data:languages',
    value: 'javascript',
);</code>
This means we reduce the number of raw strings used in the code base, which helps reduce the number of typos.

However, sometimes you may need to generate session keys dynamically. For example, suppose we want our

key to contain a Team ID field. We can create a method to generate the key as follows: wizard:current_step

<code>[
    `php`,
]</code>
As we can see in the above code, we are dynamically generating the session key so that it can be used in different methods. For example, if we try to find the current step of a team with ID 1, the key will be

. wizard:1:current_step

# Session key conflict

Another trap I see when dealing with a project that has been around for a while is session key conflict.

For example, imagine that a few years ago you built a wizard for creating new user accounts. So you might store session data like this:

<code>[
  '_token' => 'bKmSfoegonZLeIe8B6TWvSm1dKwftKsvcT40xaaW'
  '_previous' => [
    'url' => 'https://my-app.com/users'
  ]
  '_flash' => [
    'old' => [
        'success',
    ],
    'new' => []
  ]
  'success' => 'User created successfully.'
  'current_team_id' => 123
]</code>

You have now been assigned to build a new feature that also has a wizard, and you have completely forgotten the old wizard and the naming convention you used. You may accidentally use the same key for the new wizard, causing data conflicts and introducing potential errors.

To avoid this, I like to use the function name as the prefix for the session key. So for saving wizard data for creating new users, I might have the following keys:

  • new_user_wizard:current_step
  • new_user_wizard:step_one:form_data
  • new_user_wizard:step_two:form_data
  • Wait...

Then in my new wizard for creating a new team, I might have the following keys:

  • new_team_wizard:current_step
  • new_team_wizard:step_one:form_data
  • new_team_wizard:step_two:form_data
  • Wait...

We will explain how to add these prefixes to the session class later in this article.

# Unknown data type

Can you tell me what the data type is stored in this session value?

<code>$currentStep = session()->get(key: 'wizard:current_step');</code>

If you guessed it was an instance of AppDataTransferObjectsWizardsFormData, you are right.

Just talking and talking, I want to make it clear that when you read data from a session, it is not always immediately clear what type of data you are using. You end up having to look at the code that writes the data to the session to figure out what it is. This can be distracting and time consuming and can lead to errors.

You can add comments or document blocks to the code that reads session data. But this is just a hint. If the comment is not kept up to date (if the session data type changes), then it doesn't help and increases the likelihood of errors.

Another method I like to use is to read the session data inside the method and add the return type to the method. This way, you can make sure that the data type you are using is correct. It also helps your IDE and the people who read the code.

For example, let's look at this code:

<code>$currentStep = session()->get(key: 'wizard:current_step', default: 1);</code>

We can now see that the stepOneFormData method returns a AppDataTransferObjectsWizardsFormData instance. This clearly illustrates the type of data we are using. We can then call this method in the controller like this:

<code>$currentStep = session()->pull(key: 'wizard:current_step');</code>

# Process session data in session class

As we have seen in the previous sections, there are some easy (but common) pitfalls when using sessions in Laravel.

By using "session classes", each of these traps can be avoided (or at least reduced). I like to use session classes to encapsulate session data processing logic related to a single function in one place.

For example, suppose we have a wizard for creating users and another for creating teams. I'll create a session class for each of these wizards:

  • AppSessionsUsersNewUserWizardSession
  • AppSessionsTeamsNewTeamWizardSession

By using the session class, you can:

  • Use the function name automatically as the prefix for all keys.
  • Add a type prompt and a return type to the method.
  • Reduce the number of original strings used in the code base.
  • Make refactoring session data structures easier.
  • Make testing session data easier.
  • Know exactly where to go if any changes are needed to make to the session data for a given function.

Using this class-based approach when processing session data saves me countless times when working with large Laravel projects. This is a simple method that can have a big impact.

In the previous example, I have already suggested using a session class. But let's get a deeper look at how I like to build these classes.

Suppose we have the following session class for the new user wizard. At first glance it might be a bit overwhelming, but let's look at the code and break it down:

<code>[
  '_token' => 'bKmSfoegonZLeIe8B6TWvSm1dKwftKsvcT40xaaW'
  '_previous' => [
    'url' => 'https://my-app.com/users'
  ]
  '_flash' => [
    'old' => [
        'success',
    ],
    'new' => []
  ]
  'success' => 'User created successfully.'
  'current_team_id' => 123
]</code>

In the above AppSessionsUsersWizardSession class, we first define the constructor that accepts the IlluminateContractsSessionSession instance. By doing this, Laravel will automatically inject a session instance into us when we parse the AppSessionsUsersWizardSession class from the service container. I'll show you how to do this in the controller later.

We then define 5 basic public methods:

  • getCurrentStep - Return to the current step in the wizard. If there is no setup step, the default is 1.
  • setCurrentStep - Current steps in the Setup Wizard.
  • setFormDataForStep - Set the form data for the given step in the wizard. This method takes the step number and AppDataTransferObjectsWizardsUsersFormData example.
  • getFormDataForStep - Get the form data for the given step in the wizard. This method takes a step number and returns the AppDataTransferObjectsWizardsUsersFormData instance or if the data does not exist. null
  • - Delete all data related to the wizard from the session. If the wizard has been completed or canceled, you may want to call this method. flush
You may have noticed that all keys are generated inside the method. I like to do this to reduce the number of raw strings used (and reduce the chance of typos). This also means that if we want to add another way to access a specific key, we can do this very easily.

The additional benefit of using these key generation methods is that we can easily prefix the keys to avoid conflicts. In this example, we set the prefix of all keys to

by using the sessionKey method. new_user_wizard:

Now that this class is set, let's see how we interact with it in the controller:

<code>[
  '_token' => 'bKmSfoegonZLeIe8B6TWvSm1dKwftKsvcT40xaaW'
  '_previous' => [
    'url' => 'https://my-app.com/users'
  ]
  '_flash' => [
    'old' => [
        'success',
    ],
    'new' => []
  ]
  'success' => 'User created successfully.'
  'current_team_id' => 123
]</code>

As we see, in the example above, we inject the AppSessionsUsersWizardSession class into our controller method. Laravel will automatically parse the session instance for us.

Then we can interact with it like we would with any other class.

At first, this may feel like over-abstract and requires more code maintenance. However, as the project grows, type prompts, return types, key generation methods, and even naming methods (making your operations more descriptive) are very useful.

Test sessions in Laravel


Just like any other part of the code base, you should make sure you have coverage of session data to make sure the correct fields are being read and written.

One of the great benefits of using session classes is that you can easily write centralized unit-style tests for each method in the class.

For example, we can write some tests for the AppSessionsUsersWizardSession method of the getFormDataForStep class. As a reminder, here is the method:

<code>$currentStep = session()->get(key: 'wizard:current_step');</code>

We can test several scenarios here:

  • Returns the AppDataTransferObjectsWizardsUsersFormData object for the step.
  • If the step does not have form data, return null.

Our test class may look like this:

<code>$currentStep = session()->get(key: 'wizard:current_step', default: 1);</code>

In the above test class, we have two tests covering the two cases we mentioned earlier.

These unit style tests are ideal for ensuring that your session class is configured correctly to read and write session data. But they don't necessarily make you believe they are used correctly in the rest of the code base. For example, you might be calling getFormDataForStep(1), and you should call getFormDataForStep(2).

For this reason, you may also want to consider asserting session data in your functional tests (the one you usually write for your controller).

For example, suppose you have the following basic method in your controller and it will go to the next step in the wizard:

<code>$currentStep = session()->pull(key: 'wizard:current_step');</code>

In the above method, we first read the current step from the session. We then store the form data for the current step in the session. Finally, we increment the current step and redirect to the next step in the wizard.

We will assume that our AppHttpRequestsUsersWizardNextStepRequest class is responsible for validating the form data and returns the toDto instance when we call the AppDataTransferObjectsWizardsUsersFormData method.

We will also assume that the nextStep controller method is accessible via a POST request to the /users/wizard/next-step route (named users.wizard.next-step).

We may want to write the following test to ensure that the form data is correctly stored in the session:

<code>[
  '_token' => 'bKmSfoegonZLeIe8B6TWvSm1dKwftKsvcT40xaaW'
  '_previous' => [
    'url' => 'https://my-app.com/users'
  ]
  '_flash' => [
    'old' => [
        'success',
    ],
    'new' => []
  ]
  'success' => 'User created successfully.'
  'current_team_id' => 123
]</code>

In the above test, we are using some form data to issue a POST request to the /users/wizard/next-step route. You may notice that we are using withSession. This method allows us to set the session data so that we can assert that it is read correctly.

We then assert that the user is redirected to the next step in the wizard and that the current step in the session is set to 3. We also assert that the form data for step 2 is correctly stored in the session.

As we saw in our tests, we also read from the session in two ways:

  • Use the assertSessionHas method to check whether the session data is set correctly.
  • Use the session() helper function to read session data directly.

Both methods work, so you can decide which one you prefer. I used both methods in the above test to show you that you have multiple options.

Conclusion


Hope this article helps you understand well what sessions are and how they work in Laravel. I also hope they give you some ideas on how to interact with session data using class-based methods to avoid some common pitfalls.

The above is the detailed content of A Deep Dive into Sessions in Laravel. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn