4

Is there an elegant way to remove NULL values from an Eloquent Object? My object is nested with relationships. This particular call can be 1000s of lines long, so my main reason for trying this is to save bandwidth for the user, but server performance is also a consideration.

My code:

$data['locations'] = Location::with('address')->where('user_id', '1')->get();

return Response::json($data);

I experimented with Mutators, but unless I'm mistaken Mutators don't have power over the object key, just the value.

I also tried and failed to use array_filter like these:

Any PHP function that will strip properties of an object that are null?

How to remove empty associative array entries

EDIT As requested,

{
    "status": "ok",
    "locations": [
        {
            "id": "1",
            "latitude": "12.239107980271",
            "longitude": "109.19479025725",
            "user_time": "",
            "transport": "Bus",
            "title1": "",
            "title2": "",
            "address": {
                "town": "Nha Trang",
                "country": "Vietnam",
                "address": "36-44 Hùng Vương, Lộc Thọ, Nha Trang, Khanh Hoa Province, Vietnam"
            },
            "altitude": {
                "altitude": "10.006237983704"
            },
            "timezone": {
                "offset": "25200"
            },
            "forecast": {
                "icon": "",
                "high_temp": "",
                "low_temp": ""
            }
        },
        {
            "id": "2",

Desired response:

{
    "status": "ok",
    "locations": [
        {
            "id": "1",
            "latitude": "12.239107980271",
            "longitude": "109.19479025725",
            "transport": "Bus",
            "address": {
                "town": "Nha Trang",
                "country": "Vietnam",
                "address": "36-44 Hùng Vương, Lộc Thọ, Nha Trang, Khanh Hoa Province, Vietnam"
            },
            "altitude": {
                "altitude": "10.006237983704"
            },
            "timezone": {
                "offset": "25200"
            }
        },
        {
            "id": "2",

As you can see, I could simply loop through the whole lot and remove any keys - or keys of keys - without values. I was hoping Laravel might provide a neat/fast way of doing the same.

I should add that technically only the latitude and longitude are required fields!

Community
  • 1
  • 1
daviestar
  • 4,531
  • 3
  • 29
  • 47

3 Answers3

3

3 possibilities:

  1. Write a response macro which cleans up your json data: http://laravel.com/docs/responses#response-macros
  2. Extend the Response class and implement your cleanup routine there. See this great tutorial for details how to do this: http://fideloper.com/extend-request-response-laravel
  3. Implement the jsonSerialize method in your model which will be automatically called when your model is converted to json and place your cleanup routines there. You can even go a step further and write your own Collection for your Location model. Depending on your data structure this can make things a little bit easier. A nice tutorial for this purpose can be found here: http://heera.it/extend-laravel-eloquent-collection-object

I personally would prefer option 3.) because the data modifications happens where it should happen - in your model.

But bottom line it really depends which solutions fits best to your project.

passioncoder
  • 909
  • 6
  • 10
  • 1
    Thanks, those articles were interesting. This overriding/extending of core functions strikes me as quite a severe solution and might affect the upgrade path. I will report back later on. – daviestar Sep 05 '14 at 20:14
  • Don't be afraid, laravel is made for this: http://laravel.com/docs/extending and http://laravel.com/docs/ioc. None of my suggestions is a "dirty hack", everything is perfectly following the convetions. – passioncoder Sep 05 '14 at 20:21
  • 3
    All these suggestions can be used. However there's another way, in opposition to **opt 3**, for model is not the place for this (this is not data modification but presentation layer). Use api transformers and keep it SOLID. Eg. http://fractal.thephpleague.com/transformers/ and https://github.com/dingo/api – Jarek Tkaczyk Sep 06 '14 at 08:02
2

First make a trait and add your custom validation then use in your each resource where you need

trait ResourceHelpers
{
    /**
     * Remove null values from Eloquent api resource
     * @param array $data
     * @return array
     */
    public function removeNullValues(array $data)
    {
        $filtered_data = [];
        foreach ($data as $key => $value) {
            // if resource is empty
            if ($value instanceof JsonResource and $value->resource === null) {
                continue;
            }
            $filtered_data[$key] = $this->when($value !== null, $value);
        }

        return $filtered_data;
    }
}

Then use it in your resource

class UserResource extends JsonResource
{

    use ResourceHelpers;

    /**
     * Transform the resource into an array.
     *
     * @param \Illuminate\Http\Request $request
     * @return array
     */
    public function toArray($request)
    {
        return $this->removeNullValues([
            "id" => $this->id,
            "first_name" => $this->first_name,
            "last_name" => $this->last_name,
            "phone" => $this->phone,
            "email" => $this->email,
            "balance" => $this->balance,
            'address' => $this->address,
            'city' => $this->city,
            'state' => $this->state,
            'zip_code' => $this->zip_code,
            'country' => CountryResource::make($this->whenLoaded('country')),
            "joined_at" => $this->created_at,
            "updated_at" => $this->updated_at,
        ]);
    }
}
Shibbir Ahmed
  • 91
  • 1
  • 6
0

I enhanced the removeNullValues to also ignore empty arrays and be recursive to handle nested arrays. Please check this one.

function removeNullValues(array $data)
{
    $filtered_data = [];
    foreach ($data as $key => $value) {
        if (is_array($value))
        {
            if (sizeof($value) > 0)
                $filtered_data[$key] = $this->removeNullValues($value);
        }
        else if ($value != null){
            $filtered_data[$key] = $value;
        }
    }

    return $filtered_data;
}
Mahmoud Fayez
  • 3,398
  • 2
  • 19
  • 36