如何在Fractal JSON响应中返回error=false并还原原有结构?
搞定Fractal返回结构和Laravel原生模型一致的问题
嘿,我懂你现在的头疼——用了Fractal转换器后,User模型的JSON结构变了,导致所有单元测试都挂了。别慌,咱们一步步把结构调回和之前直接返回模型时一模一样的样子:
1. 先干掉Fractal默认的data外层包裹
Laravel直接返回模型时,JSON是直接输出属性的,而Fractal默认会把数据套在data键里,这是最常见的差异点。咱们通过自定义序列化器来解决:
首先创建一个自定义序列化器文件app/Serializers/LaravelSerializer.php:
<?php namespace App\Serializers; use League\Fractal\Serializer\ArraySerializer; class LaravelSerializer extends ArraySerializer { // 单个模型返回时,直接输出数据,不包外层键 public function item($resourceKey, array $data) { return $data; } // 集合返回时,直接输出数组,不包外层 public function collection($resourceKey, array $data) { return $data; } // 如果有分页需求,复刻Laravel原生分页的结构 public function paginatedCollection($resourceKey, array $data, array $pagination) { return [ 'data' => $data, 'total' => $pagination['total'], 'per_page' => $pagination['per_page'], 'current_page' => $pagination['current_page'], 'last_page' => $pagination['last_page'], 'from' => $pagination['from'], 'to' => $pagination['to'], ]; } }
然后去config/api.php里把Dingo的序列化器换成咱们自定义的:
'response' => [ 'serializer' => App\Serializers\LaravelSerializer::class, 'contentType' => 'application/json', ],
这一步做完,外层的data包裹就没了,和原生返回的结构第一步对齐。
2. 让转换器完全复刻原生模型的字段
Laravel直接返回模型时,会自动排除$hidden里的字段,同时输出所有可见属性,日期字段也会转成特定格式。咱们的转换器要完全照搬这个逻辑:
比如你的UserTransformer.php可以这么写:
<?php namespace App\Transformers; use App\User; use League\Fractal\TransformerAbstract; class UserTransformer extends TransformerAbstract { // 把你之前模型里常加载的关联列出来,和原生返回的关联一致 protected $availableIncludes = ['posts', 'profile']; public function transform(User $user) { // 先拿到模型的所有属性 $attributes = $user->getAttributes(); // 移除模型里设置的隐藏字段 foreach ($user->getHidden() as $hiddenField) { unset($attributes[$hiddenField]); } // 处理日期字段,转成和Laravel原生一样的格式 foreach ($user->getDates() as $dateField) { if (isset($attributes[$dateField])) { $attributes[$dateField] = $user->{$dateField}->toDateTimeString(); } } return $attributes; } // 处理关联,确保关联的结构也和原生一致 public function includePosts(User $user) { // 如果没加载关联,先加载(和原生懒加载逻辑一致) if (!$user->relationLoaded('posts')) { $user->load('posts'); } return $this->collection($user->posts, new PostTransformer); } public function includeProfile(User $user) { if (!$user->relationLoaded('profile')) { $user->load('profile'); } return $this->item($user->profile, new ProfileTransformer); } }
这里的关键是用getAttributes()拿原生属性,再同步$hidden和日期格式,确保字段和原生完全匹配。
3. 控制器里正确使用转换器
在控制器里返回时,用Dingo的response()->item()或者response()->collection()就行,因为咱们已经配置了自定义序列化器,返回的结构就和之前直接return $user;一样了:
public function show($id) { $user = User::with('profile')->findOrFail($id); return $this->response->item($user, new UserTransformer); }
最后验证一下
改完之后跑一遍单元测试,应该就能全部通过了。如果还有个别字段不对,就把原生返回的JSON和Fractal返回的JSON对比一下,调整转换器里的逻辑就行。
内容的提问来源于stack exchange,提问作者abbood
相关产品推荐
相关产品推荐

