splitstack / laravel-stashable
An attribute-based caching system for repositories
Requires
- php: ^8.1
- laravel/framework: ^10.0|^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^8.31
- phpunit/phpunit: ^10.5
README
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
- Add the
Stashabletrait 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(); } }
- 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');
- 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.