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

Adding completion test0:00

Let's take a ride and refactor some of our quiz functionality. But first, real quick, I'd like to add a test. We have this functionality for a Quiz to check if it is complete. And that is included in our other tests. But still, I'd like to give it its own test. So right down here, let's do it right here. It knows if it is complete. So if I have a Question, and then we grab the next Question and we answer it with 4, then the Quiz should be complete at this point.

and then we grab the next question and we answer it with 4, then the quiz should be complete at this point. Because there's one question, and that question has been answered. So I could say this assertTrue quiz is complete. And again, we already wrote this functionality in the last episode, so I expect this to pass. And it does. Now, often when I'm dealing with Boolean checks like this, I will do a before and after. Because you never know, this may always return true.

I will do a before and after. Because you never know, this may always return true. In which case, you're not really testing what you think you are. So I always do the before and after. At this point, we have a quiz where no questions have been answered. So if I check if it's complete, that should return false. But as soon as I answer all of the available questions, it should change to true. And that works as well. Okay, so now at this point, I have all passing tests.

Planning refactor improvements1:21

And that works as well. Okay, so now at this point, I have all passing tests. Which means this is a good time to just have a look and figure out, are there ways I can improve this code? Have a look. Let's do the squint test. Kind of close your eyes a little bit and scan this. And figure out what grabs your attention. A few things grab my attention. First, this line right here is kind of long.

Introducing Questions collection2:00

And there's not absolutely anything wrong with that. But again, it's just a signal that the longer the line, the longer it's going to take you to parse what's going on here. So here's what I'm thinking. What if we introduce a class to represent a collection of questions? That way the quiz can then be responsible for delegating the higher level stuff. Here's what I mean. So in my src directory, I'm going to add a new class. And this will be Questions. Questions will contain the array of questions.

And this will be questions. Questions will contain the array of questions. And this will be a basic array. Like so. So if I now switch back to quiz, rather than storing an array of questions, we're going to graduate it to a dedicated class called Questions. So let's see if we can do it together. We'll start with Questions. Next, in my constructor, I will initialize it.

We'll start with questions. Next, in my constructor, I will initialize it. And then let's go one test at a time. It consists of questions. Did we break anything? We did. Cannot use the questions class as an array on the Quiz class at line 18. Okay. So we're trying to push to an instance of questions, and we can't do that.

So we're trying to push to an instance of Questions, and we can't do that. Let's change it. We're no longer dealing with a scalar type. Let's just say this questions add a new one. So notice we can kind of flesh out that domain a bit more when we're not sticking with a plain old array. So we have our first method here, add a new one. Like so. And this is where we will work with the array.

Like so. And this is where we will work with the array. Okay. Let's run the test again. It still fails. That's okay. Argument 2 must be countable. On QuizTest at line 18. Okay. So we did add a question,

Okay. So we did add a question, but now when I call the questions method on quiz, I expect that to have a count of 1. The only problem is this is now returning an instance of Questions, and that is not countable. If you want to treat an object as if it's countable, then what you can do is implement the Countable interface that php provides. This will want a count method. Give that reformat.

This will want a count method. Give that reformat. I'll put that at the end. And that is where we will return the count of all the items associated with this instance. Cool. Give it a run, and it passes. Great. So let's keep going one by one. It grades a perfect quiz, and it fails. Cannot use object questions as an array.

It grades a perfect quiz, and it fails. Cannot use object questions as an array. Okay. So this is where we need to figure out what to do here. How do we go to the next question? Well, here's the cool thing. Notice how quiz was responsible for a lot of the nitty-gritty details. Could I just say instead return this questions, and you'd be responsible for telling me which one is next. So we give that a run.

and you'd be responsible for telling me which one is next. So we give that a run. We know it's going to fail because there is no next method on Question. We go here. We can add one, and we can figure out what the next question should be. So let's start by once again just returning questions[0]. Give it a run. Now we have a new error on quiz line 43. Ah, okay.

Now we have a new error on quiz line 43. Ah, okay. So this is where we're trying to grade it, but we first have to check if the quiz is complete. So again, what if we could replace this as our first step with, well, answeredQuestions would be this->questions, and what if I treated it almost like a computed method? Give me only the items from that array that are answered, and then for totalQuestions, I could say this->questionsCount. Okay, let's give it a run.

and then for total questions, I could say this questions count. Okay, let's give it a run. It's going to fail because there's no answered method on questions. So let's get rid of that. We'll get rid of that, and on our Questions class, we'll now get our subset answered, and here's what we had in the previous class. Let's have a look. Filter the questions down to only the questions that have been answered, but rather than returning an index,

Filter the questions down to only the questions that have been answered, but rather than returning an index, why don't we just return the array itself, or we could return a new instance of Questions if that makes sense, but right now, I'm just returning a subset of the array and give that a reformat. Now, if I switch back, give it a run again, it still fails. Oh, and of course it does because we removed the count. So let's get the count of all the answered questions and then check if that's equal to the total count of the questions.

So let's get the count of all the answered questions and then check if that's equal to the total count of the questions. So we give that a run, and we're making progress. We're now down to here. So notice the issue. filter expects parameter 1 to be an array, but an object was given. So again, we're trying to array_filter on a questions instance. I don't want to have to use this at all anymore. So let's get rid of that entirely, and once again, just say give me the questions,

So let's get rid of that entirely, and once again, just say give me the questions, and how about give me only the ones that have been solved. Okay, we give that a run. It's going to fail because no solved method exists. So notice a lot of that logic can now move here. answered. So of course the difference is answered returns only the questions that have received an answer, right or wrong. solved returns only the questions that have been solved correctly.

that have received an answer, right or wrong. Solved returns only the questions that have been solved correctly. So let's do this again. Filter down the questions to only the ones that are solved. And is that the right method? Yeah. Okay, let's give the test a run again. And now it returns green. So a bunch of changes there, but because we ran our tests after each tweak, it ends up being fairly simple. Just find where the issue occurred, solve the problem, run the test again,

Implementing next-question logic8:24

So let's now come back and implement this logic correctly. So I think what we'll do, at least to start, is track the current $index, and then maybe that will initialize to 0. 0 meaning you haven't even started. We could then say, if not isset, look in that array and find one for the current $index. So in this case, that would be 0. Look at the first item. If that isn't set, then return false.

It does. Okay. So are we... Let's have a look here. Are we all passing? I think so. Last one. Good. Okay, so now if I run the full suite, everything looks good. So now let's see if I can get rid of this current property. Because if we have a look, yeah. Notice it's only being referenced in this single method alone, which itself might be a signal.

Using array pointer functions9:32

Notice it's only being referenced in this single method alone, which itself might be a signal. So let's play around with this. If I were to get rid of it, what would be another way we could write this that might be a little simpler? What if we used the array internal pointer helper functions that PHP provides? For example, quick little recap, because a lot of people don't know these. Let's say you have an array like this. Well, if you used the current function for items,

Let's say you have an array like this. Well, if you used the php current function for items, that'll give you the current item. Give me the pointer to the current item, which would be 1. And if you did again, it would keep giving you 1. But if you changed it to next, that advances it to the next item, which means it would now return 2. Then at that point, if you said give me the current item, that would then say 2. Now if you were to reset it, that would bring it back.

that would then say 2. Now if you were to reset it, that would bring it back to the very first item, so it would go back to 1. And I think there's an end as well, right? And that would take you to the last one. Finally, though, what if you call next and there is no next? In that case, it returns false. Okay, so let's see if we can use this. The next question to take, well, let's just get the current one. So I could say current this questions, next one in the deck,

So now I can get rid of all of that sort of confusing logic and that, and, yeah, a little bit cleaner. Okay, so now let's have a look. Our Collection can add a new Question. It can figure out what the next one to take is. It can return a subset of all the Questions that have been answered by the Person. It can return all the Questions that have been solved. You could even add additional things, like give me, I'm not going to keep this, but give me all the Questions.

Instead, we were missing something. And actually, a little tip here. Notice this is multiple lines because if we were to keep it like this, Prettier thinks that's too long, which is fair enough. You might just shorten this, like so, and if it reformats it, now it goes back onto one line. Generally, I'm cautious about these short variables. I don't like to do it. On the other hand, it's very, very clear that $q refers to a question in this case,

On the other hand, it's very, very clear that Q refers to a question in this case, so it's sort of a trade-off. If I keep it small and simple, I can turn this into a one-liner. If I make it a little more verbose, it turns into four lines. So yeah, it's just something to be thoughtful of. Be very cautious about these short variable names. I've seen too much code where you have Xs and Ys and Zs, and you have no idea what they refer to. But if it's something that can instantly be identified,

All right, so have a look here. I think this is looking fairly clean. Let's now look at Quiz. Quiz is looking much better as well. Notice these methods we had earlier, which is still useful, by the way. Even though we have a Questions class, I don't want to say quiz, questions, next. You can do it if you want, but it's nice to still offer these little helper methods,

You can do it if you want, but it's nice to still offer these little helper methods, like quiz, give me the next question. And all that does is it delegates. The only thing I see remaining, I think we could still clean this one up. And then grade, let's import Exception at the top. And then here, if you like, this could be $questions, and it's already countable,

Refining question evaluation14:27

Yeah, the only thing I might change is when you provide an answer, we set it there, and then we also update the correct property. I don't think we have to do that. Instead, why don't we do that here? The solved method will perform that calculation. Check to see if the given answer matches the solution. So then, we're going to return, and check if this was solved.

So then, we're going to return, and check if this was solved. And I don't know, is that good? Yep, that works as well. And I kind of like that approach more. So when the User calls answer on question, we record the answer, and then we do evaluate it, because we might want to know, if you say question, answer, or for,

you might add additional helpers, like, think of the terms you might use when talking about a quiz. The teacher might say, you may now begin. Okay, well maybe, you should have a method called begin. And that would return, in this case,

Right, so yeah, we can keep working on this as long as you want. Same thing here. We don't use that question variable, so we'll just do it like this. And give that one a run. Good. And you get the basic idea. So the only remaining thing I can imagine

And you get the basic idea. So the only remaining thing I can imagine is I'm noticing we create a Quiz in every method, every single one. So with that in mind, let's add a setup method, and then initialize it here. Not a big one at all, because it's very simple to initialize,

Not a big one at all, because it's very simple to initialize, but we'll go ahead and do that. Now, get rid of all of those instances, like so, and then find all of our quiz variables and change them to this quiz. And whoops. Okay, let's give that a run. We're running short on time,

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