承接 snowsoft/laravel-model-caching 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

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

snowsoft/laravel-model-caching

Composer 安装命令:

composer require snowsoft/laravel-model-caching

包简介

Automatic caching for Eloquent models.

README 文档

README

Laravel Package Packagist GitHub license PHP Version Laravel Latest Stable Version GitHub Stars codecov Tests

Model Caching for Laravel masthead image

🗂️ Table of Contents

📖 Summary

Automatic, self-invalidating Eloquent model and relationship caching. Add a trait to your models and all query results are cached automatically — no manual cache keys, no forgetting to invalidate. When a model is created, updated, or deleted the relevant cache entries are flushed for you.

⚡ Typical performance improvements range from 100–900% reduction in database queries on read-heavy pages. 🧪 Backed by 335+ integration tests across PHP 8.2–8.5 and Laravel 11–13.

Use this package when your application makes many repeated Eloquent queries and you want a drop-in caching layer that stays in sync with your data without any manual bookkeeping.

🔄 Before & After

Without this package — manual cache keys, manual invalidation:

$posts = Cache::remember('posts:active:page:1', 3600, function () {
    return Post::where('active', true)->with('comments')->paginate();
});

// And in every observer or event listener…
Cache::forget('posts:active:page:1');
// Hope you remembered every key variant! 😅

With this package — add the trait, query normally:

// Just query. Caching and invalidation happen automatically. ✨
$posts = Post::where('active', true)->with('comments')->paginate();

✅ What Gets Cached

  • Model queries (get, first, find, all, paginate, pluck, value, exists)
  • Aggregations (count, sum, avg, min, max)
  • Eager-loaded relationships (via with())

🚫 What Does Not Get Cached

  • Lazy-loaded relationships — only eager-loaded (with()) relationships are cached. Use with() to benefit from caching.
  • Queries using select() clauses — custom column selections bypass the cache.
  • Queries inside transactions — cache is not automatically flushed when a transaction commits; call flushCache() manually if needed.
  • inRandomOrder() queries — caching is automatically disabled since results should differ each time.

💾 Cache Drivers

Driver Supported
Redis ✅ (recommended)
Memcached
APC
Array
File
Database
DynamoDB

📋 Requirements

  • PHP 8.2+
  • Laravel 11, 12, or 13

📦 Installation

composer require genealabs/laravel-model-caching

✨ The service provider is auto-discovered. No additional setup is required.

🚀 Getting Started

Add the Cachable trait to your models. The recommended approach is a base model that all other models extend:

<?php

namespace App\Models;

use GeneaLabs\LaravelModelCaching\Traits\Cachable;
use Illuminate\Database\Eloquent\Model;

abstract class BaseModel extends Model
{
    use Cachable;
}

Alternatively, extend the included CachedModel directly:

<?php

namespace App\Models;

use GeneaLabs\LaravelModelCaching\CachedModel;

class Post extends CachedModel
{
    // ...
}

🎉 That's it — all Eloquent queries and eager-loaded relationships on these models are now cached and automatically invalidated.

⚠️ Note: You can cache the User model — the Cachable trait does not conflict with Laravel's authentication. Just avoid using cache cool-down periods on it, and ensure user updates always go through Eloquent (not raw DB::table() queries) so cache invalidation fires correctly.

🌍 Real-World Example

Consider a blog with posts, comments, and tags:

class Post extends BaseModel
{
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }

    public function tags()
    {
        return $this->belongsToMany(Tag::class);
    }
}

// All cached automatically — the query, the eager loads, everything. 🪄
$posts = Post::with('comments', 'tags')
    ->where('published', true)
    ->latest()
    ->paginate(15);

When a new comment is created, the cache for Post and Comment queries is automatically invalidated — no manual Cache::forget() calls needed. 🧹

⚙️ Configuration

Publish the config file:

php artisan modelCache:publish --config

This creates config/laravel-model-caching.php:

return [
    'cache-prefix'         => '',
    'enabled'              => env('MODEL_CACHE_ENABLED', true),
    'use-database-keying'  => env('MODEL_CACHE_USE_DATABASE_KEYING', true),
    'store'                => env('MODEL_CACHE_STORE'),
    'fallback-to-database' => env('MODEL_CACHE_FALLBACK_TO_DB', false),
];

🔧 Environment Variables

Variable Default Description
MODEL_CACHE_ENABLED true ✅ Enable or disable caching globally.
MODEL_CACHE_STORE null 💾 Cache store name from config/cache.php. Uses the default store when not set.
MODEL_CACHE_USE_DATABASE_KEYING true 🔑 Include database connection and name in cache keys. Important for multi-tenant or multi-database apps.
MODEL_CACHE_FALLBACK_TO_DB false 🛡️ When true, falls back to direct database queries if the cache backend is unavailable (e.g. Redis is down) instead of throwing an exception.

📝 Note: The cache-prefix option is set directly in the config file (not via an environment variable). For dynamic prefixes (e.g. multi-tenant), use the per-model $cachePrefix property shown below.

💾 Custom Cache Store

To use a dedicated cache store for model caching, define one in config/cache.php and reference it:

MODEL_CACHE_STORE=model-cache

🏷️ Cache Key Prefix

For multi-tenant applications you can isolate cache entries per tenant. Set the prefix globally in config:

'cache-prefix' => 'tenant-123',

Or per-model via a property:

<?php

namespace App\Models;

use GeneaLabs\LaravelModelCaching\Traits\Cachable;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use Cachable;

    protected $cachePrefix = 'tenant-123';
}

🔌 Multiple Database Connections

When use-database-keying is enabled (the default), cache keys automatically include the database connection and name. This keeps cache entries separate across connections without any extra configuration.

🚫 Disabling Cache

There are three ways to bypass caching:

1. Per-query (only affects this query chain, not subsequent queries):

$results = MyModel::disableCache()->where('active', true)->get();

2. Globally via environment:

MODEL_CACHE_ENABLED=false

3. For a block of code:

$result = app('model-cache')->runDisabled(function () {
    return MyModel::get();
});

// or via the Facade
use GeneaLabs\LaravelModelCaching\Facades\ModelCache;

ModelCache::runDisabled(function () {
    return MyModel::get();
});

💡 Tip: Use option 1 in seeders to avoid pulling stale cached data during reseeds.

❄️ Cache Cool-Down Period

In high-traffic scenarios (e.g. frequent comment submissions) you may want to prevent every write from immediately flushing the cache. Cool-down requires two steps:

Declare the default duration on the model (this alone does nothing — it just sets the value):

<?php

namespace App\Models;

use GeneaLabs\LaravelModelCaching\Traits\Cachable;
use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    use Cachable;

    protected $cacheCooldownSeconds = 300; // 5 minutes ⏱️
}

Activate the cool-down by calling withCacheCooldownSeconds() in your query. This writes the cool-down window into the cache store:

// Activate using the model's default (300 seconds)
Comment::withCacheCooldownSeconds()->get();

// Or override with a specific duration
Comment::withCacheCooldownSeconds(30)->get();

Once activated, writes during the cool-down window will not flush the cache. After the window expires, the next write triggers a flush and re-warms the cache. 🔄

🛡️ Graceful Fallback

When enabled, if the cache backend (e.g. Redis) is unavailable the package logs a warning and falls back to querying the database directly — your application continues to function without caching rather than throwing an exception.

MODEL_CACHE_FALLBACK_TO_DB=true

🧹 Cache Invalidation

Cache is automatically flushed when:

Trigger Behavior
Model created Flush model cache
Model updated/saved Flush model cache
Model deleted Flush only if rows were actually deleted
Model force-deleted Flush only if rows were actually deleted
Pivot attach / detach / sync / updateExistingPivot Flush relationship cache
increment / decrement Flush model cache
insert / update (builder) Flush model cache
truncate Flush model cache

Cache tags are generated for the primary model, each eager-loaded relationship, joined tables, and morph-to target types, so only the relevant entries are invalidated. 🎯

🔗 BelongsToMany with Custom Pivot Models

Cache invalidation works for BelongsToMany relationships using custom pivot models (->using(CustomPivot::class)) as long as either the parent or the related model uses the Cachable trait.

🧹 Manual Cache Flushing

Artisan command — single model:

php artisan modelCache:clear --model='App\Models\Post'

Artisan command — all models:

php artisan modelCache:clear

🔧 Programmatic via Facade:

use GeneaLabs\LaravelModelCaching\Facades\ModelCache;

// Single model
ModelCache::invalidate(App\Models\Post::class);

// Multiple models
ModelCache::invalidate([
    App\Models\Post::class,
    App\Models\Comment::class,
]);

⏰ Cache Expiration (TTL)

Cached queries are stored indefinitely (rememberForever) and rely on automatic invalidation (see above) to stay fresh. There is no per-query TTL option. If you need time-based expiry, use the cool-down period feature or flush the cache on a schedule via the Artisan command.

🧪 Testing

In your test suite you can either disable model caching entirely or use the array cache driver:

🚫 Disable caching in tests:

// In your TestCase setUp() or phpunit.xml
config(['laravel-model-caching.enabled' => false]);

✅ Use the array driver (useful for testing cache behavior itself):

config(['cache.stores.model-test' => ['driver' => 'array']]);
config(['laravel-model-caching.store' => 'model-test']);

👷 Queue Workers

The package has no special queue or Horizon integration. Cached queries inside queued jobs work the same as in HTTP requests. Cache invalidation triggered in a web request is immediately visible to queue workers (assuming a shared cache store like Redis). No additional configuration is needed.

🤝 Contributing

Contributions are welcome! 🎉 Please review the Contribution Guidelines and observe the Code of Conduct before submitting a pull request.

⬆️ Upgrading

For breaking changes and upgrade instructions between versions, see the Releases page on GitHub.

🔐 Security

Please review the Security Policy for information on supported versions and how to report vulnerabilities.

📚 Further Reading

The test suite serves as living documentation — browse it for detailed examples of every supported query type, relationship pattern, and edge case. 📖

Built with ❤️ for the Laravel community using lots of ☕️ by Mike Bronner.

This is an MIT-licensed open-source project. Its continued development is made possible by the community. If you find it useful, please consider 💖 becoming a sponsor and ⭐ing it on GitHub.

🙏 Thank you to all contributors who have helped make this package better!

snowsoft/laravel-model-caching 适用场景与选型建议

snowsoft/laravel-model-caching 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 453 次下载、GitHub Stars 达 0, 最近一次更新时间为 2022 年 09 月 30 日, 在 PHP 生态内属于活跃度较高的组件。

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

围绕 snowsoft/laravel-model-caching 我们能提供哪些服务?
定制开发 / 二次开发

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

BUG 修复 & 性能优化

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

项目外包 & 长期维护

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

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

统计信息

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

GitHub 信息

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

其他信息

  • 授权协议: MIT
  • 更新时间: 2022-09-30