Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

casc-or's avatar

Eloquent Query Not Converting Correctly To Json

I have three models

Member

    public function getFullNameAttribute() 
    {
        return $this->lastname . ", " . $this->firstname;
    }

    public function memberships() 
    {
        return $this->hasMany('App\Membership');
    }

Membership

    public function member() 
    {
        return $this->belongsTo('App\Member');
    }

    public function club()
    {
        return $this->belongsTo('App\Club');
    }

Club

    public function memberships()
    {
        return $this->hasMany('App\Membership');
    }

I am trying to return a json response of all the members who belong to a given club whose memberships meet certain conditions;

    public function members($id)
    {

        $members = Member::whereHas('memberships', function ($query) use ($id) {
            $query->where('club_id', '=', $id)
                ->where('date_expiry', '>=', Carbon::now())
                ->where('member_type_id', '=', 1);
        })->get()->sortBy('full_name')->pluck('id', 'full_name' );

        return Response::json($members );

    }

Route for this is

Route::get('clubs/members/{club}',['as'=>'clubs.members','uses' =>'ClubController@members']);

Note that 'full_name' is an accessor, not a database field.

I've noted that there is nothing I can see in inspecting the $members array after the query that names the elements, nor is there anything in the json response.

If I dd($members) I get

Collection {#255 ▼
  #items: array:79 [▼
    144 => "Doe, John"
    221 => "Doe, Jane"
  ]
}

and in json I might expect to see

{"members":[{"id":"144","full_name":"Doe, John"}, 
 {"id":"221","full_name":"Doe, Jane"}]}

but I actually get

{"144":"Doe, John",
"221": "Doe, Jane"}

If I do a simple

$members = Member::all();

then I get output which appears as "fully formed" json like

[
{
"id": 144,
"join_date": "2009-01-01",
"username": "john.doe",
"email": "[email protected]",
"firstname": "John",
"lastname": "Doe",
"initials": "",
"address": "123 Anywhere St",
"city": "Somewhere",
"province": "ON",
"postal_code": "A1B 2C3",
"country": "CA",
"created_at": "2016-11-11 11:44:03",
"updated_at": "2016-11-11 18:38:28"
},

How can I get correct json output returned from my controller with this query?

Thanks in advance!

0 likes
15 replies
khaledSMQ's avatar

@casc-or i think since you are returning json trying to serialize the collection, have you tried to return array instead of json

 $members = Member::whereHas('memberships', function ($query) use ($id) {
            $query->where('club_id', '=', $id)
                ->where('date_expiry', '>=', Carbon::now())
                ->where('member_type_id', '=', 1);
        })->get()->sortBy('full_name')->pluck('id', 'full_name' );

return $members->toArray();

casc-or's avatar
return $members->toArray();

returns the same result, actually.

Note that the collection from dd($members) if placed immediately after the query is run to create $members has no tags or field information either.

casc-or's avatar

FatalErrorException in ClubController.php line 96: syntax error, unexpected '=>' (T_DOUBLE_ARROW)

tykus's avatar

The solution is missing the array brackets []

return json([
    'members'=>$members->toArray()
]);
casc-or's avatar
        return json([
            'members'=>$members->toArray()
        ]); 

gives

FatalErrorException in ClubController.php line 94: Call to undefined function App\Http\Controllers\json()

khaledSMQ's avatar

this should solve the issue

return json_encode(['members'=>$members->values()->all()]);
casc-or's avatar

I'm pretty much convinced that the problem here stems from the result of this statement

 $members = Member::whereHas('memberships', function ($query) use ($id) {
            $query->where('club_id', '=', $id)
                ->where('date_expiry', '>=', Carbon::now())
                ->where('member_type_id', '=', 1);
        })->get()->sortBy('full_name')->pluck('id', 'full_name' );

which just returns a set of key => value pairs with no identifiers.

There must be some means of getting the same structure of output as Members::all();

casc-or's avatar
return json_encode(['members'=>$members->values()->all()]);

doesn't solve it. That gives something which isn't recognized as json (because the Chrome add-in which formats json just shows it as text) which looks like this

{"members":[2628,2629,10197,2630,10201,2632, ... 2315,2316,2323,11628]}
khaledSMQ's avatar

i still can't understand where is the issue ,


    return json_encode(['members'=>$members->transform(function($member){

        return [ 'id' => @$member['id'],'full_name'=>@$member['full_name']];

    })]);

casc-or's avatar

Perhaps I'm sending the discussion in the wrong direction here.

The end goal is to feed this;

    function loadPrimaryClubMem(club) {
        var primaryMem = $('#primary_membership_id');
        alert('loadPrimaryClubMem with ' + club + ' primary ' + primaryMem.val());
        $.ajax({
            url: "clubs/members/" + club,
            type: "GET",
            dataType: "json",
            success: function (data) {
                primaryMem.empty();
               $.each(data, function (key, value) {
                    primaryMem.append("<option value='" + key + "'>" + value + "</option>");
                });

            },
            error: function (data) {
                alert('was an error with data');
            }
        })
    }

This jQuery script gets called by a change event on a form select element. I'm then trying to populate another select on a form. I'm inspecting for a value from a select (club which is an id) and passing that to the url as a parameter. When I get the response back I'm wanting to empty and populate a select with the key / value pairs in the response.

So maybe what is being returned is the expected, but what I'm doing to handle it in jQuery is wrong?

I'm going to try a simpler query and just pluck from the Member::all() and see if that is read correctly.

khaledSMQ's avatar

i think there the index in ajax response will be ( 'members') and the key is full json array means you need to parse the data to get members object then do loop to start append in your dom

success: function (data) {
        var json_obj = $.parseJSON(data);//parse JSON

                primaryMem.empty();
               $.each(json_obj.members, function (key, value) {
                    primaryMem.append("<option value='" + key + "'>" + value + "</option>");
                });

            },
casc-or's avatar

I'm going to try a simpler query and just pluck from the Member::all() and see if that is read correctly.

Nope. Makes no difference

khaledSMQ's avatar

you can check in dev tool if you are using google chrome, and console log the response of look at the get response results to know what you are receiving from server.

casc-or's avatar

So, the first thing is that my url in the $.ajax call is wrong - it needs to be

$.ajax({
            url: "/clubs/members/" + club,

The original version without the preceeding slash is a relative path and was calling www.example.com/members/clubs/members/{id} which is of course not what I want.

The next thing is that using pluck is a problem. If I change the query to not use pluck then use the syntax @khaledSMQ suggested

        return json_encode(['members' => $members->transform(function ($member) {

            return [ 'id' => @$member['id'],'full_name'=>@$member['full_name']];

        })]);

I get correctly formed json passed to my script. Finally, the jQuery syntax needs to be slightly different to that @khaledSMQ suggested.

success: function (data) {
                primaryMem.empty();
               $.each(data.members, function () {
                    primaryMem.append("<option value='" + this.id + "'>" + this.full_name + "</option>");
                });

            }

With the correctly formed response, it isn't necessary to .parseJSON the response, and it is necessary to use the 'this' keyword to reference the objects in the $.each statement.

So finally all this worked. Thanks to all for the clues and hopefully this TL;DR; thread will help someone else with a similar problem in future.

Please or to participate in this conversation.