1

I have simple controller which is fetching user from database and returning eloquent object to user.

class UsersController extends \BaseController {

    public function show($id)
    {
        $user = User::find($id);

        return $user;
    }

}

By default Laravel returns JSON. I would like instead to return RJSON so I've created following macro

Response::macro('rjson', function($collection)
{
    if($collection instanceof Illuminate\Database\Eloquent\Collection)
    {
        $collection = $collection->toArray();
    }

    $collection = Dmitrirussu\RJson::pack($collection);
    return \Response::make($collection);
});

It's working fine but it's inconvenient. I would like to make the same behavior with return $user from controller. I tried to extend json method in Respone Facade without rewarding effect. How I can achieve this?

Mateusz Nowak
  • 4,021
  • 2
  • 25
  • 37
  • Because there is a difference between `return $user;` and `return Response::json($user);`, the first call [toJson method](https://github.com/laravel/framework/blob/master/src/Illuminate/Support/Collection.php#L718), call `dd(debug_backtrace());` on it to generate a PHP backtrace. – Razor Jul 26 '14 at 17:58

2 Answers2

3

I'm going to propose a solution here. Disclaimer here that I have not tested the code, but the approach is very similar to what I'm looking to implement in the next few days (refomatting Eloquent results for Ember.js compatibility).

Basically what Taylor Otwell (creator of Laravel) suggested is:

Check out the "newCollection" method on the Eloquent\Model class. Override that in a base model and return a custom collection that extends Illuminate\Database\Eloquent\Collection. Then in that Collection extension override the toArray method to format stuff how you want.

So in theory you could do these three steps:

  1. Create a subclass of Illuminate\Database\Eloquent\Collection and override the toArray() to do RJSON formatting before returning. Let's say call it RJsonCollection.
  2. Create a subclass of Illuminate\Database\Eloquent\Model to override newCollection() method to use your new RJsonCollection subclass. Let's say call it BaseModel.
  3. Convert your models to extend the new BaseModel.

Something like this:

app/models/RJsonCollection.php:

<?php

class RJsonCollection extends Illuminate\Database\Eloquent\Collection
{
    public function toArray()
    {
        $parentArray = parent::toArray();

        return Dmitrirussu\RJson::pack($parentArray);
    }
}

Note that I'm assuming based on your question's code that Dmitrirussu\RJson::pack($parentArray) will return a collection/array. You may need to adjust the code above and make sure it returns the proper RJSON array.

app/models/BaseModel.php:

<?php

class BaseModel extends Eloquent
{
    public function newCollection(array $models = array())
    {
        return new RJsonCollection($models);
    }
}

app/models/UserModel.php:

<?php

class UserModel extends BaseModel
{
    // ...
}

And of course the usual composer dump-autoload and stuff when creating new classes. You may notice that I put all classes above in the models folder and no namespacing. Not the best practices but they do help keeping the example simple and straightforward.

Unnawut
  • 7,500
  • 1
  • 26
  • 33
  • Nice. You don't need those `use` statements btw. And `BaseModel` also needs custom `toArray()` method in order to fulfill OP's requirement. – Jarek Tkaczyk Jul 29 '14 at 19:07
  • @deczo I learnt that "You need the "use" statement in both files. Use is a file-level keyword and isn't affected by inheritance." from http://stackoverflow.com/questions/11794901/php-does-extending-class-need-another-use-to-call-namespace, could you re-confirm? – Unnawut Jul 29 '14 at 19:13
  • @deczo for the second part of your comment, I've added a small note of my assumption under RJsonCollection example right about the same time as you commented :p – Unnawut Jul 29 '14 at 19:14
  • 1
    About the `use` - it's per file but only if you use those classes in that particular file. Otherwise the deeper in the inheritance hierarchy you get, the longer that list would be. Just check the `Model.php` and any of your model files ;) – Jarek Tkaczyk Jul 29 '14 at 19:24
  • Yup that makes sense. Answer amended. Thanks! – Unnawut Jul 29 '14 at 19:29
0

You may also want to look at Fractal's Transformers as a way to create re-usable transformations for your data in a more generic manner:

http://fractal.thephpleague.com/transformers/

Seb Barre
  • 1,597
  • 1
  • 12
  • 17