如何从同表JSON列数据创建关联,获取Applicant模型集合
当然可以实现!你不需要单独的applicant表就能让$application->applicants返回Applicant模型的集合——Eloquent模型不一定非得绑定数据库表,我们可以把它当作结构化数据的封装容器来用。下面是具体的实现步骤:
实现步骤
1. 创建Applicant模型
先建一个Applicant模型,不需要对应任何数据库表,只用来封装申请人的数据和可能的业务逻辑:
namespace App\Models; use Illuminate\Database\Eloquent\Model; class Applicant extends Model { // 明确告诉Eloquent这个模型没有对应的数据库表 protected $table = null; // 允许批量赋值的字段,对应JSON里的键 protected $fillable = ['first_name']; // 因为没有表,不需要时间戳字段,关闭它 public $timestamps = false; // 你还可以在这里添加自定义方法,比如如果以后有last_name的话: // public function getFullNameAttribute() // { // return "{$this->first_name} {$this->last_name}"; // } }
2. 在Application模型中处理JSON字段转换
接下来要让applications表的applicantsJSON字段自动转换成Applicant模型的集合。有两种常用方式:
方式一:使用访问器和修改器
直接在Application模型里定义访问器(读取字段时转换)和修改器(写入字段时转换):
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; class Application extends Model { protected $table = 'applications'; // 读取applicants字段时,将JSON转成Applicant模型集合 public function getApplicantsAttribute($value) { // 把JSON字符串解析成数组,空值时返回空数组 $applicantData = json_decode($value, true) ?? []; // 将每个数组项转换成Applicant实例,再包装成Collection return collect($applicantData)->map(function ($data) { return new Applicant($data); }); } // 写入applicants字段时,将集合/模型数组转成JSON public function setApplicantsAttribute($value) { // 如果传入的是Collection,先转成数组 if ($value instanceof Collection) { $value = $value->toArray(); } // 如果传入的是Applicant模型数组,提取每个模型的属性数组 elseif (is_array($value) && isset($value[0]) && $value[0] instanceof Applicant) { $value = array_map(fn($applicant) => $applicant->toArray(), $value); } // 编码成JSON字符串存入数据库 $this->attributes['applicants'] = json_encode($value); } }
方式二:使用自定义Cast类(更优雅)
如果想让逻辑更清晰、可复用,可以创建一个自定义的Cast类:
首先创建App\Casts\ApplicantCollectionCast.php:
namespace App\Casts; use App\Models\Applicant; use Illuminate\Contracts\Database\Eloquent\CastsAttributes; use Illuminate\Support\Collection; class ApplicantCollectionCast implements CastsAttributes { // 读取字段时的转换逻辑 public function get($model, $key, $value, $attributes) { $applicantData = json_decode($value, true) ?? []; return collect($applicantData)->map(fn($data) => new Applicant($data)); } // 写入字段时的转换逻辑 public function set($model, $key, $value, $attributes) { if ($value instanceof Collection) { $value = $value->toArray(); } elseif (is_array($value)) { $value = array_map(fn($item) => $item instanceof Applicant ? $item->toArray() : $item, $value); } return json_encode($value); } }
然后在Application模型里指定这个Cast:
namespace App\Models; use App\Casts\ApplicantCollectionCast; use Illuminate\Database\Eloquent\Model; class Application extends Model { protected $table = 'applications'; protected $casts = [ 'applicants' => ApplicantCollectionCast::class, ]; }
3. 使用方式
现在你就可以像使用关联模型一样调用了:
$application = Application::find('uuid-123-abc'); // 获取Applicant模型集合 $applicants = $application->applicants; // 遍历每个申请人 foreach ($applicants as $applicant) { echo $applicant->first_name; // 输出 Dave、Steve } // 还可以使用Collection的各种方法 $firstNames = $applicants->pluck('first_name'); // 返回 ['Dave', 'Steve']
注意事项
Applicant模型因为没有绑定数据库表,所以不能调用save()、find()、create()这类数据库操作方法,它只是用来封装数据和业务逻辑的容器。- 如果以后需要扩展申请人的字段(比如
last_name、email),只需要修改Applicant模型的$fillable和JSON字段的结构即可。
内容的提问来源于stack exchange,提问作者mikelovelyuk
相关产品推荐
相关产品推荐

