Laravel Tutorials

Efficient Context Management with Laravel's Remember Functions

Published
Efficient Context Management with Laravel's Remember Functions image

Laravel's Context API introduces powerful remember() and rememberHidden() methods that streamline request-scoped data management through elegant closure-based memoization. These functions optimize expensive computations across your application with clean, expressive syntax.

The remember() function provides automatic memoization within the request context:

use Illuminate\Support\Facades\Context;

$roles = Context::remember('user-roles', fn() => $this->calculateUserRoles(auth()->user()));

This method automatically manages the existence check and computation cycle, executing the closure only when the context key is absent.

Consider a multi-service architecture where request-scoped data like organization settings, access controls, and feature configurations must be shared efficiently across various application layers. These values are computationally expensive but frequently accessed throughout the request lifecycle:

<?php

namespace App\Services;

use Illuminate\Support\Facades\Context;
use Illuminate\Support\Facades\DB;
use App\Models\Organization;
use App\Models\User;

class OrganizationContextService
{
    public function getOrganizationSettings(): array
    {
        return Context::remember('org-settings', function () {
            $organization = $this->getCurrentOrganization();
            
            return [
                'subscription_tier' => $this->getSubscriptionTier($organization),
                'enabled_modules' => $this->getEnabledModules($organization),
                'user_limits' => $this->getUserLimits($organization),
                'integration_settings' => $this->getIntegrationSettings($organization),
            ];
        });
    }

    public function getCurrentUserAccessLevel(): array
    {
        return Context::remember('user-access-level', function () {
            $user = auth()->user();
            $organization = $this->getCurrentOrganization();
            
            $directPermissions = $user->permissions()
                ->where('organization_id', $organization->id)
                ->pluck('action')
                ->toArray();
                
            $groupPermissions = $user->groups()
                ->with('permissions')
                ->get()
                ->flatMap(fn($group) => $group->permissions)
                ->pluck('action')
                ->toArray();
                
            $inheritedPermissions = $this->calculateInheritedPermissions($user, $organization);
            
            return array_unique(array_merge(
                $directPermissions,
                $groupPermissions,
                $inheritedPermissions
            ));
        });
    }

    public function getSecureCredentials(): array
    {
        return Context::rememberHidden('secure-credentials', function () {
            $organization = $this->getCurrentOrganization();
            
            return [
                'api_secret' => decrypt($organization->encrypted_api_secret),
                'webhook_token' => $this->generateWebhookToken($organization),
                'internal_key' => $this->getInternalEncryptionKey($organization),
            ];
        });
    }

    public function hasFeatureAccess(string $feature): bool
    {
        $settings = $this->getOrganizationSettings();
        $userAccess = $this->getCurrentUserAccessLevel();
        
        return in_array($feature, $settings['enabled_modules']) && 
               in_array("feature:{$feature}", $userAccess);
    }

    protected function getCurrentOrganization(): Organization
    {
        return Context::remember('current-organization', function () {
            $subdomain = request()->route('organization') ?? request()->getHost();
            
            return Organization::where('subdomain', $subdomain)
                             ->with(['subscription', 'settings'])
                             ->firstOrFail();
        });
    }

    protected function getSubscriptionTier(Organization $organization): string
    {
        return $organization->subscription->tier ?? 'basic';
    }

    protected function getEnabledModules(Organization $organization): array
    {
        $baseTier = $this->getSubscriptionTier($organization);
        $customModules = $organization->settings->pluck('module')->toArray();
        
        return array_merge(
            $this->getDefaultModulesForTier($baseTier),
            $customModules
        );
    }

    protected function getUserLimits(Organization $organization): array
    {
        $tier = $this->getSubscriptionTier($organization);
        
        return match($tier) {
            'enterprise' => ['max_users' => -1, 'storage_gb' => 1000],
            'professional' => ['max_users' => 100, 'storage_gb' => 100],
            'basic' => ['max_users' => 10, 'storage_gb' => 10],
            default => ['max_users' => 5, 'storage_gb' => 1],
        };
    }

    protected function getIntegrationSettings(Organization $organization): array
    {
        return $organization->integrations()
                          ->where('active', true)
                          ->get()
                          ->mapWithKeys(fn($integration) => [
                              $integration->service => $integration->configuration
                          ])
                          ->toArray();
    }

    protected function calculateInheritedPermissions(User $user, Organization $organization): array
    {
        if ($user->is_organization_owner) {
            return ['admin:*', 'user:*', 'billing:*', 'settings:*'];
        }
        
        if ($user->is_manager) {
            return ['user:manage', 'reports:view', 'settings:view'];
        }
        
        return ['profile:edit', 'dashboard:view'];
    }

    protected function generateWebhookToken(Organization $organization): string
    {
        return hash_hmac('sha256', $organization->id . now()->timestamp, config('app.key'));
    }

    protected function getInternalEncryptionKey(Organization $organization): string
    {
        return hash('sha256', config('app.key') . $organization->uuid);
    }

    protected function getDefaultModulesForTier(string $tier): array
    {
        return match($tier) {
            'enterprise' => ['analytics', 'integrations', 'advanced_reports', 'api_access'],
            'professional' => ['analytics', 'basic_reports', 'limited_api'],
            'basic' => ['dashboard', 'basic_reports'],
            default => ['dashboard'],
        };
    }
}

class ReportingController extends Controller
{
    public function __construct(
        private OrganizationContextService $contextService
    ) {}

    public function generateReport(Request $request)
    {
        if (!$this->contextService->hasFeatureAccess('advanced_reports')) {
            abort(403, 'Advanced reporting not available for current plan');
        }

        $settings = $this->contextService->getOrganizationSettings();
        $userAccess = $this->contextService->getCurrentUserAccessLevel();
        
        return view('reports.advanced', compact('settings', 'userAccess'));
    }

    public function exportData(Request $request)
    {
        $credentials = $this->contextService->getSecureCredentials();
        
        if (!$this->verifyExportPermissions($credentials['internal_key'])) {
            abort(401, 'Invalid export permissions');
        }

        return $this->processDataExport($request);
    }
}

The remember() function ensures expensive operations like permission calculations and organization lookups execute only once per request, regardless of access frequency. The rememberHidden() variant maintains identical caching behavior while excluding sensitive data from log entries, making it ideal for credentials and security tokens.

This pattern excels in middleware layers, service dependencies, and view composers where identical context data requires multiple access points. The closure-based design creates self-documenting code that clearly indicates the computation triggered during cache misses.

Harris Raftopoulos photo

Senior Software Engineer • Staff & Educator @ Laravel News • Co-organizer @ Laravel Greece Meetup

Sponsored

serpapi logo
SerpApi

The Web Search API for Your LLM and AI Applications

Visit SerpApi

The latest

View all →
Laravel AI SDK 1.4 Lets Agents Recover From Tool Failures image

Laravel AI SDK 1.4 Lets Agents Recover From Tool Failures

Read article
Laravel Read Replicas: Check if a Replica Has Caught Up image

Laravel Read Replicas: Check if a Replica Has Caught Up

Read article
UUID Request Input and Migration Failure Events in Laravel 13.42 image

UUID Request Input and Migration Failure Events in Laravel 13.42

Read article
Replay Missed Laravel Scheduled Tasks With Schedule Catchup image

Replay Missed Laravel Scheduled Tasks With Schedule Catchup

Read article
Search Polymorphic Relations and Filament Tables With Laravel Searchable image

Search Polymorphic Relations and Filament Tables With Laravel Searchable

Read article
Laravel Rest Api 3.0: Typed Fields, Metrics, and OpenAPI Docs image

Laravel Rest Api 3.0: Typed Fields, Metrics, and OpenAPI Docs

Read article