首页 > 解决方案 > 如何在 Laravel 中选择具有关系的自定义属性字段?

问题描述

我有表格emailsemail_attachments如下所示:

Schema::create('emails', function (Blueprint $table) {
    $table->id();
    $table->string('email')->nullable(false);
    $table->string('subject')->nullable(false);
    $table->text('body')->nullable(false);
    $table->timestamps();
});

Schema::create('email_attachments', function (Blueprint $table) {
    $table->id();
    $table->foreignId('email_id')->nullable(false);
    $table->string('file_name')->nullable(false);
    $table->string('file_path')->nullable(false);

    $table->foreign('email_id')->references('id')->on('emails');
});

我的模型类Email如下EmailAttachment

class Email extends Model
{
    use HasFactory;

    protected $fillable = ['email', 'subject', 'body'];

    function attachments()
    {
        return $this->hasMany(EmailAttachment::class);
    }
}

class EmailAttachment extends Model
{
    use HasFactory;

    public $timestamps = false;
    protected $fillable = ['email_id', 'file_name', 'file_path'];
    protected $appends = ['url'];

    function email()
    {
        return $this->belongsTo(Email::class);
    }

    public function getUrlAttribute()
    {
        return Storage::disk('public')->url($this->file_name);
    }
}

这样做return Email::with('attachments')->get();我得到了所有的回应:

{
    "id": 1,
    "email": "me@me.com",
    "subject": "My Subject",
    "body": "<html><body><h1>My test message.</h1></body></html>",
    "created_at": "2021-02-26T23:32:08.000000Z",
    "updated_at": "2021-02-26T23:32:08.000000Z",
    "attachments": [
        {
            "id": 1,
            "email_id": 1,
            "file_name": "foo.jpg",
            "file_path": "/home/user/laravel/storage/app/foo.jpg",
            "url": "http://127.0.0.1:8000/storage/foo.jpg"
        }
    ]
}

return Email::with('attachments:file_name')->get();得到attachments一个空数组:

{
    "id": 1,
    "email": "me@me.com",
    "subject": "My Subject",
    "body": "<html><body><h1>My test message.</h1></body></html>",
    "created_at": "2021-02-26T23:32:08.000000Z",
    "updated_at": "2021-02-26T23:32:08.000000Z",
    "attachments": []
}

我怎样才能得到这个?

{
    "email": "me@me.com",
    "subject": "My Subject",
    "body": "<html><body><h1>My test message.</h1></body></html>",
    "attachments": [
        {
            "url": "http://127.0.0.1:8000/storage/foo.jpg"
        }
    ]
}

我已经尝试过:

  1. return Email::with('attachments:url')->get(['email', 'subject', 'body']);

    SQLSTATE [42S22]:找不到列:1054“字段列表”中的未知列“url”(SQL:从“email_attachments”中选择“url”,其中“email_attachments”。“email_id”在(0)中)”

  2. return Email::with('attachments')->get(['email', 'subject', 'body', 'url']);

    “SQLSTATE [42S22]:未找到列:1054 '字段列表'中的未知列 'url'(SQL:从“电子邮件”中选择“电子邮件”、“主题”、“正文”、“url”)”

  3. return Email::with('attachments:url')->get(['email', 'subject', 'body', 'url']);

    “SQLSTATE [42S22]:未找到列:1054 '字段列表'中的未知列 'url'(SQL:从“电子邮件”中选择“电子邮件”、“主题”、“正文”、“url”)”

PS.:我正在使用Laravel 8.29.0

标签: laravelselecteloquentrelationshipeloquent-relationship

解决方案


在预先加载中加载特定字段时,您还应该选择用于映射相关记录的记录标识符。因此,在您的情况下,通过指向的(hasMany)外键发送电子邮件附件email_idEmail

请参阅急切加载特定列

使用此功能时,您应该始终在要检索的列列表中包含 id 列和任何相关的外键列。

Email::with('attachments:email_id,file_name')->get();

现在这将为您提供附件关系中的 2 个字段

"attachments": [
    {
        "email_id": 1,
        "file_name": "foo.jpg"
    }
]

因此,如果您需要url属性是自定义属性并且实际不存在于您的表中,您将需要欺骗查询构建器以生成包含url属性的此类 SQL 查询,最初为空值,然后当此查询输出由 laravel 数据转换时转换您url的访问器getUrlAttribute()将使用您的预期输出填充此属性

Email::with(['attachments' => function ($query) {
        $query->select(['email_id', 'file_name', \DB::raw('null as url')]);
    }])->get();

现在附件集合中将包含以下数据

"attachments": [
    {
        email_id": 1,
        "file_name": "foo.jpg",
        "url": "http://127.0.0.1:8000/storage/foo.jpg"
    }
]

推荐阅读