Collection working as expected in Tinker, but in Controller I get BadMethod

335 views Asked by At

I running into what I assume is so simple I am just overthinking it. I am running the following commands in my Tinker session and it is working as expected:

   $game = Games::find(1);
[!] Aliasing 'Games' to 'App\Models\Games' for this Tinker session.
=> App\Models\Games {#4386
     id: 1,
     user_id: 1,
     title: "Test Game",
     description: "This is a test of the game function",
     max_players: 8,
     deck: "default",
     type: "Golf",
     privacy: "Public",
     current_player: null,
     status: "pending",
     deleted_at: null,
     created_at: "2020-12-18 22:02:17",
     updated_at: "2020-12-18 22:02:17",
   }
>>> $game->players()->get();
=> Illuminate\Database\Eloquent\Collection {#4322
     all: [
       App\Models\User {#4384
         id: 1,
         name: "mark",
         email: "[email protected]",
         username: "user",
         role: null,
         email_verified_at: null,
         created_at: "2020-12-18 22:02:08",
         updated_at: "2020-12-18 22:02:08",
         pivot: Illuminate\Database\Eloquent\Relations\Pivot {#4168
           games_id: 1,
           user_id: 1,
         },
       },
     ],
   }

I have essentially placed that exact same code in my controller to pull a list of players in a game:

    $game = Games::find($game);
    $players = $game->players()->get();

and I am getting this when I hit the route:

Method Illuminate\Database\Eloquent\Collection::players does not exist.

I am confused why this wouldnt work in the controller if it works just fine in Tinker.

Thanks for the help!

2

There are 2 answers

0
patricus On BEST ANSWER

The normal usage for find() is to pass an id, and it will return a model instance for that id. However, if you pass in an array, or an object that implements \Illuminate\Contracts\Support\Arrayable, it will return a Collection of all the found instances.

Your controller code is this:

$game = Games::find($game);

If the $game value passed into find() here is an array, it will return a Collection of all the models found using the ids in the array.

Another sneaky issue here is if the $game value passed into find() here is a model instance. In this case, this statement will return a Collection, because models implement the Arrayable contract mentioned above.

So, when you call find() and pass in a model, it will call toArray() on that model, attempt to find records for every value returned in the array, and return a Collection of all the records found.

In either case, $game is now a Collection, and you'll get an error when you attempt to call $game->players(), since the players() method does not exist on collections.

0
IGP On
$game = Games::find($game);

If you pass a single id to find, it will return a Game model or null. But if you pass an array of ids (even if it's an array of length 1) or a Model (for some reason), it will return a Collection or null.

In your tinker session, try this and you'll have the same error thrown.

$game = Games::find(1);
$game = Games::find($game);
// or
$game = Games::find([1]);

$players = $game->players()->get();