Skip to content

Per-record model caching via HasPermanentCache trait#20

Open
mhortulanus wants to merge 2 commits into
mainfrom
feature/per-record-model-caching
Open

Per-record model caching via HasPermanentCache trait#20
mhortulanus wants to merge 2 commits into
mainfrom
feature/per-record-model-caching

Conversation

@mhortulanus
Copy link
Copy Markdown

Summary

Adds per-record permanent caching for Eloquent models alongside the existing class-level Cached flow.

  • HasPermanentCache trait: cached(), refreshCache(), forgetCache(), isCached(), cacheUpdatedAt().
  • $permanentCacheStore selects the cache connection only; the cache key is always <model_basename>:<getKey()>. Records without a primary key throw a LogicException.
  • Optional per-model knobs: $permanentCacheExpression (cron), $permanentCacheQueue (+ connection/name).
  • Registration in `AppServiceProvider::boot()`:
    ```php
    PermanentCache::models([User::class, Order::class]);
    ```
    Auto-attaches `saved` → refresh, `deleted` → forget, `restored` → refresh.
  • `RefreshModelCacheJob` for queued refreshes.
  • `php artisan permanent-cache:warm` iterates registered models in chunks; supports `--filter`, `--queue`, `--sync`, `--chunk`.
  • `permanent-cache:update` warms registered models after class caches.
  • Service provider schedules `permanent-cache:warm --filter=` per model that defines a cron expression.
  • `PermanentCacheUpdating`/`Updated` events now also accept Eloquent `Model`.

Test plan

  • `vendor/bin/pest` — all 18 tests pass (7 new).
  • Smoke-test in a host app: register a model, save → cache populated, update → entry refreshed, delete → entry forgotten.
  • `php artisan permanent-cache:warm` with multiple registered models.
  • `php artisan permanent-cache:update` rebuilds class caches and model caches.

Add a new way to cache Eloquent models per record. Models opt in by using
the `HasPermanentCache` trait and implementing `cachedResult(): mixed`.
Records are registered in `AppServiceProvider::boot()` with
`PermanentCache::models([...])`, which auto-attaches saved/deleted/restored
listeners to keep the per-record cache in sync.

- `HasPermanentCache` trait: cached(), refreshCache(), forgetCache(),
  isCached(), cacheUpdatedAt(). `$permanentCacheStore` selects the cache
  connection only; the key slug is `<model>:<getKey()>` and a record
  without a primary key throws.
- Optional per-model knobs: `$permanentCacheExpression` (cron),
  `$permanentCacheQueue` (+ connection/queue name).
- `RefreshModelCacheJob` queues refreshes when the model opts in.
- `permanent-cache:warm` command iterates registered models in chunks;
  `--queue`, `--sync`, `--filter`, `--chunk` flags supported.
- `permanent-cache:update` extended to also warm registered models.
- Service provider schedules `permanent-cache:warm --filter=<basename>`
  per model that defines `$permanentCacheExpression`.
- Events `PermanentCacheUpdating`/`Updated` now also accept Eloquent models.
- Tests cover key shape, save/delete invalidation, missing PK guard,
  registration validation, and warm command end-to-end.
PHP's unserialize returned __PHP_Incomplete_Class for the (object) cast
payload in some opcache/serializer scenarios, breaking ->value access.
Storing a plain array sidesteps it entirely; cacheUpdatedAt() now parses
an ISO-8601 string back into Carbon on read.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant