Redirect guests to login0:00
Okay, let's continue and work on actually voting. So before we do that, I want to make sure if the User clicks on the vote button and they're not logged in, I want to redirect to the login page. Maybe later on, we'll change it to a modal because that's a bit of a better user experience. But for now, I'm okay with redirecting. So again, there's two places, this page and also the show page. So let's start with a test. So let's go to vote, show page test. And let's make a new one here. Actually, let's just duplicate one of these ones here, since some of the setup is already
And let's make a new one here. Actually, let's just duplicate one of these ones here, since some of the setup is already done for us. Okay, so let's call it User who is not logged in is redirected to login page when trying to vote. Okay. And all of this is fine. Starting to get very repetitive. So maybe we'll switch it to a setUp method in a future video. So we don't need this.
So maybe we'll switch it to a setup method in a future video. So we don't need this. And we don't want to be logged in. So we can remove this and just put this on the line above. Let's call the vote method, which we haven't implemented yet, but we will in a second. And there's no parameter there. And we can use the assertRedirect method here. And we can use the route helper and say it's redirected to the login page. Okay. So this should fail.
Okay. So this should fail. It does. Let's take a look at our error here. It says public method vote not found. So let's start with that. So that should be on the show, or the IdeaShow Livewire component. So let's put it right here. I haven't hooked it up to the UI yet. But we'll do that after we have the test passing.
I haven't hooked it up to the UI yet. But we'll do that after we have the test passing. So say vote, or sorry, method, vote, okay. And the logic is pretty straightforward. So let's go ahead and do it. So if not logged in. So if not auth check, then redirect or return redirect to the login page. So route login. Okay. And that's it.
Okay. And that's it. I believe. Let's see if our test passes now. And it does. Cool. So I'm going to go ahead and paste it in the IdeaIndexTest as well, because it's basically the same thing. Okay. So I pasted it in that test.
It looks different here than it does on the index page. Obviously, it's here on the index page on the left side. So maybe we'll fix that in a future video. So my thought was, I was eventually going to add more functionality to these components. And I might do it here for setting the status and maybe even replying. But now I'm thinking that should probably be in its own component. Anyways, we'll fix that when we get there. So let's run that test. I don't think I ran it. Okay, so it passes.
Hook vote button clicks3:23
I don't think I ran it. Okay, so it passes. And let's try it in the browser here. So refresh, vote, not logged in. Oh, yeah, we have to actually make it work in the browser. So idea index. So right here, we have these two cases, one for mobile and one for responsive. So we just have to do multiple cursors here. wire:click equals vote. Actually, let's prevent the default as well.
wire:click equals vote. Actually, let's preventDefault as well. Okay, and it's the same for IdeaShow right here. So same thing. Let me put multiple cursors here as well. wire:click.prevent equals vote. Okay, so now let's try it in the browser. We're not logged in. Redirect to log in. Same with the index page.
Redirect to log in. Same with the index page. Refresh. Oh, yeah. So this appears twice. We have to do for mobile and for desktop. So this should appear somewhere else hasVoted. So yeah, right here. So I'm going to do the same thing here. wire:click.prevent = vote.
So I'm going to do the same thing here. wire:click.prevent = vote. And the same for the Idea show for hasVoted. So we have to do it here as well. wire:click.prevent = vote. Okay, so now it should work on desktop and mobile. Okay. Double check mobile does. And same for the show page. Mobile works and desktop works.
Implement vote logic5:06
And same for the show page. Mobile works and desktop works. Okay, now let's work on actually voting. So we have a few options here. We can put it on the Livewire component directly. So we already have that. Where is it? IdeaIndex or IdeaShow. We already have this vote function here, and we can make use of this and just put the logic in here.
We already have this vote function here, and we can make use of this and just put the logic in here. We can put it on the User model and have something like User vote for an Idea that would work. Or we can put it on the Idea model and say something like given an Idea, vote for it with this User. So either way works fine. I'm going to stick to this way on the Idea because the User model tends to get very cluttered and very large as your app grows. For example, if you want to vote on something else that's not an Idea, then you would need another method called User vote for like something else, like a Comment or something or vote.
For example, if you want to vote on something else that's not an Idea, then you would need another method called userVote for like something else, like a Comment or something or vote for Comment and then pass in the Comment. So yeah, I'm going to stick to putting it on the Idea. So let's do that. Get rid of this. So as we've been doing, I'm going to start with a test again. So let's go to idea, actually, no IdeaTest. And let's make a new one here. I'm going to copy this or duplicate this.
And let's make a new one here. I'm going to copy this or duplicate this. Let's say User can vote for Idea. And again, this is fine. This is fine. And get rid of this. And we don't need this as well. So the API I had in mind was ideaVote. So vote for this Idea with this User. And we have that User and Idea above.
So vote for this Idea with this User. And we have that User and Idea above. And after the vote, we want to check if the correct entry is in the votes table. So we can do this assertDatabaseHas. And that would work. But I'm going to make use of the method that we did in the last video. And that is ideaIsVotedByUser $user. So we can assertTrue. So this assertTrue that this is the case here after we run this method. And we can also assertFalse before we run it.
So this assertTrue that this is the case here after we run this method. And we can also assertFalse before we run it. So let's duplicate this, move it up and assertFalse. Okay. So let's try this out. So this should fail. You can see undefined method vote. So let's go ahead and create that method on the Idea model. Put it down here. And it's a vote.
Put it down here. And it's a Vote. And that takes in a User. And all we have to do is create a new entry on the votes table. So we can do Vote::create, just move this up. And we have an idea_id. So we are on the Idea model. So that's just this ID. And the user_id is the User being passed in. Okay.
And the userId is the User being passed in. Okay. See if this works. Let's run that test again. And it's still failing. And saying ideaId is not fillable. Okay. So let's go to our Vote model. And it's just say guarded to an empty array. I have a snippet for that.
And it's just say guarded to an empty array. I have a snippet for that. Okay. Let's try that again. Okay. And now we are passing. Okay. So let's write another test for removing the vote or unvoting. So we can either vote or unvote for our Ideas here. So if it's already voted, we can unvote or remove the vote.
Add unvote functionality8:52
So we can either vote or unvote for our ideas here. So if it's already voted, we can unvote or remove the vote. So let's add that functionality back to our test. Let's duplicate this one. Let's say User can remove a vote for Idea or unvote if you want to use that language. And we don't need the second User to remove it for up here as well. Now we need an existing vote so we can delete that vote from the votes table. Let's grab that up here. So this is an existing vote for the existing Idea and the existing User here. So let's add that.
So this is an existing Vote for the existing Idea and the existing User here. So let's add that. Okay. And now this one should be assertTrue because the vote does exist. And then we are going to call removeVote. And then this one should be false because we just removed it. Okay. So let's run this test. removeVote is not defined. Let's add that in Idea.
Remove vote is not defined. Let's add that in Idea. So let's put after vote here. Say removeVote. And we have to find this entry in the votes table and remove it. So let's do that. So we can do Vote::where say idea_id is idea or $this->id because we're on the Idea model. We can do andWhere so another where clause user_id is $userId the user being passed in. Okay. And let's grab the first one.
Okay. And let's grab the first one. So first and it's deleted. So first delete. Okay. So let's save that to run the test again. And we are passing. Cool. Okay. Now let's make it work in the actual browser.
Wire voting in Livewire10:52
Okay. Now let's make it work in the actual browser. So I am on the show page here. Let's log in. Okay. And now we should be able to make use of that vote and removeVote method. So let's go ahead and do that. Actually this is the index page. Let's start with the show page. So we have two cases either to add a vote or removeVote.
Let's start with the show page. So we have two cases either to add a vote or remove a vote. And that depends on if the User voted for this Idea. So we have that piece of state already in our Livewire component. So this one's IdeaShow. So after this case here, when we're checking if the User is logged in, we can have another conditional. So if hasVoted. So this is this case that we're on right now. These are already voted.
So this is this case that we're on right now. These are already voted. So we have to remove the vote. So let's do that. Okay. We can do this Idea. The Idea is being passed in and we can just make use of that method that we just created. So removeVote. And the User is the logged in User. So authUser.
And the User is the logged in User. So off User. Okay. And the other case is the other way around. So else this Idea vote instead of removeVote. And hopefully that does it. And what's the error here should be this hasVoted. Okay. You can refactor this to a ternary since it's a short conditional, but I'm just going to leave it because we have to add something here in the next video.
You can refactor this to a ternary since it's a short conditional, but I'm just going to leave it because we have to add something here in the next video. So let's add this to our Idea index as well. And let's save it and let's see if it works. So this is the show page. Actually sorry, I forgot to update the state. So let's remove this back to our show page. So we have to update two things here for this case, for both cases, actually. So for this case, when we're removing the vote, we have to decrement the votesCount votesCount minus, minus.
So for this case, when we're removing the vote, we have to decrement the votes count votes count minus, minus. Okay. And we also have to set hasVoted to false or true. Oh, no false. Yes. false. Okay. And the opposite for this case. So this is increment and this is true.
And the opposite for this case. So this is increment and this is true. Okay. So let's try that again. Let's grab this, put it to idea index, paste it in and let's see if this works now. Okay. So show page refresh on vote should go down and it does cool and vote should go up. Awesome. So it does work. Let's try it on the index page.
Add feature voting tests13:40
So it does work. Let's try it on the index page. So on vote should work, go down to 19 and it does vote should go back up to 20. Cool. Let's try one down here. I should go up to 1 and it looks like everything's working. Cool. So let's finish off by creating feature tests for voting. So the test that we wrote are just unit tests, which is fine, but I want to add two more feature tests for voting and for removing the vote as well.
So the test that we wrote are just unit tests, which is fine, but I want to add two more feature tests for voting and for removing the vote as well. So I'll just do it here for the show Livewire components of vote show page, and I'll just paste it into the index page as well. So let's grab this one. It's duplicated and let's name it User who is logged in can vote for Idea can vote for Idea. Okay. And we have to add the actingAs again, since now we're logged in actingAs User. Okay.
And we have to add the actingAs again, since now we're logged in actingAs User. Okay. But this one's online and this is all fine. We call the vote method. We don't need this anymore. So we are starting with no vote. So this is the case where we actually vote. So let's say assert set the votesCount should have went up by one. So let's do that should be six now hasVoted should be true. So let's add that hasVoted true.
So let's do that shouldBeSix now hasVoted true. So let's add that hasVoted true. Okay. And let's add one more here. Let's assertSee voted because that should show on the button after we voted. So assertSee voted and let's see if this works run this test and it does pass. Cool. So I'm going to add two more assertions here. Now I do want to use that assertDatabaseHas method just to make sure it is in the database.
Now I do want to use that assertDatabaseHas method just to make sure it is in the database. So this assertDatabaseHas, so we're checking the votes table and we want to check if it has the userId with userId and the ideaId with ideaId. Okay. Run that after the semicolon and we are good. One more assertDatabaseMissing before we actually vote. So up here, same thing, but it's the opposite case. assertDatabaseDoesNotHave this. Okay.
Assert database does not have this. Okay. Run that again. And we are good to go. So again, I'm going to paste this in the VoteIndex page test. Okay. So I pasted it in. The only thing I changed was this component here. Now it's IdeaIndex. Let's run this test and it is passing.
Now it's Idea index. Let's run this test and it is passing. Let's go ahead and run the entire test suite and everything is still passing. Cool. One last manual browser test. We're on the main page here. We can unvote and vote. The vote count goes up and down accordingly. And the same should be happening on the show page. And it is cool.
And the same should be happening on the show page. And it is cool. Let's go ahead and make our commit here. git add. What is this? Episode 20. git commit -m "Episode 20: Voting for ideas".
