首頁 >後端開發 >php教程 >深入研究拉拉維爾的會議

深入研究拉拉維爾的會議

Karen Carpenter
Karen Carpenter原創
2025-03-06 02:32:13367瀏覽

A Deep Dive into Sessions in Laravel

在構建 Laravel 應用程序時,幾乎可以肯定您會在某些時候需要處理會話。它們是 Web 開發的基礎部分。

本文將快速介紹會話是什麼,它們如何在 Laravel 中工作,以及您如何在 Laravel 應用程序中使用它們。

然後,我們將更進一步,深入探討如何使用“會話類”與會話交互,以避免我在處理 Laravel 應用程序時經常遇到的常見陷阱。

最後,我們將了解如何在 Laravel 中測試會話數據。

什麼是會話?


默認情況下,Web 應用程序是無狀態的,這意味著請求通常彼此不了解。因此,我們需要一種方法來存儲請求之間的數 據。例如,當用戶登錄網站時,我們需要記住他們在訪問期間已登錄。這就是會話的用武之地。

簡而言之,會話是一種在多個請求之間持久保存數據安全的方式。

會話數據可能用於存儲以下內容:

  • 用戶身份驗證狀態。
  • 可在另一頁訪問的臨時數據。
  • 顯示給用戶的閃存消息。

會話數據可以存儲在各種位置,例如:

  • Cookie
  • 數據庫
  • 緩存存儲(例如 Redis)

會話在 Laravel 中如何工作?


要了解會話是什麼,讓我們看看它們如何在 Laravel 中工作。

以下是您可能在 Laravel 應用程序的會話中找到的一些示例數據:

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

讓我們分解每個鍵可能代表的內容。

以下鍵由 Laravel 框架本身添加:

  • _token 值用於防止 CSRF 攻擊。
  • _previous.url 值用於存儲先前請求的 URL。
  • _flash.old 值用於存儲先前請求中閃存會話數據的鍵。在本例中,它表示在先前請求中閃存了 success 值。
  • _flash.new 值用於存儲當前請求中閃存會話數據的鍵。

以下鍵由我添加:

  • success 值用於存儲可能顯示給用戶的成功消息。
  • current_team_id 值用於存儲用戶正在查看的當前團隊的 ID。

默認情況下,Laravel 支持以下會話驅動程序:

  • cookie - 會話數據存儲在安全且加密的 Cookie 中。
  • database - 會話存儲在您的數據庫中(例如 MySQL、PostgreSQL、SQLite)。
  • memcached / redis - 會話數據存儲在這些快速的緩存存儲中。
  • dynamodb - 會話數據存儲在 AWS DynamoDB 中。
  • file - 會話數據存儲在 storage/framework/sessions 中。
  • array - 會話數據存儲在內存中的 PHP 數組中,不會持久保存。

其中一些驅動程序有設置要求。因此,在使用它們之前,務必檢查 Laravel 文檔以了解如何設置它們。

在 Laravel 中使用會話


Laravel 使使用會話變得非常簡單。文檔很好地解釋瞭如何與會話交互。但是,讓我們快速回顧一下基礎知識。

對於我們的示例,我們將假設我們正在構建一個跨越多個頁面的分步嚮導。我們將存儲當前步驟以及每個步驟中輸入的數據到會話中。這樣,當用戶完成所有步驟時,我們可以在嚮導結束時讀取所有提交的數據。

為了使示例簡單,我們還將使用 session() 輔助函數。但稍後我們將討論使用 Session 門面或請求類訪問會話數據。

# 從會話讀取數據

要從會話讀取數據,您可以使用 get 方法,如下所示:

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

運行上述代碼將返回為 wizard:current_step 鍵存儲在會話中的值。如果該鍵在會話中沒有存儲的值,它將返回 null

此方法還允許您定義一個默認值,如果鍵不存在則返回:

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

運行上述代碼將返回為 wizard:current_step 鍵存儲在會話中的值。如果該鍵在會話中沒有存儲的值,它將返回 1

還可能有一些時候您想從會話讀取數據並同時將其刪除(因此無法再次訪問)。您可以為此使用 pull 函數:

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

運行上述代碼將返回為 wizard:current_step 鍵存儲在會話中的值,然後將其從會話中刪除。

# 將數據寫入會話

要將數據寫入會話,您可以使用 put 函數,如下所示:

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

運行上述代碼將把數組(在第二個參數中傳遞)存儲為 wizard:step_one:form_data 鍵的值。

# 將數據推送到會話中的數組

類似地,您還可以使用 push 方法將數據推送到會話中的數組:

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

假設 wizard:step_one:form_data:languages 鍵具有以下數據:

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

上述代碼(調用 push 方法)將更新會話值:

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

如果 wizard:step_one:form_data:languages 值尚不存在於會話中,則使用 push 將創建會話鍵並將值設置為包含您傳入的值的數組。

# 增加和減少會話中的數據

Laravel 還提供了一些方便的輔助方法,允許您增加和減少會話中的值:

您可以像這樣增加會話中的值:

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

當我們運行上面的代碼時,如果 wizard:current_step 會話值為 3,它現在將增加到 4。

您還可以像這樣減少會話中的值:

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

如果值尚不存在於會話中,則將它們視為 0。因此,對空會話值調用 increment 將值設置為 1。對空會話值調用 decrement 將值設置為 -1。

這兩種方法都允許您指定要增加或減少的數量:

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

# 從會話中刪除數據

您還可以使用 forget 方法從會話中刪除數據:

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

運行上述代碼將從會話中刪除屬於 wizard:current_step 鍵的數據。

如果您想一次刪除多個鍵,您可以將鍵數組傳遞給 forget 函數:

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

或者,如果您希望從會話中刪除所有數據,您可以使用 flush 函數:

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

# 檢查會話中是否存在數據

Laravel 還提供了一些方便的輔助函數來檢查會話中是否存在數據。

您可以使用 has 方法檢查會話中是否存在鍵以及其值是否不是 null

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

如果值存在且不是 null,則上述代碼將返回 true。如果值為 null 或鍵不存在,它將返回 false

類似地,您還可以使用 exists 方法檢查會話中是否存在鍵(無論值是否為 null):

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

您還可以檢查會話中是否根本不存在會話:

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

# 將數據閃存到會話

可能有時您想在會話中持久保存一些數據,但僅限於下一個請求。例如,您可能希望在用戶提交表單後向用戶顯示成功通知。

為此,您可以使用 flash 方法:

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

如果您要運行上述代碼,在下一次請求中,您可以從會話中讀取該值(使用類似 session()->get('success') 的內容)進行顯示。然後將其刪除,以便在下一次請求中不可用。

可能有時您有一些閃存數據(在先前的請求中添加),並且您想將其保留到下一個請求。

您可以使用 reflash 方法刷新所有閃存數據:

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

或者,如果您只想保留一些閃存數據,您可以使用 keep 方法:

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

運行上述代碼將保留 successerror 閃存會話值,但會刪除下一個請求的任何其他閃存數據。

輔助函數、門面或請求類?


到目前為止,我們只在示例中使用了 session() 輔助函數。

但您也可以使用 IlluminateSupportFacadesSession 門面或 IlluminateHttpRequest 類與會話交互。

無論您使用哪種方法,您仍然可以使用本文前面介紹的相同方法。這些方法只是與會話數據交互的不同方式。

要使用 Session 門面,您可以像這樣調用方法:

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

或者,您可以通過調用注入到控制器方法中的 IlluminateHttpRequest 實例上的 session 方法來訪問會話。假設您有如下控制器方法:

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

每種方法都是完全有效的,因此您可以決定您和您的團隊更喜歡哪一種。

更進一步


對於較小的項目,使用我們前面討論過的方法與會話交互是完全可以的。但是,隨著 Laravel 項目的增長,您可能會遇到一些問題,這些問題會導致錯誤並使您的代碼更難以維護。

因此,我們現在將介紹一些潛在的陷阱以及如何避免它們。

# 會話鍵中的錯字

我看到的一個常見陷阱(我自己也經歷過很多次)是會話鍵中的錯字。

堅持使用我們的嚮導示例,假設我們要將當前步驟存儲在會話中。因此,我們的代碼可能如下所示:

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

然後稍後,在代碼庫的不同部分,我們可能想從會話中讀取當前步驟:

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

您看到我剛才犯的錯誤了嗎?我意外地嘗試讀取 wizard:step 鍵而不是 wizard:current_step 鍵。

這是一個簡單的示例,但在大型代碼庫中,很容易犯這種錯誤。這些顯而易見的錯誤也可能是最難發現的。

因此,避免這些錯字的一種有用方法是使用常量或方法來生成會話鍵。

例如,如果會話鍵是靜態的,您可以定義一個常量(可能在稍後我們將介紹的會話類中),如下所示:

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

這意味著我們減少了在代碼庫中使用的原始字符串的數量,這有助於減少錯字的數量。

但是,有時您可能需要動態生成會話鍵。例如,假設我們希望我們的 wizard:current_step 鍵包含一個團隊 ID 字段。我們可以創建一個方法來生成密鑰,如下所示:

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

正如我們在上面的代碼中看到的,我們正在動態生成會話鍵,以便它可以在不同的方法中使用。例如,如果我們試圖查找 ID 為 1 的團隊的當前步驟,則密鑰將是 wizard:1:current_step

# 會話鍵衝突

我在處理存在一段時間的項目時看到的另一個陷阱是會話鍵衝突。

例如,想像一下,幾年前您構建了一個用於創建新用戶帳戶的嚮導。因此,您可能像這樣存儲會話數據:

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

現在,您已被分配構建一個也具有嚮導的新功能,並且您完全忘記了舊的嚮導和您使用的命名約定。您可能會意外地為新嚮導使用相同的鍵,導致數據衝突並引入潛在的錯誤。

為了避免這種情況,我喜歡用功能名稱作為會話鍵的前綴。因此,對於保存用於創建新用戶的嚮導數據,我可能有以下鍵:

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

然後在我的用於創建新團隊的新嚮導中,我可能有以下鍵:

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

我們將在本文後面介紹如何在會話類中添加這些前綴。

# 未知數據類型

你能告訴我此會話值中存儲的數據類型是什麼嗎?

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

如果您猜到它是一個 AppDataTransferObjectsWizardsFormData 實例,那麼您是對的。

說笑歸說笑,我想說明的是,當您從會話中讀取數據時,並不總是立即清楚您正在使用什麼類型的數據。您最終必須查看將數據寫入會話的代碼才能弄清楚它是什麼。這可能會分散注意力且耗時,並可能導致錯誤。

您可以向讀取會話數據的代碼添加註釋或文檔塊。但這只是一個提示。如果註釋沒有保持最新(如果會話數據類型發生更改),那麼它就沒有幫助,並且會增加錯誤的可能性。

我喜歡使用的另一種方法是在方法內部讀取會話數據並向該方法添加返回類型。這樣,您可以確保您正在使用的數據類型正確。它還有助於您的 IDE 和閱讀代碼的人員。

例如,讓我們來看這段代碼:

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

我們現在可以立即看到 stepOneFormData 方法返回一個 AppDataTransferObjectsWizardsFormData 實例。這清楚地說明了我們正在使用的數據類型。然後,我們可以在控制器中像這樣調用此方法:

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

# 在會話類中處理會話數據

正如我們在前面幾節中看到的,在 Laravel 中使用會話時,有一些很容易避免(但很常見)的陷阱。

通過使用“會話類”,可以避免(或至少減少)這些陷阱中的每一個。我喜歡使用會話類來在一個地方封裝與單個功能相關的會話數據處理邏輯。

例如,假設我們有一個用於創建用戶和另一個用於創建團隊的嚮導。我將為這些嚮導中的每一個創建一個會話類:

  • AppSessionsUsersNewUserWizardSession
  • AppSessionsTeamsNewTeamWizardSession

通過使用會話類,您可以:

  • 自動使用功能名稱作為所有鍵的前綴。
  • 向方法添加類型提示和返回類型。
  • 減少代碼庫中使用的原始字符串的數量。
  • 使重構會話數據結構更容易。
  • 使測試會話數據更容易。
  • 確切地知道如果需要對給定功能的會話數據進行任何更改,該去哪裡。

在處理會話數據時使用這種基於類的方法在處理大型 Laravel 項目時為我節省了無數次時間。這是一種簡單的方法,可以產生很大的影響。

在前面的示例中,我已經暗示過使用會話類。但是,讓我們更深入地了解我喜歡如何構建這些類。

假設我們有以下用於新用戶嚮導的會話類。乍一看它可能有點讓人不知所措,但讓我們看看代碼,然後分解它:

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

在上面的 AppSessionsUsersWizardSession 類中,我們首先定義了接受 IlluminateContractsSessionSession 實例的構造函數。通過這樣做,當我們從服務容器中解析 AppSessionsUsersWizardSession 類時,Laravel 將自動為我們注入會話實例。我將稍後向您展示如何在控制器中執行此操作。

然後,我們定義了 5 個基本的公共方法:

  • getCurrentStep - 返迴向導中的當前步驟。如果沒有設置步驟,則默認為 1
  • setCurrentStep - 設置嚮導中的當前步驟。
  • setFormDataForStep - 設置嚮導中給定步驟的表單數據。此方法採用步驟號和 AppDataTransferObjectsWizardsUsersFormData 實例。
  • getFormDataForStep - 獲取嚮導中給定步驟的表單數據。此方法採用步驟號並返回 AppDataTransferObjectsWizardsUsersFormData 實例或如果數據不存在則返回 null
  • flush - 從會話中刪除與嚮導相關的所有數據。如果已完成或取消嚮導,您可能希望調用此方法。

您可能已經註意到所有鍵都在方法內部生成。我喜歡這樣做是為了減少使用的原始字符串的數量(並減少錯字的機會)。這也意味著如果我們想添加另一個訪問特定鍵的方法,我們可以很容易地做到這一點。

使用這些鍵生成方法的額外好處是我們可以輕鬆地向鍵添加前綴以避免衝突。在此示例中,我們通過使用 sessionKey 方法將所有鍵的前綴設置為 new_user_wizard:

現在此類已設置,讓我們看看我們如何在控制器中與它交互:

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

正如我們看到的,在上面的示例中,我們將 AppSessionsUsersWizardSession 類註入到我們的控制器方法中。 Laravel 將自動為我們解析會話實例。

然後,我們可以像使用任何其他類一樣與它交互。

起初,這可能感覺像過度抽象和需要維護更多代碼。但是,隨著項目的增長,類型提示、返回類型、鍵生成方法,甚至方法的命名(使您的操作更具描述性)都非常有用。

在 Laravel 中測試會話


就像代碼庫的任何其他部分一樣,您應該確保您擁有會話數據的覆蓋範圍,以確保正在讀取和寫入正確的字段。

使用會話類的一大好處是您可以輕鬆地為類中的每個方法編寫集中的單元樣式測試。

例如,我們可以為 AppSessionsUsersWizardSession 類的 getFormDataForStep 方法編寫一些測試。作為提醒,以下是該方法:

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

我們可以在這裡測試幾個場景:

  • 為步驟返回 AppDataTransferObjectsWizardsUsersFormData 對象。
  • 如果步驟不存在表單數據,則返回 null

我們的測試類可能如下所示:

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

在上面的測試類中,我們有兩個測試涵蓋了我們前面提到的兩種情況。

這些單元樣式測試非常適合確保您的會話類配置正確以讀取和寫入會話數據。但它們並不一定能讓你相信它們在代碼庫的其餘部分被正確使用。例如,您可能正在調用 getFormDataForStep(1),而您應該調用 getFormDataForStep(2)

出於這個原因,您可能還想考慮在您的功能測試(您通常為控制器編寫的測試)中斷言會話數據。

例如,假設您的控制器中有以下基本方法,它會轉到嚮導中的下一步:

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

在上面的方法中,我們首先從會話中讀取當前步驟。然後,我們將當前步驟的表單數據存儲在會話中。最後,我們遞增當前步驟並重定向到嚮導中的下一步。

我們將假設我們的 AppHttpRequestsUsersWizardNextStepRequest 類負責驗證表單數據,並在我們調用 toDto 方法時返回 AppDataTransferObjectsWizardsUsersFormData 實例。

我們還將假設 nextStep 控制器方法可通過對 /users/wizard/next-step 路由(名為 users.wizard.next-step)的 POST 請求訪問。

我們可能想編寫如下測試以確保表單數據被正確存儲在會話中:

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

在上面的測試中,我們正在使用一些表單數據向 /users/wizard/next-step 路由發出 POST 請求。您可能會注意到我們正在使用 withSession。此方法允許我們設置會話數據,以便我們可以斷言它被正確讀取。

然後,我們斷言用戶被重定向到嚮導中的下一步,並且會話中的當前步驟設置為 3。我們還斷言步驟 2 的表單數據被正確存儲在會話中。

正如我們在測試中看到的,我們還通過兩種方式從會話中讀取:

  • 使用 assertSessionHas 方法檢查會話數據是否正確設置。
  • 使用 session() 輔助函數直接讀取會話數據。

這兩種方法都是有效的,因此您可以決定更喜歡哪一種。我在上面的測試中使用了這兩種方法來向您展示您有多種選擇。

結論


希望本文能幫助您很好地理解會話是什麼以及它們如何在 Laravel 中工作。我還希望它們能為您提供一些關於如何使用基於類的方法與會話數據交互以避免一些常見陷阱的想法。

以上是深入研究拉拉維爾的會議的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn