splitstack/laravel-stashable

An attribute-based caching system for repositories

Maintainers

Package info

github.com/EmilienKopp/Stashable

pkg:composer/splitstack/laravel-stashable

Statistics

Installs: 163

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.1 2025-08-14 13:24 UTC

This package is auto-updated.

Last update: 2026-04-17 14:59:55 UTC


README

Tests

PHP Version Laravel Version License: MIT Total Downloads

An elegant attribute-based caching system for Laravel repositories. Simplify your repository caching with powerful attributes and flexible cache management.

Mention

This package is part of the (WIP) Splitstack suite, a collection of tools and packages to help you build better Laravel applications. Stay tuned for more packages and updates!

Features

  • 🎯 Attribute-Based Caching: Declaratively cache repository methods using PHP attributes
  • 🔑 Smart Cache Keys: Automatically includes method arguments and query parameters
  • 🏷️ Cache Tags Support: Group related cache entries for bulk operations
  • 🔄 Flexible Cache Operations: Choose between cached, fresh, or refreshed data
  • 🛠️ Developer Friendly: Simple integration with existing repositories
  • Performance Optimized: Minimize database hits while keeping data fresh

Installation

You can install the package via composer:

composer require splitstack/laravel-stashable

Usage

  1. Add the Stashable trait to your repository:
use Splitstack\Stashable\Traits\Stashable;

class UserRepository
{
    use Stashable;
    
    #[WithCache('user.all')] // Cache key: user.all
    public function getAll()
    {
        return User::all();
    }
    
    #[WithCache(ttl: 3600)] // Cache for 1 hour
    public function getById($id)
    {
        return User::find($id);
    }

    #[WithCache(key: 'role_{0}')] // key: role_admin
    public function getByRole($role)
    {
        return User::where('role', $role)->get();
    }

    #[WithCache(key: 'search.{0}.{1}')] // key: search.department.seniority
    public function searchDepartment($department, $seniority)
    {
        return Product::where('department', $department)
                      ->where('seniority', $seniority)
                      ->get();
    }

    #[WithCache(key: 'search.{company}.{position}')] // key: search.github.engineer
    public function searchCompany($company, $position)
    {
        return Product::where('department', $department)
                      ->where('position', $position)
                      ->get();
    }
}
  1. Use the caching methods:
// Get cached result (creates cache if doesn't exist)
$users = UserRepository::cache('getAll');

// Get fresh result (bypasses cache)
$user = UserRepository::fresh('getById', 1);

// Get cached result without creating cache if missing
$users = UserRepository::get('getAll');

// Refresh cache with fresh data
$users = UserRepository::refresh('getAll');
  1. Default values:

The TTL is set in config/stashable.php as 60 seconds by default. Keys will always default to the singular model name + method name. e.g.: UserRepository::getAll() will default to user.getAll if no key is provided in the #[WithCache] attribute.

Tenant-Aware Caching

Stashable supports multi-tenant applications with automatic cache isolation between tenants. When enabled, cache keys are automatically prefixed with the current tenant identifier.

Installation for Multi-Tenancy

First, install Spatie Laravel Multitenancy (recommended):

composer require spatie/laravel-multitenancy

Then publish and configure Stashable:

php artisan vendor:publish --tag=stashable-config

Configuration

Configure tenant awareness in config/stashable.php:

return [
    // Enable tenant-aware caching
    'tenant_aware' => true,
    
    // Detection methods (tried in order)
    'tenant_detection_methods' => [
        'spatie',    // Use Spatie Laravel Multitenancy
        'route',     // Extract from route parameter
        'subdomain', // Extract from subdomain
        'header',    // Extract from HTTP header
        'session',   // Extract from session
    ],
    
    // Route parameter name for tenant detection
    'tenant_route_parameter' => 'tenant',
    
    // HTTP header name for tenant detection
    'tenant_header_name' => 'X-Tenant-ID',
    
    // Session key for tenant detection
    'tenant_session_key' => 'tenant_id',
];

Usage with Tenants

Once configured, your cached repository methods automatically become tenant-aware:

// Tenant 1 context
$users = UserRepository::cache('getAll'); // Cached as: tenant_1.user.getAll

// Tenant 2 context  
$users = UserRepository::cache('getAll'); // Cached as: tenant_2.user.getAll

Tenant Cache Management

Flush cache for specific tenants:

// Flush cache for current tenant
UserRepository::flushTenantCache();

// Flush cache for specific tenant
UserRepository::flushTenantCacheFor('tenant-123');

// Get tenant cache statistics
$stats = UserRepository::getTenantCacheStats();

Console Commands

Manage tenant cache via Artisan commands:

# Flush cache for current tenant
php artisan stashable:tenant-cache flush

# Flush cache for specific tenant
php artisan stashable:tenant-cache flush --tenant=123

# View tenant cache statistics
php artisan stashable:tenant-cache stats

Cache Tags

You can tag cache entries for bulk operations:

#[WithCache(tags: ['users', 'roles'])]
public function getByRole($role)
{
    return User::where('role', $role)->get();
}

// Clear all caches with 'roles' tag
Cache::tags(['roles'])->clear();

Query Parameters (beta)

Cache keys can append query parameters from the Request facade if useQuery is set to true, ensuring different results for different query contexts:

#[WithCache(key:'search', useQuery: true)] // key: search?department=HR&seniority=Junior
public function search()
{
  $query = Request::query();
  return //... your search query
}

Testing

composer test

Security

If you discover any security related issues, please email the author instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.