Ady_Gould's avatar

Games, Scores, Players - from the scores side

I am trying to resolve a small problem using eloquent to retrieve the game and player who played that game, so they can be displayed with their score.

The idea is to store the scores for the different games played by different players to create a high score table. A player may be in the high score table multiple times.

ER:

Games -1-----m- Game_Player -1----m- Scores
                 |
Players -1----m--+

The player plays many games, the games have many players. Each game will have many scores

I want to show:

| Player | Game     | Score |
|--------|----------|-------|
| Jill   | Tumble   | 120   |
| Jack   | Tumble   | 100   |
| Jill   | Tumble   | 90    |
| Jack   | Tumble 2 | 130   |

Likewise if I select one record from the scores, I want to show just the player and game that the score belongs to.

Likewise, if I select a player, a list of games played and the scores for the games... etc etc etc

Any pointers? Thank you

0 likes
15 replies
Ady_Gould's avatar

Follow up: I have the following Models...

Game

namespace App;

use Illuminate\Database\Eloquent\Model;

class Game extends Model
{
    public function users()
    {
        return $this->belongsToMany(User::class);
    }

    public function scores()
    {
        return $this->hasMany(Scores::class);
    }

    public function developer()
    {
        return $this->belongsTo(Developer::class);
    }
}

User

class User extends Authenticatable
{
/* removed unrequired bits from this listing */

    public function userGames()
    {
        return $this->hasMany(UserGame::class);
    }

    public function scores()
    {
        return $this->hasManyThrough(Score::class, GameUser::class);
    }
}

Score

namespace App;

use Illuminate\Database\Eloquent\Model;

class Score extends Model
{
    //
    public function gameUser()
    {
        return $this->belongsTo(GameUser::class);
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function game()
    {
        return $this->belongsTo(Game::class);
    }
}

GameUser

namespace App;

use Illuminate\Database\Eloquent\Model;

class GameUser extends Model
{
    //
    public function user(){
        return $this->belongsTo(User::class);
    }

    public function game(){
        return $this->belongsTo(Game::class);
    }

    public function scores(){
        return $this->hasMany(Score::class);
    }
}

I am getting the score, but no user (thus no name) and no game (and thus no game name).

Ady_Gould's avatar

@DIEGOAURINO - hi @diegoaurino

I have already got the Users -- Games many-to-many working fine. The problem is the game_user to scores...

A user plays many games and has many scores A game is played by many users and has many scores

When I look up the scores, I'd like to see the user and game the score is for

Each score has an id, it also has a game_user_id Each Game_User has a game_id and a user_id

So for score ID 1 - could have a score of 250, and a game_user_id of 6 the game_user_id corresponds to user_id 3 and game_id 8 for example.

Ady

diegoaurino's avatar
Level 39

Your "userGame" method is returning a one-to-many, not?

In a many-to-many relationship, you need to "place another call to belongsToMany on your related model."

Check both the section "Defining The Inverse Of The Relationship" and "Retrieving Intermediate Table Columns" in the documentation.

Hope it helps.

Ady_Gould's avatar

Had a thought -

I could store the score in the game_user table.

The disadvantage to this is I have to do a little more on the query side to find the unique entries when i want to:

  • list just the games the user has played, and correspondingly,
  • list the users who play a particular game.
diegoaurino's avatar

Is this model for one single game that the user is subscribed?

Ady_Gould's avatar

@DIEGOAURINO - No

The user may have many games they are 'subscribed' to

each game may have many scores by that user

Ady_Gould's avatar

I was trying to be a little clever in removing duplicated data in the scores (as in remove the user id and game id, so we have one single user-game id that would take less storage space.

I think the Many to Many with a score added to the pivot table will be the easiest solution...

Still be nice to see the resolution to the Game has many Users, User has many Games, User-Games has many scores, Score has one User-Game (and thus Score has one user and one game)

Thank you for the help @diegoaurino .

1 like
Ady_Gould's avatar

OK All,

Thank you to *** @diegoaurino *** for their assistance and advice.

I have managed to get the problem solved by removing the extra table, and setting up the relationships, storing the scores in teh game_user table.

Not quite the solution I wanted, but it is working, even with excess queries being generated when the view is rendered.

I am sure it could be refactored around the user or game, and then sorted by the GameUser's id to put into order of score being added... but for now it is working.

Code

Models

User

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function games()
    {
        return $this->belongsToMany(Game::class)->withPivot(['user_id', 'game_id','score']);;
    }

    public function game(){
        return $this->belongsTo(Game::class)->withPivot(['user_id','game_id','score']);
    }
}

GameUser

class GameUser extends Model
{
    protected $table = 'game_user';
    public $incrementing = true;

    /**
     * @return mixed
     */
    public function user(){
        return $this->belongsTo(User::class);
    }

    /**
     * @return mixed
     */
    public function game(){
        return $this->belongsTo(Game::class);
    }
}

Game

class Game extends Model
{
    public function users()
    {
        return $this->belongsToMany(User::class)->withPivot(['user_id','game_id','score']);
    }

public function user()
    {
        return $this->belongsTo(User::class)->withPivot(['user_id','game_id','score']);
    }


    public function developer()
    {
        return $this->belongsTo(Developer::class);
    }
}```

----------------------------------------------------------------
Controllers
----------------------------------------------------------------

GameController
------
```php
class GameController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $games = Game::all();
        return view('games.index', compact('games'));
    }

    // ... rest of code removed
}

GameUserController

class GameUserController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
        $scores = GameUser::All();
        return view('scores.index', compact(['scores']));
    }

    // ... rest of code removed
}
1 like
diegoaurino's avatar

That looks good, @ady_gould !

Another approach is to create an instance from “Score” and then associate the other relationships with this new object. This new class will be responsible only for storing the points and associate the other objects. 

About the unneeded queries, there are two good lessons here about that: 1. https://laracasts.com/series/lets-build-a-forum-with-laravel/episodes/20 2. https://laracasts.com/series/lets-build-a-forum-with-laravel/episodes/21

I think both will give you a nice path to clean things up. 

Have a nice week!

1 like
KonnorRusso's avatar

With Eloquent, you can retrieve game-player data and scores efficiently. Just make sure to set up the right relationships between your tables. Btw, have you looked into games that pay real money? It could be a fun way to explore similar concepts. Good luck with your project!

Please or to participate in this conversation.