Laravel如何缓存带完整关联的Eloquent模型并还原为对应实例
解决方案
核心问题原因
hydrate()是批量处理数据集的方法,返回的始终是Eloquent集合,即使传入单个模型的属性数组也会返回包含1个元素的集合,直接调用模型属性会报错。- 直接通过
toJson()序列化后的关联数据是普通数组,默认的模型填充逻辑不会自动将其还原为关联模型实例。
方案一:直接缓存序列化模型(推荐,99%场景适用)
Laravel的Eloquent模型本身实现了序列化接口,缓存系统会自动处理序列化/反序列化逻辑,完整保留所有属性和预加载的关联关系,不需要额外处理:
if (Cache::has($website->id.'_main_page')) { $page = Cache::get($website->id.'_main_page'); } else { $expiresAt = now()->addMinutes(60); $page = Page::with(['sections', 'pageSections.sectionObjects']) ->where('website_id', $website->id) ->where('main', 1) ->first(); Cache::put($website->id.'_main_page', $page, $expiresAt); }
该方案取出的$page就是完整的Page模型实例,所有关联关系都是对应的模型对象,修改器、访问器、追加属性等逻辑都可以正常运行。
方案二:JSON存储场景手动还原
如果必须用JSON格式存储缓存(比如跨系统共享缓存),可以手动还原主模型和关联:
if (Cache::has($website->id.'_main_page')) { $data = json_decode(Cache::get($website->id.'_main_page'), true); // 还原主模型,第二个参数true表示该实例已存在于数据库 $page = (new Page())->newInstance($data, true); // 还原一级关联sections $page->setRelation('sections', Section::hydrate($data['sections'])); // 还原多层关联pageSections.sectionObjects $pageSections = PageSection::hydrate($data['page_sections']); $pageSections->each(function (PageSection $section) use ($data) { $objects = collect($data['page_sections']) ->where('id', $section->id) ->first()['section_objects'] ?? []; $section->setRelation('sectionObjects', SectionObject::hydrate($objects)); }); $page->setRelation('pageSections', $pageSections); } else { $expiresAt = now()->addMinutes(60); $page = Page::with(['sections', 'pageSections.sectionObjects']) ->where('website_id', $website->id) ->where('main', 1) ->first(); Cache::put($website->id.'_main_page', $page->toJson(), $expiresAt); }
注意事项
- 关联名对应的JSON键名是蛇形命名,比如
pageSections序列化后会变成page_sections,需要对应上 - 嵌套关联需要逐层调用
setRelation方法绑定
缓存使用建议
- 页面/关联数据更新时,主动删除对应缓存键,避免返回脏数据
- 如果缓存数据量级不大,优先使用方案一,开发成本和性能都优于JSON还原方案
内容的提问来源于stack exchange,提问作者Matrix
相关产品推荐
相关产品推荐

