[0:00] [MUSIC] [0:10] Building a restful API can be a daunting task because there are a lot of things [0:15] that you need to consider. [0:16] For one, you need a suitable framework that is both powerful and flexible. [0:21] But it also needs to provide built in tools to help you be more productive, so [0:25] that you can go from development to deployment in a relatively short amount [0:29] of time. [0:30] And Laravel has got you covered. [0:32] Hi, I am Jeremy MCPeak with Tuts+. [0:35] And I invite you to join me so [0:37] that I can teach you how to build a restful API using Laravel. [0:41] We'll start from scratch by defining our data models and [0:44] seeding our database with test data. [0:47] We'll then dive right into developing our API by handling basic GET requests. [0:52] And you'll learn how to transform the data from the database into a consistent and [0:57] conventional JSON format. [0:59] You'll then learn how to build a flexible and [1:01] reusable filter syntax so that end users can easily query and filter data. [1:06] From there, you'll learn how to handle and [1:09] validate manipulative requests such as POST, PUT and PATCH. [1:14] We'll also discuss how to implement a bulk insert features so [1:18] that end users can quickly insert multiple entities with a single request. [1:23] We'll then look at how to protect your API endpoints with Laravel Sanctum. [1:27] And how you can use its token capability feature to not only [1:31] authenticate requests but also authorize them for specific tasks. [1:36] We have a lot of ground to cover, so let's get started. [1:39] [MUSIC] [1:44] Of course, the first thing that we need to do is make sure that you have everything [1:47] that you need to follow along. [1:48] And the list is relatively short, but [1:51] there's a variety of ways that you can obtain and set all of these things up. [1:55] You need PHP, you need an HTTP server, some kinda database, [1:59] preferably MySQL or something compatible. [2:01] And then you of course need Laravel. [2:04] Now, the one solution that's going to give you everything is a Laravel Docker image. [2:11] If you're not familiar with Docker, it is a virtualized environment. [2:14] It's not a virtual machine, it is a virtual operating system, which I [2:18] know kinda sounds like I'm splitting hairs but there is a technical difference. [2:22] Docker is for creating what are called containers, so [2:26] that you can run applications regardless of hardware and host operating system, [2:30] and that's all it is. [2:32] A virtual machine is for setting up CPU, RAM, all of that other stuff. [2:37] But that's getting off into the weeds. [2:38] So if you go to laravel.com, click on the documentation, and [2:42] then click on your first Laravel project. [2:45] And it's going to kind of walk you through the process of setting all of that up. [2:49] Regardless of what operating system you're on MacOS, Windows, or Linux, [2:53] this is an option for you. [2:55] However, for Windows, [2:56] it's a little bit more involved because you have to install Windows subsystem for [3:01] Linux version 2 and enable that and get all of that setup, but that is an option. [3:06] And when it comes to development environments, I'm all for [3:09] virtualization because it completely separates your development environment [3:13] from your main machine. [3:14] However, this particular machine has some technical issues, [3:18] which prevent me from using Docker. [3:20] So instead, I'm going to use something called XAMPP, X-A-M-P-P. [3:25] This is an all in one solution that has everything but Laravel. [3:30] So this will give you PHP, it gives you Apache, [3:34] MariaDB which is a MySQL Clone and Perl. [3:38] And one of the things that I really liked about this is that there's [3:41] no configuration. [3:42] And so by downloading and installing XAMPP, you get everything installed and [3:46] configured. [3:47] And that is a very nice thing because if you install everything manually, [3:52] you have to configure everything manually. [3:55] But that's not it, you also get a nice little control panel application, so [3:59] that you can turn these services on and off at will. [4:02] And of course, using something like XAMPP gives you a little bit more than just what [4:07] it says on the screen here. [4:08] There are other tools that are built in that allow you to work with [4:13] a database like PHP, MyAdmin and a few other tools. [4:17] So this is a very nice little thing to have. [4:20] Now, if you're going to take this route or [4:23] if you are going to install and configure everything manually, [4:28] then you will need to install Laravel, and we do so using Composer. [4:32] The website is getcomposer.org, and [4:36] basically it is a very easy thing to get set up on your system. [4:41] And then from there you just need to go to the command line and [4:46] type this out, composer global require laravel/installer. [4:51] And this is going to download and install the Laravel installer, so [4:55] that you can create new projects on the fly using the letter Laravel command. [4:59] And to do that, simply type laravel new, and then the name of your project. [5:05] We'll call this laravel-api. [5:07] This will take a few moments to finish creating our project. [5:11] But once that's done we can start configuring our project to interact [5:16] with our database. [5:17] The installer creates a new directory with the name of your project. [5:21] So the first thing you want to do is cd into that new directory. [5:25] And we'll go ahead and fire up the code editor, because we need to [5:29] modify the environment file that contains the information about the database. [5:34] Now, in my case, [5:35] everything is already set up because my system uses all of the defaults. [5:40] If you are using something like XAMPP or some other kind of all in one type of [5:44] tool, then chances are you won't need to change anything either. [5:47] If you manually set up your environment then you might need to visit this file and [5:53] make some changes. [5:54] If you're using the Docker image then I think everything is already done for [5:59] you, so you don't have to do anything. [6:01] Now, in my case, I need to create this database. [6:04] And we can change the name of the database if we want, but [6:08] I typically named the databases the same as my project. [6:12] That just makes things a little bit easier for my brain to process. [6:15] So you can name your database whatever you want, but [6:19] do be sure that your connection information is correct. [6:22] For me it is, so all I need to do is go to my XAMPP control panel. [6:27] I need to start up the MySQL database, [6:30] which is actually a Maria database, but I'm going to go to the admin. [6:35] And really, this is the only thing that we need a administration tool to do. [6:40] You could use the command line for [6:42] the database if you wanted to take that approach. [6:45] However, when it comes to working with databases I prefer a graphical tool. [6:49] So I'm going to use what is given to me by XAMPP, which is PHP MyAdmin. [6:54] And I am simply going to click on New, here because this is for [6:58] creating a new database. [7:00] It shows you the databases that are already here, many of these are built [7:04] in and system databases, so we don't want to do anything there. [7:09] But as far as the database name I'm going to use the name of the project [7:13] to create and that is going to get this setup. [7:17] So that now whenever we use artisan to make our migrations and stuff like [7:21] that it's going to connect to that database and everything is going to work. [7:25] So in the next lesson, we are going to do just that. [7:28] We are going to create our models and our migrations for [7:31] the data that we are going to be working with as we build this restful l API. [7:36] [MUSIC] [7:41] In this lesson we are going to do much more than just create our models. [7:44] We are going to create the migrations, [7:46] the factories ,the cedars as well as the controllers. [7:49] And of course, that sounds like a whole lot of work but [7:52] some of that is going to be done by artisan. [7:54] And now we are going to take a smaller piece of a much larger project to where we [7:59] are just working with a couple of types of data. [8:02] Such as customers and invoices that we would issue for those customers. [8:08] So, let's get started by using artisan to make our module called customer, [8:14] and we're going to use the all flag. [8:16] This is going to create well just about everything that we need to work with this [8:21] type of data, and then we will do the same thing for the invoice as well. [8:26] The relationship between these two types of data is going to be a one to many, [8:31] one customer can have many invoices. [8:33] So, the first thing I want to do is go ahead and [8:37] open up our models so that we can make that relationship. [8:41] So, let's go to app, modules, customer, and [8:44] we're going to add in our HasMany relationships. [8:47] So, this will be just a method called invoices, [8:51] where we will return the HasMany, and we want to specify the invoice class. [8:57] And then for the invoice class we will have just the inverse relationship. [9:01] So, really let's just copy this so that we can paste it inside of invoice and [9:06] then make the necessary changes. [9:08] So, the inverse of a HasMany is a belongsTo, [9:12] our class is of course going to be the customer class, and [9:17] the name of our method will be simply customer. [9:21] And that's all we're going to do with the module class for now. [9:25] We're going to focus more on the migrations and [9:28] everything else as far as populating the database. [9:31] So, let's go to the Database folder, open up Migrations, [9:35] we'll start with the CreateCustomersTable migration. [9:38] In a real application there is a ton of information related two [9:43] individual customer. [9:44] And I don't think we need to have everything, but [9:48] I want a lot of data to just work with. [9:50] So, we're gonna have quite a few columns here, the first is going to be the name of [9:55] the customer, and then we're gonna follow that up with the customer type. [9:59] Now, the idea behind the type is that the customer could be an individual, [10:04] or it could be a business. [10:06] So, this is just going to be a column that will typically just have one character, [10:11] i for individual and a b for business. [10:14] And of course we would need some kind of email in order to contact whoever it is [10:19] that we interact with. [10:21] And then it will be helpful to have the address, city, state and [10:25] postal code, so let's go ahead and get all of that set up. [10:30] And that's all that we are going to include with our customers table. [10:33] Now of course, we could add a whole lot more but this is going to be just fine. [10:37] So, now let's go to the invoice and let's start defining this table. [10:43] Now, the very first thing that we need [10:46] other than the id is the foreign key for the customer id. [10:51] But then it would also be useful to have the amount of the invoice, and [10:55] let's just keep that as an integer. [10:58] Ideally, we would have more precision here, but for [11:01] the most part, as long as we have a numeric value, [11:04] that's really all that we care about as far as our development is concerned. [11:09] And then we will have a status, which is going to be a string, and [11:12] this is going to be somewhat similar to the type of our customer, [11:16] in that it will typically just contain one character. [11:20] Now, let's say that we could have a build, meaning that we have gone ahead and [11:25] build the customer for this invoice. [11:27] We will have a paid signifying that the customer paid the invoice and [11:31] then there will be a void because sometimes mistakes are made, and [11:35] we need to void that invoice. [11:37] Now, it would be useful to have some information about the status, [11:43] like if it is billed, that we would have a build date, and [11:47] if it is paid, then we would have a paid date. [11:51] Now, both of these of course need to be date time, [11:54] so let's go ahead and specify that. [11:56] But an invoice might not be paid, in which case pay date kind of needs to be no, [12:01] so let's mark this as mullable, and then we will keep our timestamps. [12:06] And of course there's also a whole lot more information that we can supply with [12:10] an invoice. [12:11] And ideally, we would have another table that would list all of the line items of [12:16] that invoice but we're not going to do that at least for right now. [12:20] But of course having tables isn't enough, we need some data to work with, [12:25] so let's work on our factories. [12:27] So, let's open up our CustomerFactory first, and [12:30] as far as the definition that let's start with the name. [12:33] Now, names are going to be, well, they don't have to necessarily be unique but [12:39] it doesn't make sense to have a personal name for [12:43] a company or a company's name for an individual. [12:46] So, really what we need to do first of all, is get the type that the customer [12:51] that is going to be created is going to be, and we can use a faker to do that. [12:56] So, we will use the random element method, and [13:01] our array is going to have simply I and [13:04] b, of course for individual and for business. [13:09] And then from there we can determine what the name of this particular customer is [13:13] going to be. [13:15] So, if the type is equal to an I, then we want to use the name method from faker, [13:21] because that is going to give us an individual person's name. [13:27] Otherwise, we have a business, [13:29] in which case we want to use the company method to give us a company name. [13:34] And then we can use that value for the name, and then we have the type, so [13:38] we'll go ahead and set that value as well. [13:41] And then for the email, it doesn't matter there, I mean, ideally, [13:46] the email would be somewhat related to the company if this is some kind of business, [13:51] but we'll just use faker to give us an email. [13:54] And then for address, city and state, [13:57] we will use fakers methods to provide those values. [14:00] So, with our CustomerFactory defined, let's hop on over to the InvoiceFactory, [14:06] and the first thing we will set is the customer the id. [14:10] Now, one thing that we can do here is to specify that this customer id is going to [14:16] come from the CustomerFactory, in which case we need to import that module class. [14:22] And then for the amount, all we need is some numeric value, and [14:26] it will be useful to have something that is not zero [LAUGH] and not negative. [14:31] So, we're going to use faker to give us a number [14:37] between let's say 100 and 20000. [14:41] Man, I would like to be able to invoice for that much, and [14:45] then we need the status. [14:47] Now, the status can be one of three values, it can be built, paid or void. [14:53] And really the next two fields, the build, date, and [14:57] the paid date depend upon this status. [15:01] So, let's kind of do the same thing where we are going to go ahead and [15:04] get the status. [15:05] Where we will use faker to once again give us a randomElement [15:10] from an array that contains our statuses. [15:14] So, we have build, we have paid, and then we have void. [15:18] And then for the build date, really it doesn't matter [15:23] what it is because we will always have a build date. [15:28] So, we will go ahead and use a faker to give us a date, time, [15:33] value that is going to be some time this decade. [15:36] And really the dates don't matter, this is just test data to work with. [15:41] However, we do want something that is a little bit realistic. [15:44] Now, one thing that we probably should do, is get this build date and [15:50] then base the paid date off of that if the status is indeed paid, [15:55] so we do need to check that here. [15:58] And if it is paid, then we want to date, but [16:02] you know let's just do this, once again we will call this decade. [16:07] Method, and if it is not paid, then the value is going to be null. [16:11] And now that we have our factories, we can use them to seed our database. [16:15] So let's close our factories, let's go to the seeder's folder, and [16:19] let's open up CustomerSeeder and InvoiceSeeder. [16:23] And really, the DatabaseSeeder as well, because we will need that too. [16:26] And now that I think about it, I don't really think we need the InvoiceSeeder, [16:30] because we can create everything from our CustomerSeeder, [16:34] because this is exactly what we could do. [16:36] Let's, first of all, use our factory method. [16:40] And let's say that we want at least 25 customers that have 10 invoices each. [16:48] I mean, we could be just lazy and say that we have 100 customers, [16:52] they each have 10 invoices, but let's not do that. [16:55] Let's have a different number of invoices for some of our customers. [17:00] So we're gonna use our factory, we're going to create 25 customers. [17:04] Each of them will have 25 invoices, and we will create that. [17:09] So let's just copy and paste, so [17:12] that then we can have 100 customers that each have 5 invoices, [17:17] then we can create another 100 customers that have 3 invoices. [17:23] And then finally, let's create some customers that have no invoices, [17:28] and let's make this a small amount. [17:30] So five customers will not have any invoices, and [17:34] they will just be in our system. [17:36] So with that in place, we can go to our DatabaseSeeder, and [17:41] inside of the run method, we are going to call the CustomerSeeder and there we go. [17:47] So that's now we should be able to go back to the command line and [17:52] we will use artisan to migrate. [17:55] Let's use fresh, and we also want to seed the database. [17:59] So if we typed everything correctly, which, of course, we didn't, [18:04] so we cannot find the CustomerSeeder, or rather we can't find customer [18:10] inside of the CustomerSeeder, which, of course, we need to include. [18:15] So let's use App\Models\Customer. [18:19] Let's go back to the command line. [18:21] Let's run the fresh migration again. [18:23] Another error, and it looks like that I used fake instead of faker. [18:29] What was that file that was customer factory? [18:32] So let's open up the customer factory and let's change fake to faker. [18:38] There we go. [18:39] Let's go back. [18:40] Let's run the seeder again, and of course, we get something incorrect, [18:46] column not found, billed date, and why is that? [18:50] So let's go to the migration and I probably typed something incorrect. [18:57] That was inside of invoice. [18:58] So we have billed_dated, and that's what I get for [19:03] typing and talking at the same time. [19:07] It's not that easy, at least it's not for me. [19:10] So now, finally, hooray, everything works. [19:14] We have our stuff. [19:16] And so now that we have all of our data in the database, [19:19] we can start developing our API in the next lesson. [19:22] [MUSIC] [19:28] APIs are software. [19:29] They are a specialized kind of software. [19:32] But ultimately, they are software, we write them, we release them, we fix bugs, [19:37] and then we decide to add new features, so that we can release a new version. [19:42] However, our users are developers, they are using our APIs in their applications. [19:47] And anything that we change could break their applications. [19:52] So unlike conventional applications, we need to version our APIs. [19:58] So that as we release new versions, our old versions are still there, [20:02] they're functioning. [20:03] And developers who are using those old versions can opt into the new features [20:08] in the new versions, or they can continue to use the older versions. [20:13] So unfortunately, this means that we have to not only keep track [20:16] of all of the different versions, but we have to keep them inside of our project. [20:21] But that's just the nature of developing an API. [20:25] So one of the easiest ways to do this is to segregate the different pieces that we [20:30] need for different diversions, and the controllers are really the most important, [20:36] because those are what are handling the requests. [20:39] So this is what I typically do. [20:41] Inside of the controllers folder, I will create a new folder simply called API. [20:44] The idea being that this is the folder where all of the API controllers live. [20:50] This is especially useful if you have a project that is both a typical web [20:55] application that is serving HTML, but [20:57] you also have an API that you need to provide as well. [21:01] And then inside of the API folder, this is where I would have the version folders. [21:07] So this is our first version, so we're just gonna call it v1. [21:11] And then we will put inside of there the controllers that we need, [21:15] such as the customer controller and the invoice controller. [21:19] So let's go ahead and let's move those there. [21:22] And of course, we need to do more than just isolate these files, we need to [21:27] change their namespaces, otherwise we're going to run into naming collisions. [21:31] So now, the namespaces for the customer and [21:35] invoice controllers are app HTTP controllers, API, and then v1. [21:42] And we need to make that change on both of those, so let's just copy and paste. [21:47] But this is also going to change the controller class that we are extending, [21:52] we need to import that controller. [21:55] So that's easy enough to do. [21:57] We'll just import App\ http\Controllers, and then controller. [22:03] And of course, we need that in both of those files. [22:06] So any controller that we are going to add to our API will [22:11] need to make those changes. [22:13] And let's go ahead and [22:15] at least implement the customer controllers index method, so that we would [22:20] then simply return all of the customers that we have in the database. [22:25] Now, we could take this a step further and we could also version our models, [22:30] because we could make changes to the database. [22:33] However, those changes are typically adding new columns, [22:37] as opposed to changing the columns that are already there. [22:40] So from that aspect, versioning your models is a little less common. [22:46] Actually it's a lot less common, but you could take the same approach. [22:50] Now, of course, we need to set up the routes for these controllers. [22:54] So let's go to the API file inside of routes, [22:58] this is where we define the routes or our APIs. [23:01] And we want to include the version in the URL itself, [23:05] because every version should have a unique URL. [23:08] And if you define a route inside of the API file, then your URL begins with API. [23:15] So we would want then our version, v1, or v2, or whatever, [23:20] and then our endpoints, such as customers, or invoices, or things like that. [23:27] So one of the easiest ways to do this is to use a group. [23:31] And there are several options that we can apply here. [23:34] The first is going to be the prefix, and [23:37] we can simply set the prefix to whatever version that we need. [23:42] So this is the first version. [23:43] So the prefix will be v1. [23:46] That is going to append to the already existing prefix of API. [23:52] So we will have API/v1. [23:55] But we can also make our lives a little bit easier by also specifying [24:00] the namespace, because all of our controllers reside within the same [24:05] namespace, and that is simply going to be App\http\Controllers. [24:10] Then Api, and then V1. [24:12] And then from there, we just need to define our routes. [24:15] Now, we essentially have resource controllers. [24:18] However, these are a little bit different because they are for an API. [24:22] We don't really need to Create or Edit endpoints. [24:27] So we can use the API resource method. [24:31] It essentially gives us the same thing, but it omits the Create and [24:35] Edit endpoints, so that then we can specify our endpoints. [24:40] So we have customers and we would want to say that that is our customer controller. [24:46] And then we will essentially do the same thing for our invoices, [24:50] except we of course want the URL to be invoices and the controller is [24:55] invoice controller, and I believe that's customer controller. [25:00] Okay, so with that in place, we should be able to go to the browser and [25:05] at least hit the index route on our customer controller. [25:09] So you of course need your application up and running. [25:11] And I'm using Firefox because when it receives a JSON response, [25:15] it will automatically parse it into something that is halfway readable. [25:20] Well, it's more than halfway readable, it's very readable. [25:23] So our URL is going to be API/V1/customers, [25:28] and we should see the output, and there we go. [25:33] As I said, this is very readable. [25:35] We can go through this, and if we wanted to scroll through all what, [25:40] 225 or 250 users, we could do that. [25:44] Now, as you'll notice, this JSON payload looks pretty good, [25:48] except when you get to the fields like postal code, created at, and updated at. [25:54] Instead of using underscores, we would like to use camel case because that [25:59] is the convention for the JSON format, and we want to follow the conventions [26:04] of whatever language or format that we are using. [26:07] And so in the next lesson, [26:09] we are going to look at doing just that, as well as filtering the results, [26:13] because we don't need to supply everything such as the timestamps. [26:18] [MUSIC] [26:22] We ended the previous session with code that actually works. [26:25] And there's a lot to say about code that works because it's a lot better than code [26:30] that doesn't. [26:31] And all we are doing is returning all of the records from the customers table [26:35] whenever we go to the customers endpoint. [26:37] And there are some things that I want to change about that, like for [26:42] example, the postal_code property. [26:44] Now, for PHP and MySQL, that's perfectly fine because that is the convention, [26:50] especially for MySQL columns. [26:52] However, now we are talking about JSON, and [26:55] the convention in JSON is to use camel case. [26:58] And anyone using our API is going to expect camel case, so [27:03] we kind of need to address that. [27:05] Something else that I want to do is remove the create and updated at fields. [27:11] Now, later on we're going to add authentication, and I think for [27:14] authenticated users, [27:15] that would be perfectly fine that we could provide that information. [27:19] But for just normal users that are not authenticated, [27:23] they don't need to see that ,so I want to hide that from a typical response. [27:28] So that's two things that I want to do, and [27:31] we could approach those two things in a variety of different ways. [27:35] But Laravel gives us something called a resource that allows us to transform [27:40] an eloquent model into a JSON response, and [27:43] we have complete control over that transformation. [27:47] So let's create a resource, we'll do so with Artisan. [27:50] The command is simply to make a resource, and then we give it a name. [27:55] In this case, that's for our customer, and we'll just call it CustomerResource. [28:00] But before you hit Enter, we need to think about [28:02] versioning because this is something that is going to directly affect our users. [28:07] This is the JSON response and that can change over time, so [28:12] we want to version this as well. [28:15] So we can say V1/customer resource and that's perfectly fine. [28:20] Artisan is going to create a new resource class that is going to be inside of [28:25] the Resources folder that just popped in there. [28:28] That is inside of App\Http\Resources, then we have V1, and then CustomerResource. [28:35] And the namespace is automatically set how we need it to as well. [28:39] So before we start changing this, [28:42] what I want to do is implement our show method because that is going [28:47] to give us a much easier way of showing how this is going to work. [28:53] So all we are going to do right now is return to that customer. [28:56] So if we go to customers/1, that should give us just a single customer, and great. [29:04] So we're going to modify this output by using this resource class. [29:09] So all we need to do here is return an array where the keys [29:13] are the property names that we want in the JSON response, and [29:18] then we just provide the value for those properties. [29:22] So for example, if all we wanted to do is return the ID, we could do that [29:27] very easily by just specifying ID and giving it the value of ID. [29:32] And as far as the syntax for getting those values, [29:35] it's just like using our model class. [29:38] So we could save this and we could use it inside of our customer controller. [29:43] So the first thing we need to do is import this resource and [29:47] that is inside of App\Http\Resources\V1, and then CustomerResource. [29:54] And then we are going to use this class inside of our store method, [29:59] and we are going to create a new instance of this and [30:03] we will pass in the customer object as the argument to the constructor. [30:09] And with that simple change, we can go back. [30:12] Let's refresh here and we are going to see just the ID. [30:16] Now, the overall structure change, we see that we have this data, [30:20] and that's part of using a resource. [30:22] Now, we could change that if we wanted, but personally, I like that, so [30:27] we're going to leave that alone. [30:28] So of course, we want to provide more information, [30:32] which means that we have to go back to our customer resource and [30:36] we have to specify all of the keys that we want to expose. [30:40] And for the most part, it's going to be a straight up one to one. [30:44] So the ID is going to be the ID, the name is going to be the name, [30:47] the type is going to be the type, and so on and so forth. [30:51] But then we get to the postal code, and this is where we'll use our camel case and [30:57] then we will specify the postal code from our model as the value, and there we go. [31:04] So if we go back, we can refresh and we see those values. [31:09] And if we look at postal code, then we can see that that is camel case. [31:12] And of course, the timestamp fields are not there because we [31:16] explicitly left them off inside of our customer resource. [31:21] So with that in place, let's go back to our customers. [31:25] And we are still going to get that same response to where we see everything. [31:30] And we want to use our customer resource to filter down each [31:35] individual object in the JSON. [31:38] Well, the way that we can do that is by creating another resource, [31:42] except in this case, we're going to call it CustomerCollection. [31:45] And this is still going to be a resource, but it's going to be slightly different. [31:50] It is specifically for working with a collection of things. [31:53] So if we open up CustomerCollection, we see kind of the same thing. [31:57] And we can actually just leave this alone so [32:00] that inside of our controller, we of course need to import this. [32:04] But whenever we have our customer collection, [32:08] we will use that almost exactly like we used our customer resource. [32:12] We are going to new up the CustomerCollection, and [32:16] then we will pass in the collection that we want to transform, [32:20] which is going to be all of the records from our customer model. [32:24] So if we go back to the browser, let's refresh, [32:28] we are going to see the new JSON format. [32:30] Once again, we have this data that's going to contain all of the data, but [32:35] the data itself has been transformed. [32:38] We can see the counter case for postalCode. [32:40] And of course, the timestamps are not there. [32:43] So how does all of that work then because we did not specify the customer resource, [32:48] we just specified the customer collection? [32:51] Well, it's kind of all magic. [32:53] It's going to assume that there is a customer resource, and it's going to use [32:58] that to transform each and every record so that we get the payload that we want. [33:02] And one of the beautiful things about this is that, especially for our index, [33:07] we don't want to return just all, it would be nice if we would paginated. [33:11] So if we call paginate, [33:13] it's going to automatically provide all of the information that our users [33:17] are going to need to retrieve pages of that information. [33:21] So let's go back to the browser, let's refresh, and [33:24] the overall structure is slightly different, we still have our data. [33:29] But notice that we only have 15 items within the data. [33:33] Then we have the links that a client can use to navigate to the first page, [33:38] the last page, and the next page. [33:41] And then there's some metadata about the information in this particular page. [33:45] So of course, if we change this to where we want page number 10, [33:50] then we are going to get the data for page 10. [33:54] And we, of course, want to essentially do the same thing for our invoices [33:58] even though we don't have anything set up as far as the controller is concerned, but [34:02] we can go ahead and create those resources. [34:04] So that first, we will have the invoice resource, and [34:08] then we want an invoice collection. [34:12] And this one this is really important because the invoice had the build date and [34:17] the paid date. [34:18] And does, of course, add underscores. [34:21] So if we open up the invoice resource, we can change the return array for [34:26] the to array method, specify the properties or [34:29] the fields that we want to expose and how we want to expose them. [34:34] And then we just need to implement that inside of the InvoiceController. [34:38] And of course, the first thing we need to do is import those classes. [34:42] So let's copy what we have from the customer controller, and [34:46] we will make the necessary changes. [34:48] And for the index, we will simply return a new InvoiceCollection, [34:54] where we will paginate the data from our invoice model. [34:58] And let's go ahead and implement the show method as well. [35:02] So you'll simply return a new InvoiceResource, [35:05] where we pass in the invoice, and we are good to go there. [35:08] So let's just very briefly check that out, make sure everything's working okay. [35:13] So if we go to Invoices, we should see our data for [35:16] the invoices as well as the links and metadata, we do. [35:20] If we visit the page, let's look at the format, [35:24] we should see everything in camel case, we do. [35:28] BilledDate and paidDate are camel case. [35:31] And of course, if we go to an individual invoice, we will see the same format. [35:37] So now that we have the basics working, we need to start focusing on [35:42] retrieving dynamic data such as querying and sorting. [35:46] And we will start looking at that in the next lesson. [35:48] [MUSIC] [35:54] One of the reasons why we have web APIs is to provide access to data. [35:58] And if you're going to provide access to data, you also need to provide the ability [36:03] to filter that data, because who wants to try to find a needle in this haystack? [36:08] I don't. [36:09] Now, you could say that we also need to provide search, [36:13] because search is different from filtering. [36:16] However, over the many years of running software, [36:18] I've come to find that I really don't need searching when it comes to an API. [36:22] There's been a few times I needed that functionality, but for [36:26] the most part, filtering works just fine. [36:29] So then the question becomes, how are we going to do this? [36:32] Because if you do any kind of research on API design, [36:36] you're going to find a ton of different options and opinions. [36:40] But this is what I've settled on. [36:42] So first of all, we only want to filter things that handled GET requests, [36:47] and only those GET requests that return a collection. [36:51] That makes sense. [36:53] So for our case, it's going to be customers and invoices. [36:57] We also want something that is going to be reusable so that we don't have to write [37:01] a lot of code repeatedly for the same functionality, because as our API grows, [37:06] we want to provide filtering wherever it makes sense to. [37:09] But now comes time to the syntax of the query string because there's [37:14] a lot of different opinions. [37:16] But what I've settled on for APIs written with Laravel is something like this. [37:22] We'll first begin with whatever fields that we want to filter on. [37:27] So let's say that we want to filter based upon the postalCode. [37:32] And I know that this isn't a great comparison, but [37:36] we only want the customers that have a postal code greater than, [37:41] well, what amounts to be 30000. [37:45] The postal codes are strings. [37:47] They are typically five digits long, at least in the US, but they can be longer. [37:51] And a lot of times, they contain a dash. [37:53] So yes, this is not a very good comparison, but just run with me here [37:58] because Laravel will take this and turn it into an array automatically. [38:03] There's no parsing that we have to do, Laravel just does it automatically. [38:07] Whereas if we used something like colons to where we would have postalCode, [38:13] then we would have the operator and then the value. [38:17] We would have to parse that out. [38:18] And you might like to parse strings, I don't. [38:21] So this is what we're going to go with. [38:24] And since this can be a fairly complex type of thing, [38:28] I'm going to write the code that I, [38:31] not necessarily that I want to use but what we're going to start with. [38:36] And then we will massage it into something that will be much better to use. [38:40] So the first thing we are going to do is add to the Request [38:45] parameter to our index because we want to take this request and [38:50] pass it on to whatever is going to build whatever we need for [38:54] filtering so that we will need to create a new object. [38:58] We'll call this CustomerQuery. [39:01] And then we will have the items that are going to be built from this class. [39:07] The filter will have a method called transform, [39:10] to where we will pass in the request. [39:13] And then this transform method is going to give us an array that we can just [39:18] straight up pass to the where method. [39:20] So this means that it's going to be an array that's going to have the column, [39:25] followed by the operator, and [39:27] then whatever value is going to be used for that comparison. [39:30] And this is actually going to be an array of arrays that contain that information [39:35] because we want to provide the ability to filter on multiple fields at [39:39] the same time. [39:40] So, this queryItems is going to have this kind of structure so that we can just [39:46] straight up pass it to the where method, and that's going to give us our filter. [39:51] Now, of course, queryItems might be an empty array, so [39:55] we do need to check for that first. [39:58] And we can very easily do that with count. [40:00] So if that is 0, well, then we want to continue doing what we are already doing, [40:06] just taking the data straight up from calling paginate. [40:10] Otherwise, we essentially want to do the same thing. [40:13] But instead of calling paginate right after that. [40:17] We'll call where we will pass in the query items and then we will call page eight. [40:22] So, that's the goal. [40:24] I mean, yes, we want this to be much cleaner. [40:27] But at least in this lesson, this is what we are going to implement. [40:30] So let's create a new folder inside of app. [40:35] And let's just call it services. [40:37] And since this is something that could be different based upon [40:41] the different version of the API, we need to version it. [40:45] So, we will have V1. [40:48] And then inside of here we will have a new file. [40:51] We'll call it customer query. [40:53] And let's start with the namespace. [40:56] That is going to be App Services V1. [41:02] And since we need access to the request, we'll go ahead and [41:07] pull that in as well, that is inside of illuminate HTTP and request. [41:12] And then we can just write our customer query. [41:16] Now, the very first rule of handling user input is to not trust user input. [41:22] And that is exactly what we were working with anything [41:25] in the query string is user input. [41:27] So, really we want to make sure that whatever is in the filter is [41:32] what we support. [41:33] And one of the best ways to do that is to have something that we can opt into. [41:37] Kind of have the fillable inside of our model classes which we haven't set up. [41:44] But if we wanted to we could come in here with that fillable and [41:48] we could say that certain columns are fillable. [41:51] It would be the same kind of thing, but [41:54] we would say that these fields are safe fields or allowed fields. [42:00] I guess we can call it allowed pares. [42:02] And then, this would of course be an array, [42:06] that would contain the fields that we would allow to filter on. [42:10] But really we need a little bit more than that because there are some fields that [42:15] makes sense for certain comparisons like every one of them can be an equals but. [42:19] Yeah, when it comes to the city a greater than or [42:23] less than doesn't make sense for city. [42:26] So, we also want just the allowed operators as well. [42:30] Now, postal code I'm going to leave with greater than if we have greater than [42:35] I guess we need less than as well, but that's the idea. [42:38] So with that in place, I'm just going to paste in all of the fields for [42:43] our customer. [42:44] So, everything will allow equal postal code [42:47] includes the greater than, and the less than. [42:52] But really, we also need something that's going to transform our fields into [42:56] the database columns. [42:58] And for the most part, we don't need to worry about that. [43:00] But our postal code is one of those that we do. [43:03] So, it would make sense to have a column map as well. [43:09] And for this particular case, [43:11] our column map is just going to have that one for the postal code. [43:16] So, the key could be the field from the query string. [43:20] And then the value could be the actual column name that we would need to provide [43:24] the database. [43:25] And that is postal code or postal column. [43:29] Then we also need some way to transform the operators that we're using from [43:34] the query string into the operators that eloquent, is going to need. [43:40] And we can do that with another map called an operator map. [43:44] So, I'm just going to paste that in because here's quite a bit there. [43:47] Now this is just the bare bones there are many different operators that [43:52] a SQL database supports, such as the in operator and the like operator. [43:57] So, if we wanted to supply that functionality we can do that as well. [44:01] So, then it just comes down to the actual functionality of [44:06] transforming the request query string into an array that we can pass on to eloquent. [44:13] So, here we are going to accept the request. [44:16] And we are going to build an array which I'm just going to call eloQuery. [44:21] The idea being that this is going to be the array that we are going to pass to [44:26] eloquent. [44:26] So, we will create that then we will return that and [44:30] then we just need to build that array. [44:33] So, the first thing we're going to do is iterate over our safe parms, [44:38] because it makes more sense to iterate over what is safe. [44:41] As opposed to iterate what is inside of the query string. [44:45] So, we want to iterate over our safe parms as. [44:50] And we need both the field name, which I'm going to call parm. [44:55] And the set of operators that are allowed for that field, because we need to [45:00] check not just the parameters, but the operators that are being used. [45:04] And then, we will get whatever is in the query string for this particular field. [45:11] So, we'll do that by passing in the parm, which is our field name. [45:15] And it's entirely possible that we don't have anything to work with. [45:18] In which case, there's nothing else to do, so, we'll just simply continue. [45:24] Otherwise, we have some actual stuff that we need to work with. [45:28] So, let's first of all get the column that we are going to use in our query. [45:34] So, we will use our column map and we're going to pass in the field name. [45:39] Now it's possible that we don't actually have a value there. [45:42] And in fact in this particular case most of the time that will be the case. [45:47] So, we do need to provide a default value which is of course going to be the field [45:51] name itself. [45:51] But then we need to filter the operators. [45:55] So, we're going to use another for each and [45:58] we need to check if we have that operators. [46:01] So, we'll use this set to do that and if this operator is allowed for this field. [46:09] Well, then we want to add an element to our eloQuery. [46:13] And it needs to be in the format of an array, where the first element is [46:17] the column followed by the actual operator that we want to use. [46:21] So, we will use the operator map here, we'll pass in the operator that we need. [46:26] And then we need the value that's going to be used for that comparison. [46:30] And that is from our query. [46:32] And that should do it. [46:34] We return the eloQuery array, and we're good to go. [46:39] So, all we really need to do then is, import this class inside [46:44] of our controller and everything should be ready to go. [46:49] I guess we'll find out Okay, so we'll have the customer query there. [46:53] We are creating that filter. [46:55] We're transforming it. [46:57] We are checking if we have any valid filter options. [47:02] Everything looks good. [47:03] So, let's go to the browser. [47:05] First of all refresh make sure that we didn't break anything. [47:08] And of course, I did not use the use statement. [47:13] That's kinda important there, so, let's refresh. [47:15] And now we have an undefined variable query item, that's because [47:20] it's supposed to be query items, let's refresh, there we go. [47:25] Okay, so our basic query, which is not a query works just fine. [47:31] Let's do some filtering. [47:33] So, let's first of all filter based upon the postal code [47:38] is going to be greater than let's see what we have here, [47:42] we have something that's eight to let's do greater than 30,000. [47:49] That is five digits long. [47:51] So the item like Nicolas Lachman, there. [47:55] An aging or in brackets those should not be in the result set. [47:58] So, let's hit enter let's see what happens and [48:02] something just blows up operator map and that's an array that's not a method. [48:07] So, let's change that and then we'll go back. [48:12] Let's see is this the third time, I don't remember, whatever time this was the term [48:17] because there we go, we can see that we no longer have those entries. [48:21] But let's do this, if we look at the type, we have several there's a business, [48:26] there's a business, there's a business, let's do a filter just for an individual. [48:32] So we'll add that as another query parameter to [48:36] where the type is going to be equal to i. [48:40] And all of the business entities should not be there, everything is an i. [48:46] So it looks like our filter is working just fine. [48:48] So there's a few things that you might be screaming at me. [48:52] First of all, we don't have the ability to do an or filter, and that is correct. [48:59] Unfortunately the query string in the URL lends itself to only allow and queries. [49:07] So if you wanted to include an or there would need to be some way that we do that [49:11] through the syntax of the query string, and [49:14] frankly I don't have a good way of doing that. [49:16] But that's also just the nature of URLs and query strings, [49:21] it is implicitly in and query, so there's that. [49:24] Second of all, is this is not reusable at all, and [49:29] you're correct I mean, we could copy and paste. [49:34] But really what we want to do is break this out into a base class, [49:40] so that we could then use that base class to implement a much smaller class for [49:47] the invoices, and we will do that in the next lesson. [49:52] [MUSIC] In the previous lesson we created a class [49:55] so that we can filter the results of our customers endpoint, [49:58] and it works rather well, if I do say so myself. [50:01] And so we want to replicate the same functionality not just for the invoices [50:05] endpoint but for any other endpoint that we would implement in the future. [50:10] So really we want to extract all of the key functionality from this customer query [50:15] class into a base class so that we can use that base class whenever we need to. [50:20] But first I wanna make a few changes, [50:23] instead of calling the services I want to rename this folder to filters. [50:28] Because we could make the argument that this is a kind of service class. [50:32] But I think we would be better served if we keep all of the filters together and [50:36] separate from everything else. [50:38] Especially as we implement more versions and things like that. [50:42] So there's a few changes I want to make such as the namespace and set of services, [50:48] we'll have filters, let's change the name of this class as well to customers filter. [50:54] That way we know that this is a filter for customers, [50:58] let's also change the name of the file, so that is apparent. [51:03] And so the idea is that this will extend, we can call this API filter and [51:09] ideally API filter will not be versioned. [51:13] We'll start with that and if later on we decide that we need to version this base [51:17] class then we can do that. [51:19] So let's create that file outside of our v1 folder, so [51:23] this is going to be directly inside of filters. [51:26] And really, we can start by copying and [51:30] pasting everything from the customers filter class. [51:36] So let's do that and then let's just make a few modifications. [51:40] The first is, of course, going to be the namespace, [51:44] we don't need the v1 there, the name of the classes API filter. [51:49] And as far as all of our arrays are concerned, we just need them to be empty. [51:55] So we will delete everything inside of them, [51:58] you could make the argument that operator map, [52:01] should have at least these base operators but we're gonna make this empty as well. [52:07] Again, if we decide that we want to implement that we can implement that. [52:11] And there we go, we have our API filter. [52:13] Now, we do need to import this inside of our customers filter, [52:19] so let's go ahead and do just that. [52:22] And then we will need to modify our customers controller. [52:28] So let's do that before we implement the invoice [52:31] because after all this is going to be very quick. [52:35] So let's open up that file we'll change the services to filters, [52:40] the customer query to customers filter and then we will use that class. [52:47] Now, one thing about this class, we could make the argument that we really [52:52] don't want to new up the constructor so that we could use the filter. [52:57] And it would be nice to have a façade instead, however, [53:01] there's a lot involved with creating a facade. [53:05] In fact, [53:06] we would have to then weigh what is more valuable as far as our time is concerned. [53:11] Setting up a facade so that we can not new up this constructor or [53:15] to just new up the constructor. [53:17] Of course, we could also use the service container so [53:21] that it would be injected, but there once again, [53:25] we are dealing with a lot of setup for not really a big payoff. [53:29] I think I'm just going to leave this as is, it works and [53:32] there's a lot to say about code that works as we can see here, so we're good to go. [53:38] Now, we just need to essentially recreate customers filter, but [53:42] it will be for the invoices. [53:44] So let's just copy and paste that file, let's rename the copy to invoices filter. [53:51] And it's been a while since we have looked at the fields that we have for invoices. [53:55] So after we change the name of this class, [53:59] let's open up the migration for the invoices table so [54:04] that we can take a look at those and there are several. [54:09] So let's just copy this so that we can have a reference to it inside of our [54:14] invoices filter and then we will just need to set up the map. [54:18] And we will start with the column map, and we have three, [54:23] we have customer ID, build date and paid date. [54:27] So let's just make the necessary changes, and then let's set up the safe parms, [54:31] which I think we called allowed parms or something like that. [54:35] I probably paste it in the wrong variable name, but, well that's gonna be fine. [54:40] So we have our customer ID, we'll have amount, [54:44] we'll have status billed date and then paid date. [54:49] We do not need anything else. [54:51] Now, as far as the operators are concerned, equals is going to work for [54:55] all of them. [54:56] However, I would say that amount needs, well, all of the operators, [55:01] because it makes sense to want to be able to filter whether or not the amount is [55:06] less than or greater than, or less than or equal, or greater than or equal. [55:11] And really, we could set the same thing for the billed date, [55:15] as well as the paid date. [55:16] And it would also be useful to have a not equal operator for like the status. [55:22] Because it would be useful to say that we want to find the invoices [55:27] that are not paid, or that are not void, or that are not billed. [55:32] So we will call that ne, and we of course, need to set up a map for [55:38] that, which is going to be the traditional PHP not equal, and that will work. [55:45] So now, all we have to do is reference the, [55:49] we need to delete the transform method, don't we? [55:52] So that means we need to do the same thing inside of customers filters, so [55:57] let's delete that. [55:58] And then we will open up the invoices controller so [56:02] that we can use our filter there. [56:05] And really, we can just copy and paste from our customer controller, so [56:09] let's do that. [56:10] And then we will make the necessary changes because we do need to implement [56:15] the index method. [56:16] In fact, let's just copy all of the index methods so that we don't have To [56:21] manually type in the request parameter, but of course the name of our class [56:26] is invoices filter, and then we will just paste that wherever we need. [56:31] So let's hop on over to the browser and let's duplicate this tab so [56:36] that we can have both the customers and the invoices up. [56:40] And we will do just the straight up invoices, so [56:43] that we can first of all see all of the results, there we go. [56:47] Let's say that we want to find the invoices that are not paid. [56:52] So that means we are going to filter based upon the status that is not equal to P. [56:57] And the reason why I picked that is because the invoice with the ID of one was [57:01] paid and we can see that that is no longer there. [57:04] So our filter for our invoices is working just fine. [57:08] Now there's one other thing notice that the links that are provided do not contain [57:13] the query string. [57:15] So if we navigate to any one of these, then we're going to lose the filter. [57:20] And that's easy enough to fix. [57:22] What we will need to do is first of all, do the same thing that we were doing [57:27] before, by calling layer passing in the filter and then calling paginate. [57:32] But what we're going to pass to the invoice collection in this case is [57:36] going to be invoices. [57:38] And we are going to call the append method, [57:41] because we want to append the query string. [57:44] So we will get that from the request. [57:46] We will call query and that should work. [57:49] Let's go back. [57:50] Let's refresh, make sure that that does indeed work. [57:53] It does. [57:54] So now we just need to replicate that on the customer controller. [57:59] So let's do the same thing where we are going to first of all get our [58:03] paginated results we'll just store that in a variable called customers. [58:08] And then whenever we use our customer collection we will use [58:14] our customers call appends to where we will append the query data. [58:20] So let's go back to the browser, let's go to our other tab. [58:24] Let's make sure that that is indeed the case that yes we do not [58:28] see the query string included. [58:31] So by refreshing that should be there, and it is. [58:35] So, we are now successfully filtering both our customers and invoices endpoints. [58:40] In the next lesson, we are going to implement the ability to include [58:44] related data such as including invoices with our customers. [58:48] [MUSIC] [58:54] Sometimes it makes sense to include related data. [58:57] Like for example, I think it makes sense to include the invoices, [59:01] whenever we request our customers. [59:02] But we don't want to do that for every request. [59:05] We want the client or the user to specify when they want that information. [59:11] So we could do something very simple by having this include invoices [59:16] query parameter. [59:17] If it's true, then of course we include it. [59:19] If it's false, or if it's not there, then we of course, don't include the invoices. [59:23] And there are many different ways that we can approach this type of functionality. [59:27] But I want to caution you, [59:29] it's very easy to get excited about implementing features. [59:33] But it's very easy to over engineer your application and [59:37] develop features that well they just aren't used. [59:41] So if my experience has taught me anything it is that build an application or [59:46] build software with basic functionality, [59:48] and let the users tell you what features they want. [59:52] Because you could spend a lot of time implementing features that aren't used, [59:57] and then you end up with a bunch of code that's just cluttering up your code base [60:02] and it's just a mess. [60:03] So in this lesson we are going to take that approach. [60:05] We're going to build the basic functionality of including the invoices or [60:10] not including the invoices. [60:12] So let's go to our controller and let's start implementing that feature. [60:17] So we will just call this include invoices and [60:20] we're going to check the query string for that. [60:24] Now we are already branching our code once based upon our query items which [60:29] really that should be called filter items that makes a lot more sense. [60:35] So if we don't have any filter items then of course we just start a call paginate. [60:41] If we do have query items then we pass that to where and then we call paginate. [60:47] But here's a quick little tip when it comes to queering with [60:51] eloquent if you pass in an empty array, [60:54] there is literally nothing to execute as far as where is concerned. [60:59] So in this particular case if we passed an empty array to where, [61:04] it's almost like we aren't calling where at all and we are just calling paginate. [61:10] So we could simplify this so that we don't have to check if there's any filter items, [61:15] instead we'll just pass the filter items on to where. [61:19] If there are any filter items that great if not, that's perfectly fine as well. [61:24] So this is going to give us the ability to essentially build our query and [61:28] include the invoices if we need to. [61:30] So, the first thing that we will do is simply [61:34] call where we will pass in the filter items. [61:37] And then we will check if we have the include invoices in which case we will [61:42] simply include them. [61:43] We do so by calling with and then specifying our relationship invoices. [61:48] And then when it comes to calling paginate we will do that [61:52] down here whenever we pass our collection to the customer collection. [61:57] So we will have customers and then paginate and [62:01] then the pins and there we go. [62:03] So that should be great. [62:05] Let's go to the browser. [62:07] Let's add true to this. [62:10] And let's execute. [62:12] And column not found, column zero where clause. [62:16] And that's the problem on line 26, [62:20] we are wrapping filter items with an array that's not right. [62:25] So there we go we refresh and we don't see the invoices. [62:30] Well the reason is very simple because remember that the payload that [62:35] has been sent in the response is actually being controlled by our [62:39] customer collection resource class. [62:42] And really, by extension, the customer resource, and [62:46] we don't list the invoice here at all. [62:49] So of course, we want to include the invoices, but [62:53] we only want to do that when we have that data to display. [62:56] And to makes it very easy to do that. [62:59] We are simply going to use our invoice resource. [63:04] And we're going to call the collection method now, [63:07] this is going to essentially use the invoice collection that we wrote [63:12] which is of course going to use the invoice resource. [63:15] But what we're going to pass through this collection method is indeed the invoices, [63:21] but we only want to do that when the invoices have been loaded. [63:25] So we will use this when loaded method and that's the magic behind this working. [63:31] So if we go back to the browser and [63:34] refresh we are now going to see our customers with the invoices. [63:39] If we take out that query parameter, then we only see the customers. [63:45] So now we have that functionality and really, we want to implement that for [63:49] the individual customer as well. [63:52] So let's duplicate our tab and let's just go to the customer [63:56] with an ID of nine because that's what's on top. [64:00] And there we go. [64:01] Of course, we haven't implemented this code, but let's go ahead and [64:05] let's include invoices here. [64:07] We'll set that to true. [64:09] And we will essentially do the same thing. [64:11] So let's copy this line where we are going to include invoices. [64:18] And let's go down to our show, but this is going to be a little bit different because [64:23] we aren't building a query here, we already have our customer. [64:27] So, we are going to do this. [64:29] We'll first of all check if we want to include the invoices. [64:34] And if so, we are still going to return this new customer resource. [64:39] But then, we're going to call this load missing method on our customer object. [64:45] We want to load the missing invoices, and that's going to make that work. [64:50] And then of course if we don't want to include invoices, [64:53] we just return the customer as normal, and everything is fine. [64:56] And since we've already changed our customer resource, [65:00] we are going to see this work so that we have an undefined variable of request. [65:05] And that's because we don't have request. [65:07] So, let's use the request function here. [65:10] Let's go back, let's refresh, and here we have the customer information. [65:16] And then we see his invoices. [65:18] If we take out that query parameter, then of course we will only see the customer. [65:24] So of course the magic is inside of our resource class. [65:28] By using this when loaded method, we are able to conditionally [65:33] include the related information for our resource. [65:37] [MUSIC] [65:42] We are finally to the point where we can start manipulating our data. [65:46] So in the next lesson, we are going to be looking at creating editing, and [65:51] updating our resources. [65:52] Now, before we begin, [65:54] it would be useful to have a client that we can use to issue HTTP requests. [66:00] Now, yes, we do have a browser and we could technically do that. [66:03] We could pull up the console, and then issue requests using JavaScript, but [66:07] that's a little bit more work I want to do. [66:10] Now I'm going to be using postman, which is cross platform. [66:14] However, there are many other options available. [66:17] I'm a Windows guy, so when it comes to doing anything with HTTP, I typically [66:22] go for Fiddler, because Fiddler Classic is free, and it's Windows only. [66:27] However, there is a Fiddler version that is cross platform, [66:31] it's just not free, unfortunately. [66:33] But of course, feel free to use whatever tool that you are used to using. [66:37] All that matters is that you are able to issue whatever type of request [66:42] that you need, and provide the data for that request. [66:45] So with that out of the way, we are going to start with our customer controller. [66:50] And remember that this is an API controller. [66:53] So there are a couple of methods that aren't set up as far [66:56] as routes are concerned, and [66:58] we no longer need them, such as the create method, and the edit method. [67:03] So let's just go ahead and get rid of those. [67:06] And one of the great things about using Laravel to develop a restful API is that, [67:11] we can use all of the tools that are available for [67:14] typical Laravel applications, such as request classes. [67:18] So, we can create a class called store customer request, and [67:22] we can put all of the validation rules inside of that class. [67:27] And Laravel is going to automatically handle all of that for us, so that at [67:31] the end of the day, all we really need to do is return a new customer resource. [67:36] Because that's typically what we would do for [67:39] a post request, we would return the new entity, and [67:43] then we would just need to call the create method on our customer model, [67:47] pass in all of the data from the request, and we would be good to go. [67:51] Now of course, there's a few things that we need to do to make this work. [67:56] For example, we need to specify the fillable array inside of our customer [68:00] model, so let's do that. [68:02] Now always be careful when you specify the fields that you want to be fillable. [68:07] Really think about it because it could have some [68:10] pretty damaging effects if you're not very careful. [68:14] In the case of our customer model, [68:16] it's okay if all of the fields are fillable, I'm fine with that. [68:20] So, let's just say that we'll close that, and we'll be good to go. [68:24] Then we're gonna hop on over to the command line, and [68:27] we're going to use artisan to make a new request called store customer request. [68:32] Now this does need to be versioned because this is data coming from the client, [68:37] and that means it's data for a specific version. [68:40] And of course artisan is going to create that folder, [68:43] create the request class inside of that folder, so that we don't have to do that. [68:48] So, inside of app HTTP requests v1, we have this new store customer request. [68:56] Now the first thing I'm going to do, is flip this authorized to true. [68:59] Now, of course, yes, we only want authorized users to create a customer. [69:05] However, we don't have that capability yet. [69:08] So, we need to be able to test this first. [69:11] And then we want to specify our validation rules. [69:13] And I'm going to paste this in because it's quite a bit of typing. [69:17] But for the most part, it's very straightforward. [69:20] All of the fields are required, [69:22] because we have no optional fields when it comes to creating a customer. [69:27] But the type and the email are a little bit special. [69:30] The type is special, because we only have two types, really, [69:33] we have an individual in the business. [69:35] So I'm using the n-rule here, so that if the client provides anything other [69:40] than i or b, then it's going to be rejected. [69:43] And then the email field is, well, it needs to be an email. [69:47] So we're using that validator as well. [69:50] Now because I'm using rule here, we do need to import that class, [69:54] that is inside of illuminate, validation, and then, rule. [69:58] But, we're not done here, because remember postal code. [70:01] Now it's fine that it's camel case for our rules, [70:04] because that is the information that is coming from the client. [70:09] However, we need to be able to transform that into postal underscore code, [70:14] so that we can properly store that in the database. [70:17] And one of the ways that we could do that, [70:21] is by using the prepare for validation method. [70:26] Because this allows us to merge in other values such as postal_code. [70:32] So we're going to use the merge method. [70:35] The array that we're going to pass to it will have a single key called, [70:40] guess what, postal_code. [70:43] And we're going to say that it's going to have the value of postal [70:48] code that came from the request. [70:50] So, there we go, that's going to allow us to get that information into the database. [70:54] And so now we just need to import this class into our controller. [70:59] So, let's go over there, we will add the use statement for [71:04] our namespace, and then the store customer request class. [71:08] Now we've already used that in the store method, so we are good to go. [71:14] We can hop on over to postman, and we should be able to create a new user. [71:19] Let's first of all make a request for just the customers endpoint, just so [71:23] that we can make sure that's everything is going to work, okay? [71:26] It should, because we really didn't change all of that much. [71:29] There we go, we see our data. [71:31] And now we're going to change this to a post request, [71:34] which means that we need to include a body. [71:36] I'm going to choose the raw option, and choose JSON. [71:41] And I'm going to paste in a valid JSON payload. [71:45] However, before we actually store this new entity in the database, [71:49] I want to test some of the validation rules. [71:52] Like for example, [71:53] I want to make sure that we cannot create a customer with a type of z. [71:57] So, whenever we send this, [71:59] what we're actually going to see is the result of viewing, the welcome view. [72:04] That is the markup that we're seeing if we look at the preview here, [72:08] that's exactly what we see. [72:09] And at first, we might think that we did something wrong, and we kind of did. [72:14] But remember that the basic functionality of Laravel is request validation, [72:19] is to redirect. [72:20] Usually, it is to the Create or the Edit action. [72:24] And we don't have those inside of our controller, so [72:27] it's just going back to index. [72:29] Now, we could change our code. [72:30] However, really what we need to do is address the headers that we made with [72:34] the request. [72:35] Because if we look on down at the except here, now our content type was correct. [72:42] It was set to application/json because I chose JSON as the data type. [72:47] However, the accept is set to accept everything. [72:51] And we don't want that, we want to only accept JSON. [72:54] So we're going to disable the accept that it added to that request. [72:59] We're going to add it back. [73:00] But in this case, we will say that we only want application/json. [73:04] So that whenever we submit that same request, we are going to get a response [73:09] back that says that the given data was invalid and the selected type is invalid. [73:15] So that makes sense. [73:16] We do not allow any other type than i or b. [73:21] But let's also remove the name, and let's see what happens there. [73:25] We will essentially see the same thing. [73:26] It will still say that the given data was invalid, but [73:29] then it says that the name field is required. [73:31] So our validation appears to be working. [73:34] We could test all of the fields that we can just assume that everything [73:38] is going to work just fine. [73:39] So with a valid payload, let's submit that request, and this should work. [73:45] Although, we get a response back that the column not found postalCode. [73:53] Where was postalCode used? [73:54] I know that we use it inside of the customer request to merge in [73:58] the postalCode. [73:59] So, that means we need to look at the model, and I bet that's where it is. [74:04] And yep, that's it. [74:08] So we are going to change that to be the actual column name as it should have [74:12] been to begin with. [74:14] We can go back to Postman, and we can resubmit this request. [74:18] And we should get back the entity that was created. [74:21] And there it is. [74:22] So we now have a new customer, the id is 231. [74:26] So if we wanted to hop on over to Firefox, and let's make a request for [74:32] that, we should see that customer, and we do. [74:36] But let's say that's, we made a mistake, [74:38] we need to change some of this information. [74:40] Well, that is where we can issue a put or a patch, and [74:44] we will see how to do that in the next lesson. [74:47] [MUSIC] [74:54] In a typical web application, we provide the ability to edit an entity or [74:58] a resource by loading the data into a form so [75:01] that the user can make whatever changes that they need to. [75:04] They submit the form, and then we, of course, [75:07] save those changes into the database. [75:09] But a RESTful API is very different. [75:12] We actually have two types of edits. [75:14] The first uses a put request, which is to replace an entire entity. [75:20] So this is the customer that we created in the previous lesson. [75:24] It has an id of 231. [75:26] So if we wanted to edit this customer with a put request, [75:30] our request would need to include everything, the name, [75:34] the type, the email address, and everything. [75:37] We cannot leave anything out because we need to provide the values that are then [75:42] going to replace the values for name, type, email address, city, and so on. [75:47] Now, the values could be the same thing as what's in the database, but [75:51] the idea's that we have to supply all of the values. [75:55] Now, the second type of edit uses a patch request, [75:58] which is more along the lines of what we typically think an edit is. [76:02] So that if we only wanted to change the name, type, and email, then we only supply [76:06] the name, type, and email, and those are the only properties that would be updated. [76:11] And that's all well and good, except that when it comes to Laravel applications, [76:16] we have just a single update method, and this method handles both put and [76:21] patch requests. [76:22] So the request class that we are going to create needs to handle both of [76:27] those types of requests, which sounds difficult, but it's not at all. [76:31] So let's first of all make that request, we'll call it UpdateCustomerRequest. [76:37] Now, we do need to version this. [76:39] And we're going to start by copying and [76:42] pasting pretty much all of the code from our StoreCustomerRequest. [76:46] Because the rules that we have defined are the same rules that we need for [76:50] a put request. [76:51] Because remember a put request requires us to include all of the properties, [76:56] so we need all of the rules. [76:58] So let's copy everything from authorize to [77:01] rules to even prepareForValidation because we need all of them. [77:05] And then we are going to simply paste those into our update request. [77:10] And then inside of the rules method, what we're going to do is [77:15] check the HTTP method, and we do that by using the method method. [77:20] This gives us the HTTP method that is in all uppercase. [77:24] So if you want to compare with PUT or PATCH or whatever, [77:28] you need to be sure to use all uppercase here. [77:31] So for a PUT request, we want to perform the same validation that we did for [77:36] creating our customer. [77:38] And that's all well and good. [77:40] Now, from here, we could assume that if we are inside of this class, [77:46] then chances are pretty good that we are in an update. [77:51] And so we could just go ahead and use an else here because the idea being that [77:56] it's either going to be a put or a patch, and I think it's safe to do that. [78:00] So we are going to leave it like this, and [78:03] we are going to essentially use the same rules. [78:07] However, we are going to add the sometimes rule to each one of these properties. [78:13] The idea is that if name is not there, then it's not going to be validated. [78:19] However, if name is in the payload, then it is sometimes going to be there. [78:24] And when it is, then we'll validate it according to the rules that are specified. [78:29] So, this is really it. [78:31] All we've to do is use the sometimes rule for each one of these properties, [78:36] and that is going to allow us to validate for a patch request. [78:41] Everything else is going to be the same. [78:44] So we just need to import this class, and then we, of course, [78:48] want to use this class for the type hint for our update method. [78:53] And there we go. [78:54] So that now all we need to do is use the customer that we get, we will call update, [78:59] and then we will simply pass in all the data from the request. [79:04] And that's going to work just fine. [79:06] So let's head over to Postman, let's first of all issue a request for [79:11] the ID of 231 because that is what we need to make our patch and put requests for. [79:17] We want to send those requests to whatever resource that we want to update. [79:21] So here's our customer that we created, id 231. [79:25] And let's start with a put request. [79:27] So I'm going to paste in the payload that we use from the previous lesson. [79:33] But let's test everything. [79:35] Let's change all of these values. [79:37] So this is going to be me2@tutsplus.com. [79:41] We'll change the address to 4321 wherever, some town can become some city. [79:47] Let's change Texas to something else, [79:51] like Oklahoma, O-K-L-A-H-O-M-A, Oklahoma. [79:55] And this one, we will change the postalCode. [79:59] Let's just reverse it, 54321. [80:00] Let's submit, we need to change that to a PUT request. [80:04] We need to make sure that we have the appropriate headers because we don't [80:09] want to recreate the issue of being redirected because validation failed. [80:14] It shouldn't fail, but still, the accept is application/json. [80:19] And let's just send this. [80:22] So that we shouldn't get anything back from the API because a put or [80:26] a patch request typically doesn't give us anything. [80:30] However, we need to import that rule class, don't we? [80:35] So we need to copy that from the store customer request class. [80:40] We will paste that into update. [80:42] And then we will go back, let's reissue this request. [80:46] And everything should be fine then, and it is, we're good there. [80:50] However, let's go to Firefox and let's take a look at our user, [80:55] all of the values should be updated because we changed them all. [81:00] So that's great, but let's say okay, we made a couple of mistakes. [81:05] It is an individual so we need to change the type, we don't wanna live in Oklahoma, [81:10] so let's change that back to Texas, everything else we can leave the same. [81:14] So, I guess we should change the name. [81:17] So, let's do that. [81:19] We'll change the type back to I, and then the state is gonna be Texas. [81:24] So, we're changing only three things. [81:27] We can get rid of everything else actually, or we could include them if we [81:31] wanted, but that defeats the purpose of testing a patch request. [81:35] So, let's change the type two patch, let's submit that request. [81:40] Once again, we should not get anything back from the server. [81:43] However, the status should be 200, but it's not, it's 500. [81:47] And we ran into an issue, [81:49] integrity constraint violation column postal code cannot be no. [81:54] That kind of makes sense because if we don't have postal code, [81:57] then we don't need to change anything about the data that we are working with. [82:02] So, let's just do this. [82:05] If we have postal code, [82:07] then we will merge in the new array that has the postal_code key, [82:12] otherwise we won't do anything, that should fix that. [82:17] So let's go back, let's resubmit this request, that error should go away, and [82:22] in fact, we shouldn't get anything back, looks great. [82:25] So, let's go to Firefox. [82:28] Let's request the data once again, and we can see that the name changed, [82:33] the type changed. [82:34] The email stayed the same as did the address, the city, [82:38] the state changed, and the postal code is the same. [82:42] And so now clients can update data in our database using our [82:47] API by issuing both a put or a patch request. [82:51] [MUSIC] [82:56] Sometimes we want to provide the functiontionality for [82:59] inserting records in bulk, and not every API needs to provide that. [83:03] However, I think it makes a whole lot of sense for hours, [83:06] especially if it comes to invoices. [83:08] In fact, I have worked on accounting systems where the person scanning and [83:12] indexing those invoices, will do so in batches. [83:15] And then when they were done with that batch, they would submit it, and [83:19] then it would be saved in the database, that's really an oversimplification. [83:22] But, that's the general idea. [83:24] Now, because we are using resource controllers, [83:27] there isn't a built in method for handling a bulk type of request. [83:31] So we're gonna have to write our own which is just fine, [83:34] there's nothing wrong with that. [83:36] Now, we're going to call it bulk store, [83:38] the idea being that I'm using the verbiage that's already here. [83:42] So bulk store means that we're going to create multiple records in bulk and [83:47] we will have a customer request class to validate this information [83:52] because this is very important. [83:54] Because now that we are accepting data in bulk we want to be very [83:59] sure that before we even try to store something in the database [84:03] that we have our data exactly how we want it. [84:07] But we're going to start using just this generic request object and [84:11] then after we get everything set up we will use our custom class. [84:15] Let's go ahead and set up our route for [84:18] this new endpoint which I guess we could just call bulk. [84:23] So it'll be invoices/bulk, and then we will specify where to route that request, [84:30] that is of course our Invoice controller, and the bulk store. [84:36] Now, we could use the artisan to create art request class but [84:40] I think we will be better served by just taking one of our existing requests and [84:46] copying and pasting and modifying. [84:48] I'm going to use the store customer request because it is primarily designed [84:53] for storing stuff. [84:54] And let's call this bulk store invoice request. [84:59] That's rather a mouthful, but it is very descriptive as to what it is. [85:03] Now, before we start defining our rules, [85:06] I guess we need to talk about what the payload is going to look like. [85:10] So it's basically just going to be an array that has objects representing [85:15] the invoices, so there will be customer ID, and so on and so forth. [85:20] If we have multiple which I would hope we would have multiple, [85:24] then it would just be another element in that array. [85:27] So, one thing to keep in mind is that we are going to validate [85:31] the individual objects inside of an array and I'm using JSON terms here. [85:37] So we're gonna have a JSON array that contains a bunch of JSON objects, and [85:42] Laravel gives us the ability to handle the validation for [85:46] an array of multiple objects. [85:48] And it's going to look something like this, so [85:51] that we want to validate the customer ID property on each object. [85:56] Now remember that we are working with an array, so [86:00] we're going to pre-pend them with an asterix and then the dot. [86:04] And we will essentially do the same thing for all of the other properties, so [86:08] let's just copy and paste a few times. [86:10] I think we have five fields, we have the customer ID, the amount, [86:16] then we have the status, then the build date, and the paid date. [86:21] Now the customer ID is required, and ideally, [86:24] we would also check to see if the actual customer existed in the database. [86:29] Because, If it doesn't, then we're gonna run into problems. [86:32] But we do want to make sure that we have in the integer value because the ID of [86:37] the customer is going to be an integer. [86:39] Then we want to validate the amount which is of course required but [86:44] then it's going to be a numeric value. [86:47] The status will be required, but it's going to be using an in rule, [86:52] where we need to change these values so that we have B for [86:57] build, P for paid, and V for void. [87:00] Let's also go ahead and have the lowercase versions of those and [87:04] then we have the build date, so this is required. [87:07] And we also want to say that we need the date in a specific format, [87:13] which is going to be year, month, day, space, hours, minutes, and seconds. [87:19] And we essentially want the same date format for the pay date. [87:23] However pay date is not required, in fact it can be known, so [87:27] we also want to say that this can be knowable and that is going to be it. [87:32] So whenever you want to validate an array of objects, [87:36] the keys in your rules need to reflect whatever data structure that you're [87:40] going to have since all we have is an array. [87:43] We're going to use a star dot, and then the property name. [87:46] Now, if our data structure looked like this where we had data and [87:51] then that would be an array where we would have the individual objects. [87:56] Then it would be simply data dot star dot, and then the property name. [88:02] Now as far as the prepare for validation, this is where you're taking the values for [88:07] the camel cased properties and assigning them to the underscore properties. [88:12] We'll still need to do that but it's going to be a little bit more involved here. [88:16] That essentially means we need to iterate over each element in each array and [88:21] make those same changes. [88:23] So what I'm going to do is, Build a brand new array we're just [88:28] going to call it data and we're going to use a foreach loop and [88:33] we're going to turn the input that we get into an actual PHP array so [88:38] that we can use it in a foreach and [88:40] then we are basically going to add the underscore fields here. [88:46] So that will be customer ID. [88:48] Now remember that this is prepare for validation. [88:52] So validation hasn't occurred yet and it's very possible that the payload does not [88:56] include the customer ID in which case this would actually cause an error. [89:01] So, what we want to do then is if there is not a customer ID in the payload, [89:06] well of course that's invalid, [89:08] but we need to get to the point to where we can validate the payload. [89:12] So, we're going to go ahead and assign customer ID as null and [89:16] if validation fails, then that's okay. [89:19] Nothing's lost or gained and essentially the same thing for the other fields. [89:24] And then once we set these new keys, [89:27] we are going to add this child array into our data array, so [89:32] that after the loop is done then we will merge in that new data that we have and [89:39] that is going to give us exactly what we want. [89:43] Except that we're going to run into an issue instead of our invoice controller [89:48] because now we're going to have an array that contains not just the actual column [89:53] names in the database but we're also going to have these Camelcase-keys. [89:58] And the method that we are going to use inside of our bulk store [90:02] which is going to be insert. [90:05] It's going to assume that everything that we're going to pass to it is [90:10] going to have a column in the database that means that it's going to try [90:15] to insert something into a column called billedDate all in Camelcase. [90:20] And that's a problem we will run into an error there so [90:24] now we need to take those out. [90:26] So I'm going to do this, I'm going to create a new array and [90:30] I'm going to turn the request information into a collection so [90:35] that it makes it a little bit easier to work with because collections have the map [90:41] function so that we can essentially create a new array that can change [90:45] just the columns that we need and that's going to look a lot like this. [90:51] So that to inside of our closure, we are going to use the array accept helper. [90:56] And we basically want to say, we want all of the columns [91:01] except these customer ID billedDate and then paidDate. [91:06] Everything else is fine. [91:08] We want to store that in the data base and we will be good to go. [91:12] So that once all that is done then we will use invoice we will call insert and [91:17] we will pass in bulk but we can only pass in an array we can't pass in a collection [91:23] so we will call it toArray method and that will get us what we want. [91:28] Now there are a few things that we need to import here first is that array façade and [91:34] I don't know if we call it array or not. [91:37] I don't want to call it r because that sounds kind of piratey but [91:42] we'll just call it Arr, how's that? [91:45] We need that, we also need to import the request class. [91:50] And we need to use the class name for the type hint on our bulk store method. [91:57] So let's do that there. [91:58] That's very important because otherwise we would not get our validation. [92:02] So now we just need to test this out. [92:05] Now I've gone ahead and pulled up the record for [92:08] the first customer, we have 10 invoices and [92:11] we're going to add two more so we will have 12 after this is done. [92:16] So let's hop on over to postman and let's make a post request to invoices/bulk. [92:23] And there is our payload. [92:25] Let's make sure that we have the application JSON accept header we [92:30] still do. [92:31] And let's, first of all, make this invalid. [92:33] So I'm going to remove the customer ID on one of these and we will submit. [92:39] If everything was typed correctly, then we should get a validation message back [92:44] saying that, the first customer ID is not there and there it is. [92:48] The zero dot customer ID field is required. [92:53] So I find that this is very helpful because whoever is submitting [92:58] this will know exactly where to look for the customer ID. [93:02] Let's put the customer ID back and then let's submit this again. [93:08] And we shouldn't see anything in the response because we didn't return anything [93:12] because I don't really know what we should return for a bulk insert. [93:17] But let's go to Firefox. [93:19] Let's refresh the page. [93:20] We should have 2 more. [93:23] And we do. [93:24] And so that's how you can add the functionality for [93:27] bulk inserting records to your API. [93:30] It's not as straightforward as other types of requests. [93:33] But thankfully Laravel gives us the tools that we need to validate and [93:38] modify the input payload to make it all work. [93:41] [MUSIC] [93:46] Laravel seven introduced a new feature called Sanctum. [93:49] It is a token authentication scheme primarily for API's and [93:53] single page applications. [93:56] And that makes a whole lot of sense because single page applications typically [94:00] use an API in order to work with and manipulate data. [94:04] However, token authentication has always kind of been a thorn in the side of [94:09] developers because a lot of authentication packages want you to use a OAuth, [94:14] and that just complicates the heck out of everything. [94:18] So thankfully, Sanctum doesn't. [94:20] It is an easy to use, easy to implement token authentication scheme that [94:25] ties very nicely into Laravel's default authentication scheme. [94:30] And Sanctum is installed by default and the later versions of Laravel. [94:36] You can check by going to composer.JSON and [94:39] inside of there require section there should be Laravel Sanctum. [94:43] Now if it's not there then you will of course need to install it you can [94:48] use composer to require Laravel Sanctum and [94:51] then after that is installed, you would want to publish it and [94:55] you would use artisans vendor publish command to do that. [94:59] You would specify the provider of Laravel Sanctum Sanctum service provider. [95:05] And then once that's done, you will want to run the migration that is created [95:10] because that is going to add a table to your database called [95:14] Personal Access Tokens and it ties in very nicely with the users table. [95:19] Now of course we haven't done anything with our users but a typical application [95:24] would have some kind of interface so that the admin can manage those users or [95:28] at least the users could sign in and assign themselves tokens. [95:32] Now we aren't going to implement any of that because that would take [95:37] quite a bit of time instead we are going to have a URL called it setup. [95:42] And we are basically going to check to see if a user exists and [95:45] if it doesn't then we are going to create that user so that then we can assign [95:50] some tokens and of course if the user does exist then nothing is going to happen. [95:55] So I've already written the code that is going to create that user [96:00] the next thing we need to do is sign in with that newly created user. [96:05] So let's do that with our credentials, and [96:09] then we will retrieve the user because we need to use its create token method, [96:15] and it would be a lot easier if we just assign that to a variable. [96:20] So we're going to create three tokens, one is going to be the admin token, [96:24] the idea being that this is going to be the token that admins would use. [96:28] That give them all of the capabilities that they would need. [96:33] So we're going to use our user, and we're going to call createToken. [96:38] Then we're going to give this token a name. [96:40] Now, the name really doesn't mean much. [96:42] It's just simply a name. [96:44] And we could leave it like this and create a token. [96:47] However, we can assign capabilities to a token so [96:50] that whenever we receive a request that includes a token, [96:54] we could check to see if there are any capabilities. [96:57] And we can determine whether or not whoever issued that request has [97:01] authorization to perform whatever action that they were trying, [97:05] such as creating something or editing something. [97:08] So since this is an admin, we want to essentially be able to do everything. [97:13] So we can create, we can update, and we can delete. [97:18] Now, I know we haven't implemented deletion because, well, [97:22] it's just kind of straightforward. [97:24] You receive a delete request and you delete the record. [97:27] There's really nothing else there. [97:29] And then we would want one for a user that would be able to update. [97:34] So let's call this updateToken. [97:36] We'll call the name updateToken. [97:38] And they will be able to create an update, [97:40] but we don't want them to be able to delete, and that will be fine. [97:44] And then finally, we will have our basicToken, [97:48] which is just going to be read only access. [97:52] We don't want to provide any functionality except being able to read. [97:56] And we want to sign any capabilities for this token. [98:01] Now, whenever we create these tokens, we, of course, [98:05] are going to get the plain text of that. [98:08] However, what is stored in the database is actually the hash of that key. [98:13] So if you want somebody to be able to use their token, you need to output [98:18] the token because this is the only time that we can get that plain text. [98:24] So we're going to return an array with three things. [98:27] We'll have the adminToken, and we are going to get the plainTextToken, [98:32] that is the actual token that will be used with the request. [98:36] Then we essentially want the others as well. [98:39] So we'll call the second one update. [98:41] We'll call the third one basic. [98:43] And of course, we want to use the update and basicTokens there. [98:48] So that's going to give us the tokens that we will then want to include within our [98:53] requests. [98:53] And we will talk about how we can do that. [98:55] But first, I want to open up the routes for our API. [98:59] And I want to go ahead and [99:00] I want to use the Sanctum middleware to protect all of the endpoints in this API. [99:06] Because I think that makes good sense, we don't want just anyone to [99:10] be able to hit our API and get whatever data that they need. [99:14] They need to have a token. [99:15] So we are going to use the Sanctum middleware for our authentication scheme. [99:21] And that'll be good there. [99:23] So let's close out of our routing files. [99:27] And let's go to the browser because we want to make a request to that setup URL. [99:32] So let's duplicate one of these tabs, [99:36] let's go to setup, and then we will get our API keys. [99:40] And we will, of course, copy these so that we can use them later on. [99:45] So let's look at the raw data that's copy, and [99:48] let's put them inside of a notepad, just so that we have them. [99:52] Now, when it comes to using these tokens, we want to use the value. [99:56] We, of course, have these names, admin, basic, and update, but [100:01] we want to use the values, starting with the numeric value, [100:05] followed by the pipe, and then the token itself. [100:08] And we are going to include this with the authorization header. [100:12] So let's first of all try to make a request, just a simple GET request for [100:17] the customer's endpoint. [100:19] And we should not be able to because the API is now protected with Sanctum, [100:24] and we can see that we were redirected to login. [100:28] I can't find login. [100:29] So we are good there as far as our authentication is concerned. [100:34] So we want to go to postman because this is going to allow us to add [100:38] the authorization header. [100:40] Let's do essentially the same thing to where we are going [100:44] to try to retrieve the customers. [100:46] And of course, if we send this, we are going to get the same thing. [100:50] It will be an error that says, unauthenticated. [100:54] So let's go to the Authorization tab here. [100:56] We want to set the type to Bearer Token. [101:00] And then we will provide the value of that token. [101:02] So let's start with the basicToken. [101:06] We're going to copy that value. [101:07] We will put it inside of here. [101:09] And then we will submit this request once again. [101:13] And we should see the results of making requests for [101:17] our customers, and there they are. [101:20] In the next lesson, you will learn how to use Sanctum's capabilities feature so that [101:26] you can either allow or deny authorization for certain types of requests. [101:32] [MUSIC] [101:35] Laravel Sanctum has a fantastic feature that allows us to assign [101:39] capabilities to a token. [101:41] This is a great way of authorizing whether or not a client can do something [101:46] that they are requesting to do, such as creating, updating, or deleting. [101:52] So we, of course, want to provide that functionality for [101:57] our create and update requests. [101:59] So let's open up those files. [102:02] And we want to open up all three of them, the BulkInsert, the StoreCustomer, [102:07] and the UpdateCustomer. [102:08] And let's start with the StoreCustomer. [102:11] And basically, what we want to do is, first of all, [102:15] get the user because the user object is going to have a method that will [102:20] allow us to determine whether or not the token can do something. [102:25] But we need to check to see if we have a user first. [102:28] Because if we don't and if we tried to call this method, then our code blows up. [102:33] So the first thing that we want to do after getting the user is checking if it [102:37] is not null. [102:38] Because if it's not, then we want to proceed and use the token can method, [102:43] which is going to check to see if our token can do a certain capability. [102:49] In this case, we are creating something. [102:52] So we want to see if the token or [102:54] the request that has the provided token can create. [102:58] And if it can't, then of course, we return false, otherwise, [103:02] it's going to return true. [103:03] So there, we have protected this request, and [103:07] we essentially want to do the same thing in the other requests. [103:12] So let's go to the BulkStoreinvoiceRequest, [103:15] let's use the same code for the authorize. [103:18] Now, if we wanted to break this down even more, we could do something like this [103:23] to where we could have a capability of customer:create or invoice:create. [103:29] So that only certain people could create invoices, [103:32] certain other people could only create customers. [103:35] I didn't want to go that granular, we are just creating, updating, and so on. [103:41] So this is going to be just fine here. [103:43] Now, for the UpdateCustomerRequest, this is going to check for [103:47] a different capability called update. [103:50] Once again, we could have done customer:update, or invoice:update, or [103:55] something along those lines, but you get the idea. [103:58] So now these requests should be protected so that our basicToken should [104:03] only have read access, it should not be able to manipulate anything. [104:08] So let's test that out, I have the token for the basic user here. [104:14] Let's change the request to a POST. [104:17] We are going to try to create a new customer. [104:21] So I have a payload ready to go for that. [104:24] And let's add that payload to the body. [104:28] And whenever we submit this, we should see that we are not authorized to do that. [104:33] However, we get the response back indicating that it was created. [104:38] Now, the question becomes, how? [104:40] Because whenever we created this basic-token, [104:44] we did not specify any capabilities whatsoever. [104:47] Let's take a look at the database. [104:49] We want to go into the personal access tokens, and [104:52] we will see the three tokens that we created. [104:55] Now, we have the admin, the update, and the basic-tokens. [104:59] We have the hashed tokens. [105:02] Notice that these are not the actual values of the tokens themselves. [105:06] Remember that I said that we only see those whenever we create the tokens. [105:11] So you need to remember that. [105:14] But look at the abilities. [105:17] So admin has the abilities of create, update, and delete, that makes sense. [105:21] Update has create and update, make sense. [105:26] Basic-token has an asterisk. [105:28] And I find this kind of interesting, because if you're going to go through [105:33] the process of setting up abilities for some tokens, but you don't for others. [105:38] Then you would expect that, like in our case, [105:41] the basic-token wouldn't have any ability whatsoever. [105:44] That's not the case. [105:45] If you don't specify an ability whenever you create a token, [105:49] you are essentially creating a token that has all abilities. [105:54] So the way to fix this or [105:55] the way to avoid this is to assign abilities to your tokens. [106:00] But of course, we want to fix this so [106:03] that we don't run into this issue with these tokens that we have. [106:07] So we can simply edit this record. [106:10] And we can change that so that instead of an asterisk, we have a string of none, [106:17] or basic, or whatever we would want to refer to an ability. [106:22] I think none kind of makes sense, so we're going to run with that. [106:27] So now if we go back to postman, and we try to run this same request, [106:32] we are going to see that the basic-token will not be able to do so. [106:37] This action is unauthorized. [106:39] So that was perfect. [106:41] But let's test one of our other tokens. [106:44] Let's test the admin-token. [106:46] And all we need to do is change the value for [106:49] the authorization header token so that we will use the admin-token. [106:54] We will send this request. [106:56] We should get the response back, indicating that the customer was created, [107:01] and it was. [107:02] And just for the sake of completeness, let's test the update. [107:06] And instead of creating a new customer, [107:09] let's edit this one that we just created, so that was with an ID of 234. [107:15] Let's use a patch, and let's change the payload so [107:19] that all we're going to do is change the name from John Doe to Jim Doe. [107:25] And if we issue that request, we should get nothing back, which is what we wanted. [107:31] But now, let us request that customer, and there we go. [107:37] We can see that the update was successful, that we have Jim Doe now. [107:41] So protecting requests is very straightforward. [107:44] When you use Sanctum, your user object has a method called tokenCan. [107:49] And if you have assigned abilities to token, [107:52] then you can check if that token can do that certain capability. [107:56] Just remember that if you don't assign any abilities to a token, [108:01] then that token can do anything. [108:03] So if you plan on using that feature, [108:06] define an ability that is the most restricted that you want. [108:11] [MUSIC] [108:15] Laravel has the tools that we need to build usable and [108:19] scalable applications, including RESTful APIs. [108:22] In fact, Laravel does a lot of the hard stuff for us, making it so [108:26] much more enjoyable to build software. [108:29] For example, you learned how using Laravel form request classes, [108:33] not only work for incoming JSON payloads, but [108:36] their built-in validation capability saves us so much time. [108:40] And Sanctum, [LAUGH] its authentication and [108:43] authorization features makes it so easy to protect our resources. [108:49] It's these types of features that help us write better and [108:52] more feature-filled software. [108:54] Laravel has a huge community. [108:56] So if you get stuck on anything, not just writing an API, don't be shy, [109:01] hit the web and ask questions. [109:03] We are all happy to help. [109:05] Thank you so much for watching this course. [109:07] Please feel free to contact me through Twitter or the Tuts+ forums, [109:11] if you have any questions. [109:12] From all of us here at Tuts+, [109:16] thank you, and I will see you next time. [109:21] [MUSIC]