You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从同表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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:31:53