Laravel's Context Remember Functions: Smart Closure-Based Caching

Laravel's Context API just got smarter with remember()
and rememberHidden()
functions that eliminate the need for manual has-get-set patterns. These new methods streamline context management with elegant closure-based caching.
From Verbose Conditionals to Clean Closures
Previously, managing context values required verbose conditional logic to check existence, retrieve values, or compute and store new ones:
// The old verbose approach
if (Context::has('user-permissions')) {
$permissions = Context::get('user-permissions');
} else {
$permissions = auth()->user()->permissions;
Context::set('user-permissions', $permissions);
}
The new remember()
function condenses this entire pattern into a single, expressive line:
// Clean and efficient
$permissions = Context::remember('user-permissions', fn() => auth()->user()->permissions);
This approach automatically handles the check-compute-store cycle, only executing the closure when the context value doesn't already exist.
Real-World Example
Consider a multi-tenant application where you frequently need to access tenant-specific configuration, user permissions, and feature flags throughout a request. These values are expensive to compute but needed across multiple services:
<?php
namespace App\Services;
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Facades\Cache;
class TenantService
{
public function getCurrentTenantConfig(): array
{
return Context::remember('tenant-config', function () {
$tenant = $this->getCurrentTenant();
return [
'features' => $this->getFeatureFlags($tenant),
'billing_plan' => $this->getBillingPlan($tenant),
'custom_settings' => $this->getCustomSettings($tenant),
'api_limits' => $this->getApiLimits($tenant),
];
});
}
public function getUserPermissions(): array
{
return Context::remember('user-permissions', function () {
$user = auth()->user();
$tenant = $this->getCurrentTenant();
// Complex permission calculation
$basePermissions = $user->permissions->pluck('name')->toArray();
$rolePermissions = $user->roles->flatMap->permissions->pluck('name')->toArray();
$tenantOverrides = $this->getTenantPermissionOverrides($tenant, $user);
return array_unique(array_merge(
$basePermissions,
$rolePermissions,
$tenantOverrides
));
});
}
public function getEncryptionKeys(): array
{
// Sensitive data using rememberHidden - not logged
return Context::rememberHidden('encryption-keys', function () {
$tenant = $this->getCurrentTenant();
return [
'api_key' => $this->decryptTenantApiKey($tenant),
'webhook_secret' => $this->getTenantWebhookSecret($tenant),
'jwt_secret' => $this->getTenantJwtSecret($tenant),
];
});
}
public function canAccessFeature(string $feature): bool
{
$config = $this->getCurrentTenantConfig();
$permissions = $this->getUserPermissions();
return in_array($feature, $config['features']) &&
in_array("access:{$feature}", $permissions);
}
private function getCurrentTenant()
{
return Context::remember('current-tenant', function () {
return Cache::remember(
'tenant:' . request()->getHost(),
3600,
fn() => Tenant::where('domain', request()->getHost())->first()
);
});
}
private function getFeatureFlags($tenant): array
{
// Implementation details...
return [];
}
private function getBillingPlan($tenant): string
{
// Implementation details...
return 'standard';
}
private function getCustomSettings($tenant): array
{
// Implementation details...
return [];
}
private function getApiLimits($tenant): array
{
// Implementation details...
return ['requests_per_hour' => 1000];
}
private function getTenantPermissionOverrides($tenant, $user): array
{
// Implementation details...
return [];
}
private function decryptTenantApiKey($tenant): string
{
// Implementation details...
return 'encrypted-api-key';
}
private function getTenantWebhookSecret($tenant): string
{
// Implementation details...
return 'webhook-secret';
}
private function getTenantJwtSecret($tenant): string
{
// Implementation details...
return 'jwt-secret';
}
}
// Usage across different parts of your application
class ApiController extends Controller
{
public function __construct(
private TenantService $tenantService
) {}
public function handleWebhook(Request $request)
{
// Encryption keys are retrieved from context (if cached) or computed once
$keys = $this->tenantService->getEncryptionKeys();
if (!$this->verifyWebhookSignature($request, $keys['webhook_secret'])) {
abort(401);
}
if (!$this->tenantService->canAccessFeature('webhooks')) {
abort(403, 'Webhook feature not available for current plan');
}
// Process webhook...
}
public function getUserDashboard()
{
// Permissions computed once per request, reused everywhere
$permissions = $this->tenantService->getUserPermissions();
$config = $this->tenantService->getCurrentTenantConfig();
return view('dashboard', compact('permissions', 'config'));
}
}
The remember()
function ensures expensive operations like permission calculations and tenant configuration lookups happen only once per request, regardless of how many times they're called. The rememberHidden()
variant keeps sensitive data like encryption keys out of logs while maintaining the same caching behavior.
This pattern is particularly powerful in middleware chains, service layers, and view composers where the same context data might be accessed multiple times. The closure-based approach also makes the code self-documenting—you can immediately see what computation happens when the cache is empty.
Both functions integrate seamlessly with Laravel's existing Context system, meaning the cached values are automatically available to queued jobs and other parts of your application that rely on request context.
Stay Updated with More Laravel Tips
Enjoyed this article? There's plenty more where that came from! Subscribe to our channels to stay updated with the latest Laravel tips, tricks, and best practices: