Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Include model relationships in json response using Eloquent and Laravel 5

I have a Model setup as so:

<?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');     } } 

And when JsonSerialize() is called, it returns:

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

This 92 references the id in another table (which App\Models\Mime represents) with a string, type, associated with it. I would like to replace this 92 with said string.

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

How is this possible? I tried some stuff with protected $appends in the Upload model, but I'm not sure I fully understand how to use/access relationships from within the model.

Clarification The table mimes contains columns id and type, while table uploads contains an integer column called mime that references an id in mimes

like image 404
Lucas Raines Avatar asked Jun 06 '15 22:06

Lucas Raines


2 Answers

It is not a good idea to name a relationship the same name as one of the fields on the table. This causes issues (as you've found out) when trying to access the relationship versus accessing the field.

Ideally, your mime field should be renamed to mime_id. This conforms to Laravel's conventions, and is a more accurate name for the field.

However, if you don't have the ability to change the name of the field, you should change the name of the relationship.

class Upload extends Model {     protected $hidden = array('id', 'user', 'created_at', 'updated_at');      public function uploadMime() {         return $this->belongsTo('App\Models\Mime', 'mime');     } } 

In the class above, the relationship name is now uploadMime. Additionally, the relationship was changed from a hasOne to a belongsTo. Since your uploads table has the foreign key to the mimes table, the Upload model belongsTo the Mime model (and the Mime model hasOne/hasMany Upload models).

Now, your code should look something like:

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

This should give you output something along the lines of:

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

Modifying JSON using $appends and attribute accessors

If you wanted to get closer to the JSON output you've provided in your question, you can create a mimeType accessor and add it to the $appends property:

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');     } } 

This should give you output something along the lines of:

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

Modifying JSON by overriding the toArray() function

Or, if you really want the JSON to use the mime key, you can modify the toArray() method directly:

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

This should give you output something along the lines of:

{     "serverPath": "upload/2015/06/06/21/filename.jpg",     "filename": "filename.jpg",     "mime": "image/jpeg" } 
like image 110
patricus Avatar answered Sep 19 '22 18:09

patricus


Okay I believe this is what you're looking for ...

Upload.php (no changes here)

<?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');     } } 

Then you have your Mime model

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';  } 

if you do this for a test I believe you should see the type

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; }); 

Check docs for further details on eager loading: http://laravel.com/docs/5.0/eloquent#eager-loading

like image 45
haakym Avatar answered Sep 19 '22 18:09

haakym