Включите модельные отношения в ответ json, используя Eloquent и Laravel 5

У меня есть настройка модели:

<?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 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 представляет) с строкой, type, связанный с ней. Я бы хотел заменить эту 92 указанной строкой.

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

Как это возможно? Я попробовал некоторые вещи с protected $appends в модели Upload, но я не уверен, что полностью понимаю, как использовать/получать доступ к отношениям внутри модели.

Уточнение Табличные мимы содержат столбцы id и type, в то время как загрузка таблиц содержит целочисленный столбец с именем mime который ссылается на id в mimes

Ответ 1

Неплохо было бы назвать отношения такими же именами, как одно из полей таблицы. Это вызывает проблемы (как вы выяснили) при попытке получить доступ к зависимости от доступа к полю.

В идеале ваше поле 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. Так как ваша таблица загрузок имеет внешний ключ для таблицы mimes, то модель загрузки принадлежит модели Mime (а модель Mime имеет одну или несколько моделей загрузки).

Теперь ваш код должен выглядеть примерно так:

$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"
    }
}

Изменение JSON с использованием $appends и аксессуаров атрибутов

Если вы хотите приблизиться к выходу 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"
}

Изменение JSON путем переопределения функции toArray()

Или, если вы действительно хотите, чтобы 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"
}

Ответ 2

Хорошо, я считаю, что это то, что вы ищете...

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 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