Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Cachable Attributes Laravel Package

astrotomic/laravel-cachable-attributes

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require astrotomic/laravel-cachable-attributes
    

    Publish the config (optional, defaults are sensible):

    php artisan vendor:publish --provider="Astrotomic\CachableAttributes\CachableAttributesServiceProvider"
    
  2. Basic Usage: Add the HasCachableAttributes trait to your Eloquent model:

    use Astrotomic\CachableAttributes\HasCachableAttributes;
    
    class User extends Model
    {
        use HasCachableAttributes;
    
        // Your model code...
    }
    
  3. First Cachable Attribute: Define a cached accessor using the cachableAttribute() method:

    public function getFullNameAttribute(): string
    {
        return $this->cachableAttribute('full_name', function () {
            return "{$this->first_name} {$this->last_name}";
        });
    }
    
  4. Cache Invalidation: Automatically invalidates cache when related models change:

    // In User model
    public function profile()
    {
        return $this->hasOne(Profile::class);
    }
    
    // In Profile model
    public function user()
    {
        return $this->belongsTo(User::class)->withCachableAttributes();
    }
    

Implementation Patterns

Core Workflows

1. Defining Cachable Attributes

// Simple cached accessor
public function getFormattedAddressAttribute()
{
    return $this->cachableAttribute('formatted_address', function () {
        return "{$this->street}, {$this->city}, {$this->zip}";
    });
}

// Cached accessor with TTL (time-to-live)
public function getProcessedDataAttribute()
{
    return $this->cachableAttribute('processed_data', function () {
        return $this->expensiveComputation();
    }, now()->addMinutes(30));
}

2. Dynamic Caching

Cache attributes conditionally:

public function getDynamicContentAttribute()
{
    if ($this->shouldCacheContent()) {
        return $this->cachableAttribute('dynamic_content', function () {
            return $this->generateContent();
        });
    }
    return $this->generateContent();
}

3. Mass Assignment with Cachable Attributes

// In controller
$user = User::updateOrCreate(
    ['email' => $request->email],
    [
        'first_name' => $request->first_name,
        'last_name' => $request->last_name,
        // Cachable attributes will auto-update
    ]
);

4. Eager Loading Cachable Attributes

// In controller
$users = User::withCachableAttributes(['full_name', 'formatted_address'])->get();

5. Cache Invalidation Strategies

  • Manual Invalidation:
    $user->invalidateCachableAttribute('full_name');
    
  • Model Events:
    protected static function booted()
    {
        static::saved(function ($model) {
            $model->invalidateAllCachableAttributes();
        });
    }
    
  • Relationship Updates:
    // Automatically invalidates when related model changes
    public function profile()
    {
        return $this->hasOne(Profile::class)->withCachableAttributes();
    }
    

6. Custom Cache Stores

Configure in config/cachable-attributes.php:

'store' => 'redis',
'prefix' => 'cache.attributes',

Integration Tips

1. API Responses

Use cached attributes in API responses for consistent performance:

return UserResource::collection(
    User::withCachableAttributes(['full_name'])->get()
);

2. View Composition

Cache expensive view logic:

// In User model
public function getDisplayNameAttribute()
{
    return $this->cachableAttribute('display_name', function () {
        return $this->first_name ?? $this->email;
    });
}

// In view
<h1>{{ $user->display_name }}</h1>

3. Testing

Mock cache behavior in tests:

public function test_cachable_attribute()
{
    $user = User::factory()->create();
    Cache::shouldReceive('get')
         ->withArgs(['user.1.formatted_address'])
         ->andReturn('Cached Value');

    $this->assertEquals('Cached Value', $user->formatted_address);
}

4. Queue-Based Cache Updates

For long-running operations, defer cache updates:

public function update()
{
    $this->fireModelEvent('updating');
    // ... other updates ...
    Cache::forget("user.{$this->id}.full_name");
    $this->fireModelEvent('updated');
}

Gotchas and Tips

Pitfalls

  1. Cache Key Collisions:

    • Ensure attribute names are unique across models.
    • Fix: Use fully qualified names:
      $this->cachableAttribute('App\Models\User.formatted_address', ...);
      
  2. Lazy Loading Overhead:

    • Cachable attributes are lazy-loaded by default. Overuse can lead to N+1 queries.
    • Fix: Use withCachableAttributes() in queries or eager-load explicitly.
  3. Serialization Issues:

    • Complex objects (e.g., Dates, Collections) may not serialize properly.
    • Fix: Use serialize() or json_encode() in the accessor:
      $this->cachableAttribute('serialized_data', function () {
          return json_encode($this->complexData);
      });
      
  4. Cache Stampede:

    • Concurrent requests may recompute attributes if cache expires.
    • Fix: Implement a lock mechanism or use Cache::rememberForever().
  5. Relationship Invalidation:

    • Forgetting withCachableAttributes() on relationships can lead to stale cache.
    • Fix: Always include it in hasOne, belongsTo, etc.:
      return $this->hasOne(Profile::class)->withCachableAttributes();
      
  6. TTL Misconfiguration:

    • Setting TTL too long may serve stale data; too short causes recomputation.
    • Fix: Use dynamic TTLs based on data volatility:
      $ttl = $this->isActive() ? now()->addHours(1) : now()->addMinutes(5);
      $this->cachableAttribute('status', ..., $ttl);
      

Debugging Tips

  1. Cache Inspection: Use Tinker to inspect cached attributes:

    php artisan tinker
    >>> $user = App\Models\User::find(1);
    >>> Cache::get("user.{$user->id}.full_name");
    
  2. Logging Cache Hits/Misses: Enable debug mode in config:

    'debug' => env('CACHABLE_ATTRIBUTES_DEBUG', false),
    

    Check logs for:

    [CachableAttributes] Cache hit for user.1.full_name
    [CachableAttributes] Cache miss for user.1.formatted_address
    
  3. Clear All Caches:

    php artisan cache:clear
    php artisan view:clear
    
  4. Check Cache Events: Listen for cache events in EventServiceProvider:

    protected $listen = [
        'Astrotomic\CachableAttributes\Events\AttributeCached' => [
            \App\Listeners\LogCachedAttribute::class,
        ],
    ];
    

Extension Points

  1. Custom Cache Drivers: Extend the Astrotomic\CachableAttributes\Cache\CacheStore interface:

    class CustomCacheStore implements CacheStore
    {
        public function get($key)
        {
            // Custom logic
        }
    
        public function put($key, $value, $ttl = null)
        {
            // Custom logic
        }
    
        public function forget($key)
        {
            // Custom logic
        }
    }
    

    Register in config/cachable-attributes.php:

    'store' => \App\Services\CustomCacheStore::class,
    
  2. Attribute Events: Listen for cache-related events:

    // In EventServiceProvider
    protected $listen = [
        'Astrotomic\CachableAttributes\Events\AttributeCached' => [
            \App\Listeners\AnalyzeCachePerformance::class,
        ],
        'Astrotomic\CachableAttributes\Events\AttributeInvalidated' => [
            \App\Listeners\NotifyCacheInvalidation::class,
        ],
    ];
    
  3. Dynamic Attribute Names: Generate cache keys dynamically:

    public function getDynamicAttribute($query)
    {
        $key = "user.{$this->id}.{$query}";
        return $this->cachableAttribute($key, function () use ($query) {
            return $this->computeDynamic($query
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle