Project Goal Overview0:00
You know, I woke up this morning and I thought, I want to build a game. I'm not talking about a complex real-time strategy, or an RPG, or anything like that. I'm just talking about a simple game of tic-tac-toe. But I want it to be more than two players who have to sit next to each other and share the same mouse and keyboard. I want to enable truly online, real-time tic-tac-toe, using the power of Laravel, Reverb, Echo, and the rest of the toolset that Laravel provides. I actually don't think it will be that difficult. Now, there will be mistakes I'm going to trip over as I do this, but if you're interested in watching me explore building online tic-tac-toe in Laravel, then come along for the ride.
Scaffold App Setup0:41
Now, there will be mistakes I'm going to trip over as I do this, but if you're interested in watching me explore building online tic-tac-toe in Laravel, then come along for the ride. And let's get started. In order to get us off the ground, I've created a basic Laravel Breeze application, and essentially I've scaffolded it with Vue and Inertia. The reason for that is, well, first of all, I'm comfortable with Vue, very comfortable, and secondly, we are going to have plenty of real-time stuff going on, which I think a dynamic framework like Vue will make easier. The first thing I want to think about is how we match two people together, and we could go into lobbies and all the rest of it, but I actually think we could go a lot more simplistic.
Create Game Model1:30
The first thing I want to think about is how we match two people together, and we could go into lobbies and all the rest of it, but I actually think we could go a lot more simplistic than that. Let's start by making ourselves a model, php artisan make:model, and I'm going to call the model Game. We'll give ourselves a factory, migration, a policy, yeah, it would be good to limit whether or not you're allowed to join a game, for example, and then a resource controller for our Game. And this Game is essentially going to be a link between the two players, the player who creates the game and then the second player who will join a created game in order to be
And this game is essentially going to be a link between the two players, the player who creates the game and then the second player who will join a created game in order to be the noughts and the crosses that will actually fill the game out. So once we've got that configured, let's go ahead and open up our IDE. Let's start inside our game's migration. So we're going to first of all need a foreignId for playerOne, table, foreignId. We'll call it playerOneId. We'll make sure it's constrained. We'll say that it references the ID column on the users table, and let's just make it extra clear that if you cascade, it's going to delete.
We'll say that it references the id column on the users table, and let's just make it extra clear that if you cascade, it's going to delete. Then we can duplicate this for playerTwo. We'll update the column name, playerTwoId, and I want this one to be nullable, I think. The reason being is that this is going to form a basic matchmaking system. When you create a game, it will create a new game entry with a null playerTwoId. And then any games that have a null playerTwo will show up in the lobby as joinable games. So another player can click join and they become playerTwo. I think that should work for our use case.
So another player can click join and they become playerTwo. I think that should work for our use case. Let's go ahead and fill out our GameFactory. So playerOneId should be an instance of UserFactory and playerTwoId, I think by default can just be null. So it's an open game. Then we can jump into the Game model itself and we could start adding some relationships. So it belongs to relationship called playerOne. Let's add the return type here just to make PHPStorm happy. And yeah, we want to say that that belongs to a User class, but the column is player.
Let's add the return type here just to make phpstorm happy. And yeah, we want to say that that belongs to a User class, but the column is player_one_id. Then we can copy this relationship and we'll change this to player_two. And of course we want to change the column name here to player_two_id. Let's go ahead and migrate our database. And I'm also going to run php artisan db:seed just so that we actually have a User. Then I should be able to log into our application using the default user, which is test@example.com with password. Here we go.
Add Create Game Button4:03
with password. Here we go. We've hit our dashboard. So I think in this episode, what I want to do is just add a create game button on the dashboard that would essentially create a new Game model inside our database. All right. So here we are dashboard.blade.php. Here's that you're logged in div. Let's replace that. And I think I'd want to put an Inertia link in here.
Let's replace that. And I think I'd want to put an InertiaLink in here. Let's make sure to update the imports at the top to add InertiaLink. And then we'd set a route. The route would be equal to something like posts.store. I'll come back and set that up in just a moment. And if we set a method in Inertia, which I'll set to post, that will essentially make this a mini form, even though there are no inputs and it will submit it as though it were a form. Let's call this button create game.
form. Let's call this button createGame. See what that looks like. Oh, wow. That is ugly. I think we can fix that. Let's go to our primary button here and I'm going to steal all of the classes that come out of the box in Laravel Breeze from the primary button. And for now, seeing as we're being a little rough and ready, I'm just going to paste that directly on the link itself.
And for now, seeing as we're being a little rough and ready, I'm just going to paste that directly on the link itself. There we go. That's better. And then perhaps we could add a little bit of padding to maybe py-4 px-6. What does that look like? Yep. That's nice. So now we have a nice button that we can click to actually create a game. Of course, it doesn't do anything at the moment because we've not actually set up our route.
Implement Store Route5:24
So now we have a nice button that we can click to actually create a game. Of course, it doesn't do anything at the moment because we've not actually set up our route and controller. Why don't we start with our controller? So we'll jump into GameController. We're interested in the store endpoint. Here we go. And then inside here, I think this will be pretty simple. game = Game::create(...); and we'd want to pass in essentially the playerOneId. So playerOneId = request-user(); like so.
Game equals game::create, and we'd want to pass in essentially the playerOneId. So playerOneId is equal to request()->user() like so. And then we could return to the route of games.show passing in the correct game. Of course, this won't work because we need to actually set up the routes. So let's jump into web.php. And here inside the auth group, I could say Route::resource. I'm interested in a games resource, which points to the GameController class. And let's say that the only routes we're actually interested in for now are store and show. And then we can add to that as time goes on. Okay.
And then we can add to that as time goes on. Okay. So now hopefully we can go back to dashboard.view, and we could update route here to actually point to route games.store. And I think that should work. I imagine things are going to break. So let's try it on the front end. I'll hit create game. The POST method is not supported for route dashboard.
The post method is not supported for route dashboard. Okay. Maybe I just need to refresh the page there and try again. No apparently not. Okay. Maybe there's something wrong here then. Ah, idiot. That needs to be href instead of route. Forgive me. Okay.
Forgive me. Okay. Let's try that again. Refresh. Create Game. Add playerOneId to the fillable property. Of course. So let's jump into Game. And here at the top, we'll add our fillable property. And inside here, we can say playerOneId.
And here at the top, we'll add our fillable property. And inside here, we can say playerOneId. And at the same time, I'm going to add playerTwoId to the list as well. All right. Let's try again. This time, we're getting an integrity constraint violation. Hold in. Yep. I passed the entire User into the controller instead of just the ID. And again, okay.
I passed the entire User into the controller instead of just the ID. And again, okay. That actually didn't break. It didn't do anything, but it didn't break. Let's go to network and I'll hit create game. Yes. We get a 302. So it is working, but obviously we've not set up the routes.show endpoint to actually do anything. Certainly not to return an Inertia request.
Build Game Show Page7:45
do anything. Certainly not to return an Inertia request. Let's get that wired up next. For now, we could simply return inertia. I'm going to go games/show. And then let's find our pages endpoint here. I'm going to create a directory called games and inside there, we'll create the show.vue component. Let's create our template. And then inside we can use the authenticated layout.
Let's create our template. And then inside we can use the authenticated layout. And then let's just say testing so that we can actually check it all works. So let's test it out. We'll head back to the dashboard. Hit create game. And yes, there we go. We are redirected successfully to games/4 where we see that testing output we've just created. So it's now possible for an authenticated user to go ahead and create a game.
we've just created. So it's now possible for an authenticated User to go ahead and create a game. But of course, what we now need to facilitate is the ability to see open games in the dashboard and join one if one already exists. So let's tackle that in our next episode.
