codewiser / http-cache-control
Laravel 的 HTTP 缓存控制层
Requires
- php: ^8.0
- laravel/framework: ^10.0|^11.0
Requires (Dev)
- fakerphp/faker: ^1.21
- phpunit/phpunit: ^9.6
README
此包提供与 HTTP Cache-Control 标头一起工作的解决方案。资源被缓存,然后在 Eloquent 事件上失效。服务器可以不进行数据库请求,仅使用缓存值来响应。
准备模型
CacheControl
使用缓存来保存标头值。然后模型发生变化时,相关的缓存必须失效。
模型必须实现 \Codewiser\HttpCacheControl\Contracts\Cacheable
。
以下是一个实现示例。类共享缓存标签,因此更改任何模型将使共享缓存失效。
use Codewiser\HttpCacheControl\Contracts\Cacheable; use Codewiser\HttpCacheControl\Observers\InvalidatesCache; use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Cache; use Psr\SimpleCache\CacheInterface; #[ObservedBy(InvalidatesCache::class)] class User extends Model implements Cacheable { public function cache(): CacheInterface { return Cache::tags(['user', 'order']); } } #[ObservedBy(InvalidatesCache::class)] class Order extends Model implements Cacheable { public function cache(): CacheInterface { return Cache::tags(['order', 'user']); } }
用法
CacheControl
类分析请求标头并创建带有适当标头的响应。
第一个参数必须是一个 \Psr\SimpleCache\CacheInterface
、\Codewiser\HttpCacheControl\Contracts\Cacheable
的类名,或者实现该接口的模型类。
第二个参数是一个回调,它应该返回响应内容。此回调仅在必要时才会被调用。
Cache-Control
标头
您可以设置您喜欢的任何 Cache-Control
指令
use Codewiser\HttpCacheControl\CacheControl; use Codewiser\HttpCacheControl\CacheControlHeader; public function index(Request $request) { return CacheControl::make(Order::class, function() { return OrderResource::collection(Order::all()) }) ->cacheControl(fn(Request $request) => new CacheControlHeader( public: true, max_age: 1800, must_revalidate: true, )); }
如果没有设置
public
或private
指令,则默认为private
。
在这种情况下,服务器上不会缓存任何内容。
Expires
标头
或者您可以只设置 Expires
标头
use Codewiser\HttpCacheControl\CacheControl; use Codewiser\HttpCacheControl\CacheControlHeader; public function index(Request $request) { return CacheControl::make(Order::class, function() { return OrderResource::collection(Order::all()) }) ->expires(now()->addHour()); }
在这种情况下,服务器上不会缓存任何内容。
缓存整个响应
您可能希望缓存整个响应
use Codewiser\HttpCacheControl\CacheControl; use Codewiser\HttpCacheControl\CacheControlHeader; public function index(Request $request) { return CacheControl::make(Order::class, function() { return OrderResource::collection(Order::all()) }) ->remember() ->cacheControl(new CacheControlHeader( public: true, max_age: now()->addHour(), must_revalidate: true, )); }
请谨慎使用。注意,缓存可能会变得很大。
私有缓存
如果控制器响应不应在用户之间共享,您应该设置 Cache-Control: private
指令。
use Codewiser\HttpCacheControl\CacheControl; use Codewiser\HttpCacheControl\CacheControlHeader; public function index(Request $request) { return CacheControl::make(Order::class, function(Request $request) { return OrderResource::collection(Order::all()) ->whereBelongsTo($request->user() }) ->cacheControl(new CacheControlHeader( private: true, max_age: new DateInterval('PT1H'), must_revalidate: true, )); }
Vary
标头
Vary
标头描述了一组对缓存有影响的请求标头。
例如,您的应用程序支持不同的语言,因此缓存取决于 Accept-Language
请求标头
use Codewiser\HttpCacheControl\CacheControl; use Codewiser\HttpCacheControl\CacheControlHeader; public function index(Request $request) { return CacheControl::make(Order::class, function() { return OrderResource::collection(Order::all()) }) ->vary('Accept-Language') ->cacheControl(new CacheControlHeader( public: true, max_age: 1800, must_revalidate: true, )); }
请注意,Web 服务器可能会附加更多的 Vary 标头值。通常为
Accept-Encoding
。
条件标头
控制器可以响应 ETag
和/或 Last-Modified
标头,因此下一个请求可以带有 If-None-Match
或 If-Modified-Since
标头,这使得它成为条件性的。
use Codewiser\HttpCacheControl\CacheControl; use Codewiser\HttpCacheControl\CacheControlHeader; public function index(Request $request) { return CacheControl::make(Order::class, function() { return OrderResource::collection(Order::all()) }) ->cacheControl(['public' => true]) // Implicit ->etag() // Or explicit ->etag(fn() => custom_etag_calculation(Order::all())); }
在此示例中,本地缓存仅存储 ETag
值。这足以检查未来的请求中的 If-None-Match
。
这种方式是最节省缓存的。
use Codewiser\HttpCacheControl\CacheControl; use Codewiser\HttpCacheControl\CacheControlHeader; public function index(Request $request) { return CacheControl::make(Order::class, function() { return OrderResource::collection(Order::all()) }) ->cacheControl(['public' => true]) // Return timestamp or DateTimeInterface ->lastModified(fn() => Order::all()->max('updated_at')); }