Drafting the First Test0:00
Now that we have a general idea of where we're going, let's get started. I'm going to begin with a test, so that we can sort of figure out together what it is we're doing here. So I know we're going to have something like Product, and note these classes don't exist yet. But yeah, maybe something like if I had a Product for a Nintendo Switch, and then if we had a retailer, we'll do this one to Best Buy, then we could ask questions like, well, is the Switch currently in stock? Or does Best Buy have stock or has stock for a Switch? You know, things like this. We're starting to flesh out our API. Okay, so let's say here our test name will be, it checks stock for Products at retailers. And this is fine. You know, we're still sort of like a painter looking at a blank canvas.
Creating Product Model0:47
Okay, so let's say here our test name will be, it checks stock for products at retailers. And this is fine. You know, we're still sort of like a painter looking at a blank canvas. We're not entirely sure what we're doing yet, but we're fleshing out how we want to interact with it. All right, so for now, our assertion, we'll start with this one. By default, it's not going to be in stock. So we give it a run. And of course it fails because we have no Product model. All right, let's make a model for a Product and include a migration and a factory. And actually on that note, let's go ahead and pull in the RefreshDatabase trait. Finally, I will import the Product. Okay, let's run it again. And now we get a mass assignment exception, and that's because I'm literally mass assigning the attributes here,
Finally, I will import the product. Okay, let's run it again. And now we get a mass assignment exception, and that's because I'm literally mass assigning the attributes here, and Laravel is trying to protect us against that. But I don't really like to do that. So we have a couple choices. You could set the fillable fields and be explicit. You could set guarded to an empty array. Or if you want to turn this off application-wide, and if you know what you're doing, you might go to AppServiceProvider, and in your boot method, set model::unguard(). And this will disable it application-wide. So yeah, now if I run it again, it's going to fail, but for a different reason. The product has no column name. Okay, let's do that now. Create products table.
Adding Retailer Model2:07
it's going to fail, but for a different reason. The Product has no column name. Okay, let's do that now. Create products table. And yeah, for now, a Product consists of a name. Maybe does that need to be unique? Probably. Let's leave it like that and run it again. Okay, next we don't have a Retailer. So let's run our create model call again, but update it to Retailer. Run it again. Okay, and I'm sorry, we have to import that. Alright, now it looks like Retailer has no column name. Okay, so notice it's just
Improving IDE Autocomplete2:39
import that. Alright, now it looks like retailer has no column name. Okay, so notice it's just giving us a set of steps to follow. We don't even have to think about it. Just follow the steps. Every retailer has a name, and again, that's a unique name. Alright, so once again, we come back, run it again, and now there's no method in stock for the Product model. So let's work on that. But first, real quick, notice how in PhpStorm, it's squawking because it can't find a create method on Product, even though it's available dynamically through Eloquent. So what you can do, it actually is a little
because it can't find a create method on Product, even though it's available dynamically through Eloquent. So what you can do, it actually is a little tricky, but what you can do is, let's go to Composer, manage dependencies, there it is. And it's, I can't remember the name, but it's from Barry. There it is, Laravel IDE Helper. So this is going to generate a file that contains all of the symbols and methods that we need, so that PhpStorm can understand it. Okay, so now the only remaining step, if we run php artisan, as you'll see, that adds these IDE helper commands. So let's run the generate command.
run php artisan, as you'll see, that adds these IDE helper commands. So let's run the generate command. And this, again, is only if you're on PhpStorm, and only if you care. So if you run it, you'll find in your routes file, you're not going to see any errors, like you might have before. Or not errors, but little warnings, where it can't understand what you're doing with facades. But you're still going to see it for Eloquent. So a little thing you can do is you add this mixin. At the very top, I can say we have a mixin here for Eloquent. Now you'll see it goes away, because it can understand those methods,
we have a mixin here for Eloquent. Now you'll see it goes away, because it can understand those methods, as you see there. But I kind of hate, I'm sorry, I kind of hate adding it here. So what I generally do for most applications anyways, is all my models extend a relative parent model. So I'm going to do that now. I will get rid of all of this, and then in my app directory, I will add a new class called Model. This will extend Illuminate\Database\Eloquent\Model, and then right up here in one location, I will add that mixin.
Illuminate\Database\Eloquent\Model, and then right up here in one location, I will add that mixin. And now it should update everywhere I reference it. Okay, so now notice that product doesn't extend Illuminate\Model, it extends my local model. And now if I have any global helpers I need to add, I can put them there. And the same will be true for the Retailer class. And now that works. Alright, let's get back to it. So we need an inStock method on our product model. And let's just slime that for now. We give it a run, and it passes. Okay, but now if we were to continue on,
Designing Stock and Relationships5:19
And let's just slime that for now. We give it a run, and it passes. Okay, but now if we were to continue on, it sounds like we're going to need Stock here. So let's see how this might look. We'll have a Stock class. A Stock will have a price that will store in cents. It'll have a URL. It'll have potentially a SKU. It will have, it'll be associated with a Product. So a Stock is available for a particular Product at this Retailer. So we'll add attributes for the Retailer ID as well as the Product ID.
a particular product at this Retailer. So we'll add attributes for the RetailerID as well as the ProductID. And then finally, for that stock, is it currently available to buy? So if we had something like this, and then we said Best Buy, we're going to add stock for the switch. Yeah, maybe something like that. What do you think? We give it a run, and of course it fails because a Stock model doesn't exist. So yet again, we run our make command to make a Stock model, and then I will import this. And of course if I run it again, it fails because the addStock method.
to make a Stock model, and then I will import this. And of course if I run it again, it fails because the addStock method doesn't exist. Alright, let's go to our Retailer, and we're going to add stock for a particular product, like so. Let's give that a run. Okay, that returns green. But of course, we need one more test to say, well, after we add the stock, if we now check if it's in stock, it should return true. And of course, we know that won't happen. Okay, so right around here, it sounds like we need to do something. So if we were to addStock, let's think
happen. Okay, so right around here, it sounds like we need to do something. So if we were to add stock, let's think a Retailer has stock, and that's going to be a hasMany relationship. So then, if we want to add stock, we would just say $this->stock->save($stock). So now, think about it. When we call that method, it'll add all of these attributes here. It will then add the automatically, I'm sorry, the Retailer ID as part of this call, but we also need the product_id. So let's do that all in line here. We'll say, well, $stock->product_id
call, but we also need the productId. So let's do that all in line here. We'll say, well, stockProductId is equal to productId, and then we can perform that save call. Okay, so let's give it a run, see how we're doing. It fails right now because of our stocks table migration. So the first thing is, if I go to create stocks table, the plural is really just stock. So I'm going to rename this to stock like so, and then update the table name. Finally, because we've abandoned the conventions, I'm going to have to update.
like so, and then update the table name. Finally, because we've abandoned the conventions, I'm going to have to update my model to set the table name explicitly. Okay, but anyways, for a stock, we know that there will be a, what first? The product_id. So what do we have stock for? That'll be constrained, and then we'll say cascade on delete. We need another one for the retailer_id. So we have stock for what retailer? Next, what is the price for this particular stock? And that'll be stored in cents.
Implementing In-Stock Query9:03
We set up our stock, we add it, and then if we check if it's currently in stock, yeah, of course, it fails because we slimed it there. So think about it. If we go back to our stocks table migration, there is a relationship between stock and a product, right? So if I were to say, once again, hasMany stock, then I should be able to do a quick database check where I say, okay, well look at the stock. So this will return all of the stock records that are associated with this particular product ID. And let me know if, look for stock, only stock, where
So all we're doing here for a Product is checking, well, look at the stock and see if you can find any records where inStock is set to true, and let me know if that exists. And if it does, you can buy it. A little tip here, if you want to use magic methods, you can also do whereInStock, but I usually won't reach for those. And there we go. So let's have a look at our example test. We're still figuring things out in our head. We're deciding how we want it to work, but I think this is a good start. Notice how, if we go back to our scratch file here, a lot of those keywords are now visible in the code. Notice
how, if we go back to our scratch file here, a lot of those keywords are now visible in the code. Notice stock, retailer, product. We haven't done availability yet, but maybe that'll show itself in one of the methods we create. You could even maybe change it to something like this. Is the Switch available? That might be nice as well. Anyways, we're making good progress. Let's continue in the next episode.