0

i have two tables. School and Student.

When i fetch all Students

Student::all()->toJson();

I get a response with

[
  {
    "id": 1,
    "school_id": 1,
    "name": "Jhon"
  }
]

But actually i would like to receive

[
  {
    "id": 1,
    "school": {
        "id": 1
    },
    "name": "Jhon"
  }
]

I know i could do

Student::with(['school']);

But then it do an extra query to school table, which is not necessary, as i only need the school id, and not all other attributes.

rmobis
  • 26,129
  • 8
  • 64
  • 65
Guilherme Miranda
  • 1,052
  • 2
  • 10
  • 19
  • 1
    You'll have to specify your own `toJson` method in order to get this kind of transformation. If Eloquent did this kind of transformation on an underscore, and there was a field `full_name` should eloquent do `[ { "full": { "name": "John Smith" }, "id": 2 } ]`? – alexrussell Nov 11 '14 at 11:36
  • No it shouldn't. It should do `method_exists(Student, 'full')` first. For example, on `school_id`, we have `public function school() { return $this->belongsTo('\Project\Model\School'); }` – Guilherme Miranda Nov 11 '14 at 13:25

2 Answers2

1

You can override the toArray method in your model. The same thing also would be possible with toJson, but toArray gets called by toJson and in my opinion its nicer to do such stuff in toArray

class Student extends Eloquent {

    public function toArray($options = 0){
        $this->school = ['id' => $this->school_id];
        unset($this->school_id);

        return parent::toArray($options);
    }
}

Edit

To keep the model the same and only change to output (which is probably better)

public function toArray($options = 0){
    $array = parent::toArray($options);
    $array['school'] = ['id' => $array['school_id']];
    unset($array['school_id']);
    return $array;
}
lukasgeiter
  • 147,337
  • 26
  • 332
  • 270
  • It works but i'm not comfortable with modifying the model itself – Guilherme Miranda Nov 11 '14 at 13:58
  • That looks better. I can mix your answer with @Raphael_'s to make it generic. But i'm trying to figure if it's better to create a base model and override toArray() or use the collection way that he said. – Guilherme Miranda Nov 11 '14 at 14:09
  • Both ways work. If you're never gonna need the original json, I'd recommend extending the (Base) model itself. And you'll need to extend the model anyway to overwrite the `newCollection` method. So, yeah... – rmobis Nov 11 '14 at 14:14
  • I personally don't see an advantage of using `transform` over `toArray` (but maybe I'm missing something) However the cool thing about `toArray` is, that it is also used by a lot of Laravel code. For example `Response::json($students)` will automatically call `toArray` – lukasgeiter Nov 11 '14 at 14:16
1

You can use Illuminate\Support\Collection's transform method for a more automated solution:

$students = Student::all();

$students->transform(function($el) {
    $el = $el->toArray();

    foreach ($el as $k => $v) {
        if (substr($k, -3) === '_id') {
            $el[substr($k, 0, -3)]['id'] = $v;

            unset($el[$k]);
        }
    }

    return $el;
});


return $students;

If you're going to call this multiple times (which I assume you will), the best would be to extend Illuminate\Database\Eloquent\Collection and add a method to do that. Then edit the newCollection method on your base model to use your new extended class.

rmobis
  • 26,129
  • 8
  • 64
  • 65