【问题标题】:Include model relationships in json response using Eloquent and Laravel 5使用 Eloquent 和 Laravel 5 在 json 响应中包含模型关系
【发布时间】:2015-08-21 16:13:15
【问题描述】:

我有这样的模型设置:

<?php namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Upload extends Model {

    /**
     * The database table used by the model.
     *
     * @var string
     */
    protected $table = 'uploads';

    /**
     * The attributes excluded from the model's JSON form.
     *
     * @var array
     */
    protected $hidden = array('id', 'user', 'created_at', 'updated_at');

    public function mime() {
        return $this->hasOne('App\Models\Mime', 'mime');
    }
}

JsonSerialize() 被调用时,它会返回:

{
    "serverPath": "upload/2015/06/06/21/filename.jpg",
    "filename": "filename.jpg",
    "mime": "92"
}

这个92 引用另一个表(App\Models\Mime 代表)中的id,并带有一个与之关联的字符串type。我想用上述字符串替换这个92

{
    "serverPath": "upload/2015/06/06/21/filename.jpg",
    "filename": "filename.jpg",
    "mime": "image/jpeg"
}

这怎么可能?我在Upload 模型中使用protected $appends 尝试了一些东西,但我不确定我是否完全理解如何在模型中使用/访问关系。

澄清mimes 包含列 idtype,而表 uploads 包含一个名为 mime 的整数列在 mimes

中引用一个 id

【问题讨论】:

  • 我会在退货时说 ->with('mime') - 这有帮助吗?例如return \Upload::find(1)-&gt;with('mime');
  • 这样的东西会去哪里?你的意思是当我在另一个函数中返回 Upload 模型时? \App\Models\Upload::findOrFail(1)-&gt;with('mime') 在调用 JsonSerialize 时只返回 {}
  • 那你现在怎么退货?请用此更新您的问题,因为它可能会提供更好的上下文。另外,您能否尝试不调用 JsonSerialize 我认为所有 Eloquent 模型都会自动转换为 JSON,或者您可以在 Eloquent 模型上执行 -&gt;toJson()
  • 这是执行return new JsonResponse($data) 的API 的所有部分。所以或多或少在做return new JsonResponse(\App\Models\Upload::findOrFail(1))

标签: php laravel eloquent laravel-5


【解决方案1】:

好的,我相信这就是您要找的...

Upload.php(这里没有变化)

<?php namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Upload extends Model {

    /**
     * The database table used by the model.
     *
     * @var string
     */
    protected $table = 'uploads';

    /**
     * The attributes excluded from the model's JSON form.
     *
     * @var array
     */
    protected $hidden = array('id', 'user', 'created_at', 'updated_at');

    public function mime() {
        return $this->hasOne('App\Models\Mime', 'mime');
    }
}

那么你就有了你的 Mime 模型

Mime.php

<?php namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Mime extends Model {

    /**
     * The database table used by the model.
     *
     * @var string
     */
    protected $table = 'mimes';

}

如果你这样做是为了测试,我相信你应该看到类型

routes.php

Route::get('test', function() {
    $upload = \Upload::with('mime')->first();
    // return $upload //here would return it as JSON I'm pretty sure!
    return $upload->mime->type;
});

查看文档以获取有关预加载的更多详细信息:http://laravel.com/docs/5.0/eloquent#eager-loading

【讨论】:

  • 这将返回错误:Connection.php 第 624 行中的 QueryException:SQLSTATE[42S22]:找不到列:1054 'where 子句'中的未知列 'mimes.mime'(SQL:select * from mimes 其中mimes.mime (826))。如果有帮助,表mimes 包含列idtype,而uploads 包含一个名为mime 的整数列,该列引用mimes 中的id
  • 将关系更改为return $this-&gt;hasOne('App\Models\Mime', 'id', 'mime');,从而修复了 SQL 错误。虽然现在的问题是 $upload-&gt;mime 不是对象,而只是 id。
  • 查看@partricus 的答案,如果他还没有发布,我也会更新我的答案
【解决方案2】:

将关系命名为与表中的某个字段同名并不是一个好主意。这会在尝试访问关系而不是访问字段时导致问题(正如您所发现的)。

理想情况下,您的mime 字段应重命名为mime_id。这符合 Laravel 的约定,并且是更准确的字段名称。

但是,如果您无法更改字段名称,则应更改关系名称。

class Upload extends Model {
    protected $hidden = array('id', 'user', 'created_at', 'updated_at');

    public function uploadMime() {
        return $this->belongsTo('App\Models\Mime', 'mime');
    }
}

在上面的类中,关系名称现在是uploadMime。此外,关系从hasOne 更改为belongsTo。由于您的 uploads 表具有 mimes 表的外键,因此 Upload 模型属于 Mime 模型(并且 Mime 模型有一个/hasMany Upload 模型)。

现在,您的代码应该如下所示:

$data = \App\Models\Upload::with('uploadMime')->findOrFail(1);
return new JsonResponse($data);

这应该会给你一些类似的输出:

{
    "serverPath": "upload/2015/06/06/21/filename.jpg",
    "filename": "filename.jpg",
    "mime": "92",
    "uploadMime": {
        "id": 92,
        "type": "image/jpeg"
    }
}

使用 $appends 和属性访问器修改 JSON

如果您想更接近您在问题中提供的 JSON 输出,可以创建一个 mimeType 访问器并将其添加到 $appends 属性:

class Upload extends Model {
    // hide the mime field and uploadMime data
    protected $hidden = array('id', 'user', 'created_at', 'updated_at', 'mime', 'uploadMime');

    // add the mimeType attribute to the array
    protected $appends = array('mimeType');

    // code for $this->mimeType attribute
    public function getMimeTypeAttribute($value) {
        $mimeType = null;
        if ($this->uploadMime) {
            $mimeType = $this->uploadMime->type;
        }
        return $mimeType;
    }

    public function uploadMime() {
        return $this->belongsTo('App\Models\Mime', 'mime');
    }
}

这应该会给你一些类似的输出:

{
    "serverPath": "upload/2015/06/06/21/filename.jpg",
    "filename": "filename.jpg",
    "mimeType": "image/jpeg"
}

通过覆盖 toArray() 函数修改 JSON

或者,如果你真的希望JSON使用mime键,你可以直接修改toArray()方法:

class Upload extends Model {
    // hide uploadMime data, but not the mime field
    protected $hidden = array('id', 'user', 'created_at', 'updated_at', 'uploadMime');

    public function uploadMime() {
        return $this->belongsTo('App\Models\Mime', 'mime');
    }

    // override the toArray function (called by toJson)
    public function toArray() {
        // get the original array to be displayed
        $data = parent::toArray();

        // change the value of the 'mime' key
        if ($this->uploadMime) {
            $data['mime'] = $this->uploadMime->type;
        } else {
            $data['mime'] = null;
        }

        return $data;
    }
}

这应该会给你一些类似的输出:

{
    "serverPath": "upload/2015/06/06/21/filename.jpg",
    "filename": "filename.jpg",
    "mime": "image/jpeg"
}

【讨论】:

  • 这太完美了!非常感谢。
  • 这适用于 5.2 吗?
  • @user3779015 是的,这里的信息仍然适用于 5.2。
  • 不会访问getMimeTypeAttribute 访问器或toArray mutator 中的模型关系可能会产生n+1 问题?比如在Upload::all().
  • @Soulriser 简单地检索记录不会导致n+1 问题,因为在访问假字段之前不会调用访问器。但是,如果您检索记录,然后执行某些操作来访问所有这些记录的虚假字段,如果您没有急切加载依赖关系,这将导致 n+1 问题。您可以将依赖关系添加到 $with 模型属性,或者确保在访问假属性时始终预先加载关系。
猜你喜欢
  • 1970-01-01
  • 2016-03-25
  • 1970-01-01
  • 2022-01-09
  • 1970-01-01
  • 2016-02-26
  • 2021-03-23
  • 1970-01-01
  • 2016-02-06
相关资源
最近更新 更多