kai-init/laravel-normcache 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

kai-init/laravel-normcache

Composer 安装命令:

composer require kai-init/laravel-normcache

包简介

Normalized caching for Laravel Eloquent. Self-invalidating, Redis-backed. Caches query IDs and model entities separately with versioned invalidation.

README 文档

README

Normalized, self-invalidating Redis caching for Laravel Eloquent.

Tests PHPStan Latest Version on Packagist License

Normcache caches query results as ID lists and stores model attributes in versioned model keys. When a model changes, Normcache bumps a version key instead of scanning and deleting every query that may have returned that model.

Requirements: PHP 8.2+, Laravel 12/13, Redis 6.0+

Table of Contents

Installation

composer require kai-init/laravel-normcache

Add Cacheable to models you want Normcache to manage:

use Illuminate\Database\Eloquent\Model;
use NormCache\Traits\Cacheable;

class Post extends Model
{
    use Cacheable;
}

What's new in 3.0

Redis Cluster sharding is now fully atomic within each cache space. Normcache keeps the keys for a cached operation and its valid dependencies in one hash slot, so cache reads, rebuilds, and invalidation coordination remain atomic.

  • Cache spaces: declare $normCacheSpaces on a model and select a declared space with ->space() when needed.
  • Space-targeted flushing: use NormCache::flushAll('space') or php artisan normcache:flush --space=....
  • Named table dependencies: dependsOnTables() works in named spaces and is invalidated with invalidateTableVersion().
  • Upgrade from 2.4: run php artisan normcache:flush before deploying v3 to clear legacy cache keys.

Usage

Normal Eloquent reads are cached automatically for cacheable models:

Post::all();
Post::where('active', true)->get();
Post::find(1);
Post::paginate(20);

Use withoutCache() or ttl() per query:

Post::withoutCache()->get();
Post::where('active', true)->ttl(600)->get();

Cross-table queries

Simple whereHas / whereDoesntHave constraints on cacheable relations and plain string joins with an explicit root-table projection are inferred automatically:

Author::whereHas('posts', fn($q) => $q->where('published', true))->get();

Author::join('posts', 'posts.author_id', '=', 'authors.id')
    ->select('authors.*')
    ->get();

For other cross-table reads, declare dependencies explicitly:

Author::query()
    ->dependsOn([Post::class])
    ->get();

Author::join('legacy_stats', 'legacy_stats.author_id', '=', 'authors.id')
    ->select('authors.*')
    ->dependsOnTables(['legacy_stats'])
    ->get();

dependsOnTables() declares a read dependency only. If that table is changed outside Eloquent, call NormCache::invalidateTableVersion($connection, $table) after the write.

Aggregates and relationships

count, exists, value, pluck, sum, avg, min, max, pagination totals, and withCount / withSum / withAvg / withMin / withMax / withExists are cached when their dependencies are safe.

Eager-loaded BelongsTo, BelongsToMany, MorphTo, MorphToMany, MorphedByMany, HasManyThrough, and HasOneThrough relations are cached. attach, detach, sync, and updateExistingPivot invalidate the relevant pivot cache.

Invalidation

Eloquent writes on cacheable models invalidate automatically. For manual invalidation:

use NormCache\Facades\NormCache;

NormCache::flushModel(Post::class);
NormCache::flushAll();
NormCache::flushAll('content');
php artisan normcache:flush --model="App\Models\Post"
php artisan normcache:flush
php artisan normcache:flush --space=content

If you mutate cacheable tables outside Eloquent, flush the affected model or table version yourself:

DB::table('posts')->update(['published' => true]);
NormCache::flushModel(Post::class);

Tags can group query entries for manual flushing:

Author::whereHas('posts')
    ->dependsOn([Post::class])
    ->tag('homepage')
    ->get();

NormCache::flushTag(Author::class, 'homepage');
NormCache::flushTagAcrossModels('homepage');

Cache spaces

Cache spaces are Normcache's Redis Cluster sharding boundary. Each space has a Redis hash tag, so a cached operation stays within one Cluster slot.

Models without a declaration use the default space ({nc}). Declare named spaces with $normCacheSpaces:

use Illuminate\Database\Eloquent\Model;
use NormCache\Traits\Cacheable;

class Post extends Model
{
    use Cacheable;

    protected static array $normCacheSpaces = ['content'];
}

How space resolution works:

  • If no space() is selected, a model uses its first declared space as its home space.
  • Post::query()->space('content') explicitly selects a space.
  • space() must select a space declared by the model, otherwise Normcache throws an InvalidArgumentException.
  • A model may declare multiple spaces, up to spaces.max_per_model.
  • Writes bump the model version in every declared space.

Dependencies must belong to the active space:

class Author extends Model
{
    use Cacheable;

    protected static array $normCacheSpaces = ['content'];
}

Post::query()
    ->space('content')
    ->dependsOn([Author::class])
    ->get();

If a model dependency is not valid in the active space, Normcache bypasses the cache by default. Set spaces.cross_space_behavior to throw to fail loudly during development. Raw table dependencies from dependsOnTables() can be used in any active space and are invalidated with invalidateTableVersion().

Configure placement when you need to control Redis Cluster hash tags:

'spaces' => [
    'max_per_model' => 16,
    'cross_space_behavior' => env('NORMCACHE_CROSS_SPACE_BEHAVIOR', 'bypass'),
    'placement' => [
        'catalog' => ['hash_tag' => 'nc:catalog'],
    ],
],

Configuration

Publish config/normcache.php if you need to customize runtime behavior:

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

Common options:

Option Purpose
connection Redis connection name. Default: cache.
enabled Master on/off switch.
ttl Model attribute key lifetime.
query_ttl Query/result/pivot/through key lifetime.
key_prefix Prefix for all Normcache Redis keys.
cooldown Debounce version bumps for write-heavy models.
building_lock_ttl Cache rebuild lock lifetime.
stampede_wait_ms How long waiters block for a rebuild wake signal.
stampede_wake_tokens Number of waiters to wake after a rebuild.
fallback Fail open to the database on Redis errors when true.
events Dispatch cache hit/miss events when true.
fire_retrieved Fire Eloquent retrieved for cached models when true.
debugbar Enable Laravel Debugbar integration when installed.
spaces.* Cache-space limits, cross-space policy, and hash-tag placement.

Bypasses and limitations

Normcache bypasses caching for unsafe reads rather than risking stale or incorrect data.

Always bypassed:

  • pessimistic locks (lockForUpdate, sharedLock)
  • reads inside a database transaction
  • DB::table(...), DB::select(), and raw SQL
  • chunk(), each(), lazy(), and sole()

Usually require dependsOn() or dependsOnTables():

  • manual whereExists
  • raw predicates
  • nested relation constraints
  • expression joins
  • GROUP BY, DISTINCT, and calculated columns

Other limitations:

  • Models should use standard single-column primary keys.
  • Writes outside Eloquent are invisible unless you manually flush or invalidate.
  • Packages that replace Eloquent builders, relation classes, or hydration behavior may bypass parts of Normcache.
  • Normcache caches model connection/table metadata. Call CacheKeyBuilder::reset() after switching tenants dynamically.

Observability

When events are enabled, Normcache dispatches query/model hit and miss events. When fruitcake/laravel-debugbar is installed and normcache.debugbar is enabled, cache hits, misses, bypasses, and model fetches appear in Debugbar.

License

MIT

kai-init/laravel-normcache 适用场景与选型建议

kai-init/laravel-normcache 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 78 次下载、GitHub Stars 达 2, 最近一次更新时间为 2026 年 05 月 05 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「redis」 「cache」 「laravel」 「eloquent」 「query-cache」 「model-cache」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 kai-init/laravel-normcache 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 kai-init/laravel-normcache 我们能提供哪些服务?
定制开发 / 二次开发

基于 kai-init/laravel-normcache 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 78
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 2
  • 点击次数: 54
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 2
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-05-05