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

Review Models and Factories1:00

Instead we're focused on the logic that calculates which usernames to display there. Okay, so let's get started. Let me show you what our beginning framework will be, conversations and replies. So take a look at my prep work here. A Conversation has many Replies. It belongs to a User. And if we want to add a Reply, we have a little helper method here that just delegates. Weird stuff. Next, we have a ConversationFactory to quickly whip up a Conversation for seeding or for a database test.

Next, we have a ConversationFactory to quickly whip up a Conversation for seeding or for a database test. Okay, next we have a Reply model. The only thing I'll add here is a Reply belongs to a User. If I visit the migration for that, you'll see we've already added that here. So let's just add the necessary relationship. Return this belongsTo a User. Great. Now if we go to the ReplyFactory as well, you'll see I have whipped that up. And that's all you need to know.

Write Owner Username Test1:54

Now if we go to the ReplyFactory as well, you'll see I have whipped that up. And that's all you need to know. So let's get to work. I'm going to begin by creating a test here, and we'll call it MentionableUsernamesTest. Now I'm going to start by simply describing what it is I'm trying to test. Well, I'm trying to test mentionable usernames. So the first thing I might test is, well, like I said, at the very least, when I hit that at symbol, it should pull up the owner of the conversation. So the username of the person who created the conversation. So why don't we say it includes the username of the conversation owner, like so.

So the username of the person who created the conversation. So why don't we say it includes the username of the conversation owner, like so. All right, let's build our world. Given I have a conversation, let's build that up. Well, to start, the conversation's username, the owner username, should be included in the list. So I think we can fetch the list by saying conversation, and I did say mentionableUsernames, but you could say relevantUsernames, whatever you want. I'm going to stick with this. If I call this method, I expect it to return an array or a collection.

I'm going to stick with this. If I call this method, I expect it to return an array or a collection. I'm not sure yet. But at the very least, that should contain the username of the person who created the conversation. Now again, don't forget, on our Conversation model, we do have a relationship to the owning User. And on that model, we do have a username column. Okay, so let's give this a run. It fails, unexpected semi-colon.

Okay, so let's give this a run. It fails, unexpected semi-colon. All right, one more time. Call the undefined method mentionableUsernames. Fair enough, it doesn't exist. At the bottom, I'm going to add a new method here, mentionableUsernames, and we'll run it again. Okay, argument two for assertContains must be an array, but we gave it null. So in this case, we didn't return anything here. Why don't we just slam it for now, just return an array, run it, and here we go.

So in this case, we didn't return anything here. Why don't we just slam it for now, just return an array, run it, and here we go. All right, so we expected the returned array, when we call that method, to include some random User's username, the person who created the conversation. But at the moment, it doesn't. All right, well, it sounds like our first step is to return an array that includes the username of the person who created the conversation. All right, if I give it a run, I think we'll get green. Yeah, and we do. So that's good.

Include Reply Creator Usernames4:17

Yeah, and we do. So that's good. Let's move on to our next test. It includes the username of the conversation owner, but it also includes the usernames of all replies, or reply creators, something like that. Okay, this one's slightly different. Yes, I still need a Conversation, but next I want to add a Reply, like so. So let's quickly whip one up. We'll say factory for a Reply, and I don't want to persist it, actually. I'm just going to whip up some fields here, and then I will pass it to the addReply method.

We'll say factory for a reply, and I don't want to persist it, actually. I'm just going to whip up some fields here, and then I will pass it to the addReply method. Okay, so now, yes, it should include the username of the conversation owner, which we're testing in the first method here, but it should also include the username of the person who created the reply. And again, if I go to reply, you'll see there's that relationship that we added right at the beginning of the lesson. So we run it, and it fails. This reply creator is not included in the list. Okay, let's make it pass now.

This reply creator is not included in the list. Okay, let's make it pass now. So yes, we want the conversation username, but let's also see. We can fetch the replies relationship there, and let's just start. We could map over all of the replies, and for each one, we return the reply->user->username. So we could say usernames there, and let me show you what that will look like. If we run it, there we go. So we have a collection that includes that one username. We could even fetch all, and that will just give us the underlying array. Okay, so we could do something like this, array_merge with the replyUsernames, let's

We could even fetch all, and that will just give us the underlying array. Okay, so we could do something like this, array_merge with the replyUsernames, let's call it that, and give it a run again, and we do get green. Okay, so now at this point, both of these tests should be passing. Let me show you. Let's pull off the filter here, so we're only testing mentionableUsernamesTest. And if we give that a run, we do get green. So now one thing I want you to be aware of is, if we look in the database, give this a refresh, you'll see we have a bunch of dummy data here from all of the testing we're doing here.

Parse Mentioned Usernames6:53

All right. So what's the next thing it should include? Well, it should include, let's copy this again, it includes the usernames of all mentioned users within replies. Make sense? So given we have a conversation, and we whip up a reply where the body is something like check with some or exampleUser, we should have some logic that recognizes this as a username and includes it. So if that's the case, when we add the reply, yes, it should include the conversation owner. Yes, it should include the username of the reply creator.

So if that's the case, when we add the reply, yes, it should include the conversation owner. Yes, it should include the username of the reply creator. But it should also include, this time, we're looking for example user to be included within that list. All right. Give that a run. Ah, it fails. It's not included. Let's see what is included. We'll copy that and just real quick dd and dump that.

Let's see what is included. We'll copy that and just real quick dd that. We run it. And at the moment, we just had the conversationOwner and the person who left a reply. Okay. How might we make this work? And we're doing it in a few steps. We can do a refactoring phase once we have everything to green, and we should. What I think we'll do here is remove this and we'll say conversationOwner, like so. Then the reply usernames, and then we have something like that, at least to start.

show you how to find the correct location for logic like that. So yeah, to start, we might say, well, there's the body, right? And at the moment, we know that $body will be equal to check with exampleUser, right? So we could just do like a standard regular expression. So let me pull up a tool I use, regexr. Okay. Often I will just pull this up here, and let's try to tackle or find or target that username. So it looks like we need an @ symbol, and then, yeah, it depends on what you allow for a username. We could always say any word character, so any basic letter, if you hover over this,

Let's see if we remove the period. Ah, no, we have to handle if it's at the end of the sentence, too. So we'll say, or the end of the line. And it's good enough for now, right? So I'm going to copy that, switch back to Sublime, and here I'll say, all right, do a preg_match, paste that in. We're taking a look at the replies body, and anything we did match, I'm going to save within this array called matches. So now if we do a die and dump of matches, and we give our test a run, sure enough, it did detect exampleUser.

So now if we do a var_dump of $matches, and we give our test a run, sure enough, it did detect exampleUser. However, if we were to come back and say, check with exampleUser and another User, and if I give it another run, you'll see we still only fetch that first occurrence. And that's because we used preg_match. We actually want to do preg_match_all if we want more than a single match. So now we get both of those users, which is what we want. Okay, so let's grab the first match, and that'll give us an array there. Let's bring it back to what we had before. And it looks like $matches in the first item there is what we want.

Let's bring it back to what we had before. And it looks like matches in the first item there is what we want. So let's return that. And now I'm going to, one more time, mostly for your benefit, dd what we fetched there. Run it. Okay, so we have a Collection, but notice the first item is itself an array. So it sounds like we want to flatten this down. We can do flatten and run it. And now we get a Collection of one item, which is good.

We can do flatten and run it. And now we get a collection of one item, which is good. Or by the way, you could do flatMap. And we get the same thing here. So I think we're looking pretty good here. Let's now merge conversationOwner with the replyUsernames with the replyMentionedUsers. And let's say usernames to be consistent. All right, let's give our test a run again, and we get argument 3 is not an array. Okay, so it's trying to merge these three arrays here, but in this case, this is an Illuminate\Collection.

Okay, so it's trying to merge these three arrays here, but in this case, this is a Illuminate\Collection. We could say all there. Run it again, and we do get green. So once again, if I remove the filter and we just run all of our tests, we get green, which means everything is working. However, I still think that there might be a bug or two that we need to work out. For example, what if we reference something twice? So let's say we have a reply, and that reply says check with, and then we'll grab the conversation's username, the owner username.

Remove Duplicate Usernames14:52

So let's say we have a Reply, and that Reply says check with, and then we'll grab the conversation's username, the owner username. Okay, well we want to make sure we don't double up, because we should have the one match where we grab the username of the conversation, and then we have another pass where we grab all mentioned users in the reply. But one of those mentioned users is already included in our list. So I want to make sure that we are returning only unique values. So why don't we do this? We'll say it strips any duplicates, and then, to show you, let's just dd(someCall) and give it a run.

We'll say it strips any duplicates, and then, to show you, let's just dd that call and give it a run. Okay, so yeah, well actually we can see two issues here. Here's the conversationOwner, and then that owner was mentioned here, but we have the @ symbol included. Sounds like when we run this call, the only thing I actually want is everything after the @ symbol. Now I can grab not the first match, which would include basically everything here, but the second. So let's give that a run.

the second. So let's give that a run. Yeah, there we go, okay. So now we have Riley44 and then Riley44 again. It sounds like we need to fetch only unique values. So again, we're introducing a lot of possibilities for refactoring, and that's fine. Collect all of those items within an array, give me only the unique ones, and then return the result as an array. If I give this a run, now you can see we have two items instead of three. Okay, great.

If I give this a run, now you can see we have two items instead of three. Okay, great. So let's make this test pass. Why don't we say assertCount should now be, well, to start, two. Give it a run, and we do get green. So count of two, because when we get this array, it should have the conversation's owner username, and it should have the reply's username, and that's it. Even though the reply mentioned somebody, well, it was already the conversation owner, so it shouldn't be included. Or we could be explicit here and say assertEquals the conversation's username and the

Anyways, we give that a run, and now everything is returning green. What I want to do is show you this working so you can see it with your own eyes, then we'll do a quick refactoring phase, and then we'll call it a day. So I'm going to whip up a Conversation, like so. And then let's build up a Reply. And we'll say the body of the reply is check with some User. And the reply userId is, let's see, do we have any existing users? We do. 11. All right.

Eleven. All right. And now let's add the reply. Conversation addReply, like so. Oh, I'm sorry, convo addReply. Okay, so at this point we should have, when we call the method, the username of the conversation owner, the username of the person who wrote the reply, and then finally the some User username who was mentioned. So if I were to say convo, give me the mentionable usernames, we should have an array of three items.

This is just a simple explanation with no code.

And then could I get rid of that? Yes, it looks like everything's still working there. Okay, fingers crossed. We're going to go back to Sublime. In conversation.php, I will replace our regular expression, and we're going to give it another shot. So create a conversation, and I'll hit up a few times. Create a reply, set the userID, set the body, add the reply, and then fetch all mentionable usernames. And there we go.

Refactor Mention Logic to Reply19:59

We're asking for all of the replies. So think, we are in the Conversation class. So we're saying, hey, give me all your replies, and I'm going to look into your body, and I'm going to look for something specific, and then I'm going to return that. That's not good management, right? Why don't we delegate to Reply instead? We'll say, hey, let me know which users were mentioned in your reply. I like that a little more. So let's go to Reply here.

I like that a little more. So let's go to reply here. And if we switch back, we're just going to say, return reply mentionedUsers. And then I would get rid of this. Okay. Well, right now, if we run our tests, everything's going to blow up, of course, because that doesn't exist. All right. mentionedUsers. And I'm going to grab all of that here and paste it in, like so.

Mentioned users. And I'm going to grab all of that here and paste it in, like so. Okay. So now we will update this. Look in the body, save any matches as an array, and then return the first matching group. So if we give the tests a run again, we now get green, but we've moved this logic over to reply. reply cannot be responsible for letting the world know and letting anyone know who are the users who were mentioned within you or within your body.

the users who were mentioned within you or within your body. Okay. So let's say mentioned usernames. Still a green. Yes. Okay. So that's the first refactor. The second refactor is, because we are calling a method on reply, we can actually shorten this up a little bit using higher order collections. I can do this little number here.

this up a little bit using higherOrderCollections. I can do this little number here. Let's see if that works. Run it again, and we still get green. Yeah. flatMap over all replies, and then for each reply call mentionedUsernames. This is a Laravel feature called higherOrderCollections. Pretty nifty. Now, we could do the same thing here if we wanted to make the username a method on reply. We could do that.

Now, we could do the same thing here if we wanted to make the username a method on Reply. We could do that. That would look like... I don't think you have to do this, but it might be something to consider. Return the user. You know, if you're referencing the username constantly, that might be useful. And again, if you wanted to take that, you could then clean this up to map over and give me the username, and then give me the underlying array. So if we give that a run, we still get green. Next, rather than calling merge on all of these arrays, let me show you.

So if we give that a run, we still get green. Next, rather than calling merge on all of these arrays, let me show you. Yeah. Why don't we instead do this? Let's say collect the conversationOwner, the replyUsernames, and the replyMentionedUsernames. Let's see what happens if we run that. It's going to fail, because now we have these nested arrays. So why don't we flatten that down to a single array, run it again, and now we're back to green.

So why don't we flatten that down to a single array, run it again, and now we're back to green. All right. That'd be an option. So what if we're going to collect anyways? Is there any reason to convert to an array? What if we treat both of these as collections themselves? Well, if we do nothing, I think we'll still get green. Yeah, we do. And that's because we collected anyways.

So we could stick with something like that. I think that's fairly clean. Let's take one last stab at it. Why don't we start by collecting this first item, and then I'm going to merge in all of the replies, or specifically all the reply usernames, and then I'm going to merge in all of the mentioned usernames, then flatten it down, strip out any non-unique values, and then give me an array. And if you even want it, you might want the collection instead, but we're going to stick with an array. So if I run that, yay, we still get green.

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