تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Prevent Reputation Gaming0:00

Okay, I'd like to finish up at least the first wave of this reputation system, so that we can move on to other areas of managing an open source project. So at the moment, I'm in PHPStorm today, everything's passing. But yeah, we haven't taken into account situations like, well, a User earns points when they create a thread, yes. But what if they create that thread, get the points, then delete it, then create another exact thread, get more points, delete it, and then do it again? There's easy ways to game the system at the moment. Okay, so why don't we do an, oh, and actually, real quick, one change I made that's not in the last screencast is rather than using magic numbers like this, I've replaced them with

Deduct on Thread Deletion1:31

Let's dive in. We're going to do the inverse of some of these. So a User earns points when they create a thread, but they also lose points when they delete a thread. It's just how it's got to be. Well, how about this? We'll sign in a User. And by the way, if you're in P2P Storm, this is kind of a cool tip that I didn't know for a long time. Often you will reference a method, right?

a long time. Often you will reference a method, right? This sign in. But did you know you can just type the method name alone? And then when you hit enter, P2P Storm will add in the this portion. That'll save you a lot of time. Anyways, that's kind of cool. So if we continue, given we have a User that signed in, and that User creates a thread, well, yes, at that point, we've already proven that they should have this amount of reputation, and that passes.

well, yes, at that point, we've already proven that they should have this amount of reputation, and that passes. However, let's then delete the thread. And we can do that by submitting a DELETE request to the path to the thread. We already have a route set up for that. So route, delete. So if we submit a DELETE request to this endpoint, that will destroy the thread, essentially. So if we hit that, well then, I expect the user's reputation to be effectively zero at this point, because we reversed it. So let's say threadCreator.

this point, because we reversed it. So let's say thread creator. Now at this point, creator is cached, so it's not going to reflect any changes that have been made from this endpoint hit. So I'm going to get a fresh copy of that User, and then look at their reputation. And it should now be at zero, because you got 10 points, you deleted a thread, so now you should lose 10 points here, and that should bring us back to zero. So if we run it, of course it fails, and we need to make it pass. So let's see, I'm going to go to my Thread class, and yeah, to start, we're putting everything in these model events.

So let's see, I'm going to go to my Thread class, and yeah, to start, we're putting everything in these model events. And like I said, at some point we may change that if we feel this grossness, and it's guiding us towards a refactor like that. But until then, I'm going to choose the simplest way to get this test to green. So how about this? When you delete the Thread, well, maybe we could say reputation, and what would be the inverse of award? What would be something good? Now we could do stripAward.

because you're demoting the User. You're not necessarily demoting the reputation points. So back to the drawing board, reputation, why don't we say reduce. How about reduce the reputation for now, and then like I said, on GitHub, if you have some good ideas, we will switch that. So we reduce the User's reputation with user->decrement. All right, so if we give that another run, it's passing, so that works. All right, so let's get rid of that, and like I said, that's returning green. So now you can no longer use that loophole where you create a thread and delete it, create a thread, delete it, and suddenly you have double the amount of points.

Deduct on Reply Deletion5:33

So now you can no longer use that loophole where you create a thread and delete it, create a thread, delete it, and suddenly you have double the amount of points. Let's do another one here. A User earns points when they reply to a thread. All right, but when they delete that reply, exact same thing is going to be true. So a User loses points when their reply to a thread is deleted. So let's see. Once again, given we have a signed in User, and let's see, why don't we, let's create a reply. So quickly scaffold up a Reply for me where the owner of the Reply is the authenticated

a reply. So quickly scaffold up a reply for me where the owner of the reply is the authenticated User, and then sometimes I'll just have an intermediate assertion like this, not necessarily even to assert anything, but almost as a signal to the person reading it, like, okay, at this point, we expect the reputation to be equal to this amount. So like if we give that a run, yes, and we already have this under test up here. It's just kind of like a, okay, so based upon what we've written up until this point, the reputation should be this. But then when we submit a DELETE request to the reply, and actually that would be, what is it, replies, replies, and the reply ID, I think that's it.

But then when we submit a delete request to the reply, and actually that would be, what is it, replies, replies, and the replies ID, I think that's it. Anyways, yeah, if we submit a delete request there, it should hit a RepliesController, and then this method, that will destroy the reply. So then, yeah, once again, I'm going to assert that the user's reputation is back to zero. So reply owner, give me a fresh copy, and look at their reputation. I think that should do it. And by the way, yeah, self. It's fine, but I'm going to stick with that. Okay, so if we run that, it of course fails because we aren't reducing their reputation.

It's fine, but I'm going to stick with that. Okay, so if we run that, it of course fails because we aren't reducing their reputation. So let's see, once again, on reply, if we come down to our model event, we already have one set up, so this should be okay. As part of that, we're going to reduce their reputation. And if we give that a run, now we've closed that loophole. Okay, let's go a little bit faster since it's starting to get a little boring here. Now actually on that note though, one thing we haven't added is that a User earns points when their reply is favorited. I think that's something we decided we wanted to add.

Award Reply Favorited Points7:48

when their reply is favorited. I think that's something we decided we wanted to add. So let's go to reputation. Yeah, so maybe we would have one like replyFavorited, and we'll make that worth five points. How about that? Okay, so let's see. Right here, a User earns points when their reply is favorited. So let's see, given I have a thread and I'm signed in, or actually given there is a thread and we're signed in, well then, if we add a reply to that thread, and we'll set the

So let's see, given I have a Thread and I'm signed in, or actually given there is a Thread and we're signed in, well then, if we add a reply to that Thread, and we'll set the userId to the person who is signed in, and then the body is some reply, well, we want to say at this point, when we favorite that reply. So let's see what endpoint we could hit to favorite that. Here it is. So if you hit this URL with a POST request, we will favorite the referenced reply. So let's do that. This, submit a POST request to replies, and then the replyId, and then favorites. So now think about it.

This, submit a POST request to replies, and then the reply ID, and then favorites. So now think about it. At this point, the total amount of reputation should be reputation. Well you got two points for posting the reply, and now you should also have five points for having that reply favorited. So that's going to be my assertion. Total, and then let's say reply owner, and give me their reputation. All right, let's give that a run, and of course it fails. So we're expecting seven, but at the moment, we're not awarding the favoriting of a reply. So it looks like it hit that favorites controller, right, and this is fairly clean.

So we're expecting seven, but at the moment, we're not awarding the favoriting of a reply. So it looks like it hit that FavoritesController, right, and this is fairly clean. It just calls replyFavorites. So yeah, at some point, we want to do it here, something like this, where I say reputation, and we're going to award the authenticated user with this amount of reputation. So if we give that a run, ah, it's still failing. Hmm. Let's see. Are we hitting this point in that test? Yes.

Are we hitting this point in that test? Yes. And we are awarding the User reputation. Let's see. Right here. Get the authenticated user's reputation, five, but even if we have a fresh copy, seven. Okay. So you know what? My test probably didn't account for that. Give me a fresh copy of the owner and give me their reputation.

My test probably didn't account for that. Give me a fresh copy of the owner and give me their reputation. Okay. Yeah, that was right. We just had a cache issue there. Now also, a user, yeah, I know this one isn't the most fun video to watch. It's kind of the more mundane stuff, but that's okay. You can fast forward, do what you want. A user loses points when their favorited reply is deleted, or not deleted, but unfavorited, something like that.

Feature Tests vs Unit10:41

A user loses points when their favorited reply is deleted, or not deleted, but unfavorited, something like that. So here, maybe I can change this up. Why don't we just assume that we have a Reply where the userId is our signed in user, and then, yeah, you have a couple of ways to do this. So in this example, we're calling addReply directly on Thread to add it. You can also do more of an endpoint level test, where you say, hit the endpoint, that will add the reply. Of course, as with anything, there's pros and cons to each approach. Now one thing I do like about the endpoint approach is, what if later, this is not the

Of course, as with anything, there's pros and cons to each approach. Now one thing I do like about the endpoint approach is, what if later, this is not the way that I add a reply? Maybe I decide, eh, I didn't really need an addReply method, I only referenced it once or twice or something. Maybe I'll just do it in the controller, something like Thread::replies->create(). You know, I just do something like that directly in the controller. Well, if I make that change, which is a perfectly valid refactor that does not break the code in any way, it will, in fact, break the test, because now we assumed that this is how the system will always add a reply, but we changed it.

in any way, it will, in fact, break the test, because now we assumed that this is how the system will always add a reply, but we changed it. So the actual functionality is still good, it works, but the test is failing, and that means that the test is a little brittle there. Now if you go with the feature test, it's a little more zoomed out, I would say, however it does allow for any kind of refactoring of that sort, and it will still properly return green. So if you want to see that approach, just for a demo, you could say, all right, well, if we submit a POST request to favorites the reply, so you'll remember that was replies, the ID of the reply, and then favorites.

if we submit a host request to favorites the reply, so you'll remember that was replies, the ID of the reply, and then favorites. If we hit that endpoint to favorite the reply, well, at that point our total should be, what, the reputation for hosting the reply, and then the reputation for favoriting the reply. And we can do our assertion here, so let's do that. Give that a run. Of course that's going to pass. However, next we're going to submit a delete request to, once again, replies, and you know what, we should probably make this a named route. But anyways, replies, favorites.

Deduct on Unfavorite13:00

what, we should probably make this a named route. But anyways, replies, favorites. If we make a DELETE request there, well now we should remove the reputation. So at that point the total should be reputation plus reply favorited minus, just if we want to be incredibly explicit about what we expect to have happen. We have this, and then we added a favorite, and then we removed a favorite, and that should be our new total. So now we could run our assertion again, and let's give this a run. It fails because we aren't reducing the reputation. So let's go to, hmm, the method was called favorite, and there should be one called unfavorite

It fails because we aren't reducing the reputation. So let's go to, hmm, the method was called favorite, and there should be one called unfavorite right here. All right, so let's say reputation, reduce, like so. We give that a run, and now that's passing. But now, again, I want you to see how we keep adding this. The reality of things like this is they're perfectly fine until, like I often say, it starts to feel gross, and that's when you change the structure a little bit. So in this case, we're unfavoriting the reply, and we do that, but now we're also doing a side effect of unfavoriting the reply, which is reducing reputation.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟