How to Build a REST API With Laravel: PHP Full Course

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] building a restful api can be a daunting task because there are a lot of things that you need to consider for one you need a suitable framework that is both powerful and flexible but it also needs to provide built-in tools to help you be more productive so that you can go from development to deployment in a relatively short amount of time and a laravel has got you covered hi i am jeremy mcpeek with tuts plus and i invite you to join me so that i can teach you how to build a restful api using laravel we'll start from scratch by defining our data models and seeding our database with test data we'll then dive right into developing our api by handling basic get requests and you'll learn how to transform the data from the database into a consistent and conventional json format you'll then learn how to build a flexible and reusable filter syntax so that end users can easily query and filter data from there you'll learn how to handle and validate manipulative requests such as post put and patch we'll also discuss how to implement a bulk insert feature so that end users can quickly insert multiple entities with a single request we'll then look at how to protect your api endpoints with larevel sanctum and how you can use its token capability feature to not only authenticate requests but also authorize them for specific tasks we have a lot of ground to cover so let's get started of course the first thing that we need to do is make sure that you have everything that you need to follow along and the list is relatively short but there's a variety of ways that you can obtain and set all of these things up you need php you need an http server some kind of database preferably mysql or something compatible and then you of course need laravel now the one solution that's going to give you everything is a laravel docker image if you're not familiar with docker it is a virtualized environment it's not a virtual machine it is a virtual operating system which i know kind of sounds like i'm splitting hairs but there is a technical difference docker is for creating what are called containers so that you can run applications regardless of hardware and host operating system and that's all it is a virtual machine is for setting up cpu ram all of that other stuff but that's getting off into the weeds if you go to laravel.com click on the documentation and then click on your first laravel project and it's going to kind of walk you through the process of setting all of that up regardless of what operating system you're on mac os windows or linux this is an option for you however for windows it's a little bit more involved because you have to install a windows subsystem for linux version 2 and enable that and get all of that set up but that is an option and when it comes to development environments i'm all for virtualization because it completely separates your development environment from your main machine however this particular machine has some technical issues which prevent me from using docker so instead i'm going to use something called zamp xampp this is an all-in-one solution that has everything but laravel so this will give you php it gives you apache maria db which is a mysql clone and perl and one of the things that i really like about this is that there's no configuration and so by downloading and installing xampp you get everything installed and configured and that is a very nice thing because if you install everything manually you have to configure everything manually but that's not it you also get a nice little control panel application so that you can turn these services on and off at will and of course using something like xampp gives you a little bit more than just what it says on the screen here there are other tools that are built in that allow you to work with a database like phpmyadmin and a few other tools so this is a very nice little thing to have now if you're going to take this route or if you are going to install and configure everything manually then you will need to install laravel and we do so using composer the website is getcomposer.org and basically it is a very easy thing to get set up on your system and then from there you just need to go to the command line and type this out composer global require laravel slash installer and this is going to download and install the laravel installer so that you can create new projects on the fly using the larevel command and to do that simply type laravel new and then the name of your project we'll call this laravel api this will take a few moments to finish creating our project but when it's done we can start configuring our project to interact with our database the installer creates a new directory with the name of your project so the first thing you want to do is cd into that's new directory and we'll go ahead and fire up the code editor because we need to modify the environment file that contains the information about the database now in my case everything is already set up because my system uses all of the defaults if you are using something like zamp or some other kind of all-in-one type of tool then chances are you won't need to change anything either if you manually set up your environment then you might need to visit this file and make some changes if you're using the docker image then i think everything is already done for you so you don't have to do anything now in my case i need to create this database and we can change the name of the database if we want but i typically named the databases the same as my project that just makes things a little bit easier for my brain to process so you can name your database whatever you want but do be sure that your connection information is correct for me it is so all i need to do is go to my xampp control panel i need to start up the mysql database which is actually a berea database but i'm going to go to the admin and really this is the only thing that we need a administration tool to do you could use the command line for the database if you wanted to take that approach however when it comes to working with databases i prefer a graphical tool so i'm going to use what is given to me by zamp which is phpmyadmin and i am simply going to click on new here because this is for creating a new database it shows you the databases that are already here many of these are built-in and system databases so we don't want to do anything there but as far as the database name i'm going to use the name of the project create and that is going to get this set up so that now whenever we use artisan to make our migrations and stuff like that it's going to connect to that database and everything is going to work so in the next lesson we are going to do just that we are going to create our models and our migrations for the data that we are going to be working with as we build this restful api in this lesson we are going to do much more than just create our models we are going to create the migrations the factories the cedars as well as the controllers and of course that sounds like a whole lot of work but some of that is going to be done by artisan now we are going to take a smaller piece of a much larger project to where we are just working with a couple of types of data such as customers and invoices that we would issue for those customers so let's get started by using artisan to make our model called customer and we're going to use the all flag this is going to create well just about everything that we need to work with this type of data and then we will do the same thing for the invoice as well the relationship between these two types of data is going to be a one to many one customer can have many invoices so the first thing i want to do is go ahead and open up our models so that we can make that relationship so let's go to app models customer and we're going to add in our has mini relationship so this will be just a method called invoices where we will return the has many and we want to specify the invoice class and then for the invoice class we will have just the inverse relationship so really let's just copy this so that we can paste it inside of invoice and then make the necessary changes so if the inverse of a has mini is a belongs to our class is of course going to be the customer class and the name of our method will be simply customer and that's all we are going to do with the model class for now we're going to focus more on the migrations and everything else as far as populating the database so let's go to the database folder and open up migrations we'll start with the create customers table migration in a real application there is a ton of information related to an individual customer and i don't think we need to have everything but i want a lot of data to just work with so we're going to have quite a few columns here the first is going to be the name of the customer and then we're going to follow that up with the customer type now the idea behind the type is that the customer could be an individual or it could be a business so this is just going to be a column that will typically just have one character i for individual and a b for business and of course we would need some kind of email in order to contact whoever it is that we interact with and then it would be helpful to have the address city state and postal code so let's go ahead and get all of that set up and that's all that we are going to include with our customers table now of course we could add a whole lot more but this is going to be just fine so now let's go to the invoice and let's start defining this table now the very first thing that we need other than the id is the foreign key for the customer id but then it would also be useful to have the amount of the invoice and let's just keep that as an integer ideally we would have more precision here but for the most part as long as we have a numeric value that's really all that we care about as far as our development is concerned and then we will have a status which is going to be a string and this is going to be somewhat similar to the type of our customer in that it will typically just contain one character but let's say that we could have a build meaning that we have gone ahead and billed the customer for this invoice we will have a paid signifying that the customer paid the invoice and then there will be a void because sometimes mistakes are made and we need to void that invoice now it would be useful to have some information about the status like if it is billed that we would have a build date and if it is paid then we would have a paid date now both of these of course need to be date time so let's go ahead and specify that but an invoice might not be paid in which case pay date kind of needs to be null so let's mark this as nullable and then we will keep our timestamps and of course there's also a whole lot more information that we can supply with an invoice and ideally we would have another table that would list all of the line items of that invoice but we're not going to do that at least for right now but of course having tables isn't enough we need some data to work with so let's work on our factories so let's open up our customer factory first and as far as the definition let's start with the name now names are going to be well they don't have to necessarily be unique but it doesn't make sense to have a personal name for a company or a company's name for an individual so really what we need to do first of all is get the type that the customer that is going to be created is going to be and we can use a faker to do that so we will use the random element method and our array is going to have simply i and b of course for individual and for business and then from there we can determine what the name of this particular customer is going to be so if the type is equal to an i then we want to use the name method from faker because that is going to give us an individual person's name otherwise we have a business in which case we want to use the company method to give us a company name and then we can use that value for the name and then we have the type so we'll go ahead and set that value as well and then for the email it doesn't matter there i mean ideally the email would be somewhat related to the company if this is some kind of business but we'll just use faker to give us an email and then for address city and state we will use faker's methods to provide those values so with our customer factory defined let's hop on over to the invoice factory and the first thing we will set is the customer id now one thing that we can do here is to specify that this customer id is going to come from the customer factory in which case we need to import that model class and then for the amount all we need is some numeric value and it will be useful to have something that is not zero and not negative so we're going to use faker to give us a number between uh let's say 120 000. man i would like to be able to invoice for that much and then we need the status now the status can be one of three values it can be billed paid or void and really the next two fields the build date and the paid date depend upon this status so let's kind of do the same thing where we're going to go ahead and get the status where we will use faker to once again give us a random element from an array that contains our statuses so we have build we have paid and then we have void and then for the build date really it doesn't matter what it is because we will always have a build date so we will go ahead and use faker to give us a date time value that is going to be some time this decade i mean really the dates don't matter this is just test data to work with however we do want something that is a little bit realistic now one thing that we probably should do is get this build date and then base the paid date off of that if the status is indeed paid so we do need to check that here and if it is paid then we want a date but you know let's just do this once again we will call the this decade method and if it is not paid then the value is going to be null and now that we have our factories we can use them to seed our database so let's close our factories let's go to the seeders folder and let's open up customer seater and invoice theater and really the database heater as well because we will need that too and now that i think about it i don't really think we need the invoice heater because we can create everything from our customer seeder because this is exactly what we could do let's first of all use our factory method and let's say that we want at least 25 customers that have 10 invoices each i mean we could be just lazy and say that we have a hundred customers they each have ten invoices but let's not do that let's have a different number of invoices for some of our customers so we're going to use our factory we're going to create 25 customers each of them will have 25 invoices and we will create that so let's just copy and paste so that then we can have 100 customers that each have five invoices then we can create another 100 customers that have three invoices and then finally let's create some customers that have no invoices and let's make this a small amount so five customers will not have any invoices and they will just be in our system so with that in place we can go to our database seeder and inside of the run method we are going to call the customer seeder and there we go so that's now we should be able to go back to the command line and we will use artisan to migrate let's use fresh and we also want to seed the database so if we typed everything correctly which of course we didn't so we cannot find the customer seeder or rather we can't find customer inside of the customer seeder which of course we need to include so let's use app models and then customer let's go back to the command line let's run the fresh migration again another error and it looks like that i used fake instead of faker what was that file that was customer factory so let's open up the customer factory and let's change fake to faker there we go let's go back let's run the seeder again and of course we get something incorrect call not found build date and why is that so let's go to the migration and i probably type something incorrect that was inside of invoice so we have build date ted and that's what i get for typing and talking at the same time it's not that easy at least it's not for me so now finally hooray everything works we have our stuff and so now that we have all of our data in the database we can start developing our api in the next lesson apis are software they are a specialized kind of software but ultimately they are software we write them we release them we fix bugs and then we decide to add new features so that we can release a new version however our users are developers they are using our apis in their applications and anything that we change could break their applications so unlike conventional applications we need to version our apis so that as we release new versions our old versions are still there they're functioning and developers who are using those old versions can opt in to the new features in the new versions or they can continue to use the older versions so unfortunately this means that we have to not only keep track of all of the different versions but we have to keep them inside of our project but that's just the nature of developing an api so one of the easiest ways to do this is to segregate the different pieces that we need for different versions and the controllers are really the most important because those are what are handling the requests so this is what i typically do inside the controllers folder i will create a new folder simply called api the idea being that this is a folder where all of the api controllers live this is especially useful if you have a project that is both a typical web application that is serving html but you also have an api that you need to provide as well and then inside of the api folder this is where i would have the version folders so this is our first version so we're just going to call it v1 and then we will put inside of there the controllers that we need such as the customer controller and the invoice controller so let's go ahead and let's move those there and of course we need to do more than just isolate these files we need to change their namespaces otherwise we're going to run into naming collisions so now the namespaces for the customer and invoice controllers are app http controllers api and then v1 and we need to make that change on both of those so let's just copy and paste but this is also going to change the controller class that we are extending we need to import that controller so that's easy enough to do we'll just import app http controllers and then controller and of course we need that in both of those files so any controller that we are going to add to our api we'll need to make those changes and let's go ahead and at least implement the customer controllers index method so that we would then simply return all of the customers that we have in the database now we could take this a step further and we could also version our models because we could make changes to the database however those changes are typically adding new columns as opposed to changing the columns that are already there so from that aspect versioning your models is a little less common actually it's a lot less common but you could take the same approach now of course we need to set up the routes for these controllers so let's go to the api file inside of routes this is where we define the routes for our apis and we want to include the version in the url itself because every version should have a unique url and if you define a route inside of the api file then your url begins with api so we would want then our version v1 or v2 or whatever and then our endpoints such as customers or invoices or things like that so one of the easiest ways to do this is to use a group and there are several options that we can apply here the first is going to be the prefix and we can simply set the prefix to whatever version that we need so this is the first version so the prefix will be v1 that is going to append to the already existing prefix of api so we will have api v1 but we can also make our lives a little bit easier by also specifying the namespace because all of our controllers reside within the same namespace and that is simply going to be app http controllers then api and then v1 and then from there we just need to define our routes now we essentially have resource controllers however these are a little bit different because they are for an api we don't really need the create or edit endpoints so we can use the api resource method it essentially gives us the same thing but it omits the create and edit endpoints so that then we can specify our endpoints so we have customers and we would want to say that that is our customer controller and then we will essentially do the same thing for our invoices except we of course want the url to be invoices and the controller is invoice controller end i believe that's customer controller okay so with that in place we should be able to go to the browser and at least hit the index route on our customer controller so you of course need your application up and running and i'm using firefox because when it receives a json response it will automatically parse it into something that is halfway readable well it's more than halfway readable it's very readable so our url is going to be api v1 slash customers and we should see the output and there we go as i said this is very readable we can go through this and if we wanted to scroll through all what 225 or 250 users we could do that now as you'll notice this json payload looks pretty good except when you get to the fields like postal code created at and updated ad instead of using underscores we would like to use camelcase because that is the convention for the json format and we want to follow the conventions of whatever language or format that we are using and so in the next lesson we are going to look at doing just that as well as filtering the results because we don't need to supply everything such as the time stamps we ended the previous lesson with code that actually works and there's a lot to say about code that works because it's a lot better than code that doesn't and all we are doing is returning all of the records from the customers table whenever we go to the customer's endpoint and there are some things that i want to change about that like for example the postal underscore code property now for php and mysql that's perfectly fine because that is the convention especially for mysql columns however now we are talking about json and the convention in json is to use camelcase and anyone using our api is going to expect camelcase so we kind of need to address that something else that i want to do is remove the create and updated at fields now later on we're going to add authentication and i think for authenticated users that would be perfectly fine that we could provide that information but for just normal users that are not authenticated they don't need to see that so i want to hide that from a typical response so that's two things that i want to do and we could approach those two things in a variety of different ways but laravel gives us something called a resource that allows us to transform an eloquent model into a json response and we have complete control over that transformation so let's create a resource we'll do so with artisan the command is simply to make a resource and then we give it a name in this case that's for our customer and we'll just call it a customer resource but before you hit enter we need to think about versioning because this is something that is going to directly affect our users this is the json response and that can change over time so we want to version this as well so we can say v1 slash customer resource and that's perfectly fine artisan is going to create a new resource class that is going to be inside of the resources folder that just popped in there that is inside of app http resources then we have v1 and then customer resource and the namespace is automatically set how we need it to as well so before we start changing this what i want to do is implement our show method because that is going to give us a much easier way of showing how this is going to work so all we are going to do for right now is return to that customer so if we go to customers slash one that should give us just a single customer and great so we're going to modify this output by using this resource class so all we need to do here is return an array where the keys are the property names that we want in the json response and then we just provide the value for those properties so for example if all we wanted to do is return the id we could do that very easily by just specifying id and giving it the value of id and as far as the syntax for getting those values it's just like using our model class so we could save this and we could use it inside of our customer controller so the first thing we need to do is import this resource and that is inside of app http resources v1 and then customer resource and then we are going to use this class inside of our store method and we are going to create a new instance of this and we will pass in the customer object as the argument to the constructor and with that simple change we can go back let's refresh here and we are going to see just the id now the overall structure changed we see that we have this data and that's part of using a resource now we could change that if we wanted but personally i like that so we're going to leave that alone so of course we want to provide more information which means that we have to go back to our customer resource and we have to specify all of the keys that we want to expose and for the most part it's going to be a straight up one to one so the id is going to be the id the name is going to be the name the type is going to be the type and so on and so forth but then we get to the postal code and this is where we will use our camel case and then we will specify the postal code from our model as the value and there we go so if we go back we can refresh and we see those values and if we look at postal code then we can see that that is camelcase and of course the timestamp fields are not there because we explicitly left them off inside of our customer resource so with that in place let's go back to our customers and we are still going to get that same response to where we see everything and we want to use our customer resource to filter down each individual object in the json well the way that we can do that is by creating another resource except in this case we're going to call it customer collection and this is still going to be a resource but it's going to be slightly different it is specifically for working with a collection of things so if we open up customer collection we see kind of the same thing and we can actually just leave this alone so that's inside of our controller we of course need to import this but whenever we have our customer collection we will use that almost exactly like we used our customer resource we are going to new up the customer collection and then we will pass in the collection that we want to transform which is going to be all of the records from our customer model so if we go back to the browser let's refresh we are going to see the new json format once again we have this data that's going to contain all of the data but the data itself has been transformed we can see the camel case for postal code and of course the timestamps are not there so how does all of that work then because we did not specify the customer resource we just specified customer collection well it's kind of all magic it's going to assume that there is a customer resource and it's going to use that to transform each and every record so that we get the payload that we want and one of the beautiful things about this is that especially for our index we don't want to return just all it would be nice if we would paginate it so if we call paginate it's going to automatically provide all of the information that our users are going to need to retrieve pages of that information so let's go back to the browser let's refresh and the overall structure is slightly different we still have our data but notice that we only have 15 items within the data then we have the links that a client can use to navigate to the first page the last page and the next page and then there's some metadata about the information in this particular page so of course if we change this to where we want page number 10 then we are going to get the data for page 10. and we of course want to essentially do the same thing for our invoices even though we don't have anything set up as far as the controller is concerned but we can go ahead and create those resources so that's first we will have the invoice resource and then we want an invoice collection and this one is really important because the invoice had the build date and the paid date and those of course had underscores so if we open up the invoice resource we can change the return array for the to array method specify the properties or the fields that we want to expose and how we want to expose them and then we just need to implement that inside of the invoice controller and of course the first thing we need to do is import those classes so let's copy what we have from the customer controller and we will make the necessary changes and for the index we will simply return a new invoice collection where we will paginate the data from our invoice model and let's go ahead and implement the show method as well so we'll simply return a new invoice resource where we pass in the invoice and we are good to go there so let's just very briefly check that out make sure everything's working okay so if we go to invoices we should see our data for the invoices as well as the links and metadata we do if we visit the page oh well let's look at the format we should see everything in camelcase we do build date and pay date or camel case and of course if we go to an individual invoice we will see the same format so now that we have the basics working we need to start focusing on retrieving dynamic data such as querying and sorting and we will start looking at that in the next lesson one of the reasons why we have web apis is to provide access to data and if you're going to provide access to data you also need to provide the ability to filter that data because who wants to try to find a needle in this haystack i don't now you could say that we also need to provide search because search is different from filtering however over the many years of writing software i've come to find that i really don't need searching when it comes to an api there's been a few times i needed that functionality but for the most part filtering worked just fine so then the question becomes how are we going to do this because if you do any kind of research on api design you're going to find a ton of different options and opinions but this is what i've settled on so first of all we only want to filter things that handle get requests and only those get requests that return a collection that makes sense so for our case it's going to be customers and invoices we also want something that is going to be reusable so that we don't have to write a lot of code repeatedly for the same functionality because as our api grows we want to provide filtering wherever it makes sense to but now comes time to the syntax of the query string because there's a lot of different opinions but what i've settled on for apis written with liar val is something like this we'll first begin with whatever field that we want to filter on so let's say that we want to filter based upon the postal code and i know that this isn't a great comparison but we only want the customers that have a postal code greater than well what amounts to be thirty thousand postal codes are strings they are typically five digits long at least in the us but they could be longer and a lot of times they contain a dash so yes this is not a very good comparison but just run with me here because laravel will take this and turn it into an array automatically there's no parsing that we have to do laravel just does it automatically whereas if we used something like colons to where we would have postal code then we would have the operator and then the value we would have to parse that out and you might like to parse strings i don't so this is what we are going to go with and since this can be a fairly complex type of thing i'm going to write the code that i well not necessarily that i want to use but what we're going to start with and then we will massage it into something that will be much better to use so the first thing we are going to do is add the request parameter to our index because we want to take this request and pass it on to whatever is going to build whatever we need for filtering so that we will need to create a new object we'll call this customer query and then we will have the items that are going to be built from this class the filter will have a method called transform to where we will pass in the request and then this transform method is going to give us an array that we can just straight up pass to the where method so this means that it's going to be an array that's going to have the column followed by the operator and then whatever value is going to be used for that comparison and this is actually going to be an array of arrays that contain that information because we want to provide the ability to filter on multiple fields at the same time so this query items is going to have this kind of structure so that we could just straight up pass it to the where method and that's going to give us our filter now of course query items might be an empty array so we do need to check for that first and we can very easily do that with count so if that is a zero well then we want to continue doing what we are already doing just taking the data straight up from calling paginate otherwise we essentially want to do the same thing but instead of calling paginate right off the bat we'll call where we'll pass in the query items and then we will call page innate so that's the goal i mean yes we want this to be much cleaner but at least in this lesson this is what we are going to implement so let's create a new folder inside of app and let's just call it services and since this is something that could be different based upon the different version of the api we need to version it so we'll have v1 and then inside of here we will have a new file we'll call it customer query and let's start with the namespace that is going to be app services v1 and since we need access to the request we'll go ahead and pull that in as well that is inside of illuminate http and request and then we can just write our customer query now the very first rule of handling user input is to not trust user input and that is exactly what we are working with anything in the query string is user input so really we want to make sure that whatever is in the filter is what we support and one of the best ways to do that is to have something that we can opt into you know kind of like the fillable inside of our model classes which we haven't set up but if we wanted to we could come in here with that fillable and we could say that certain columns are fillable it would be the same kind of thing but we would say that these fields are safe fields or allowed fields i guess we can call it allowed parms and then this would of course be an array that would contain the fields that we would allow to filter on but really we need a little bit more than that because there are some fields that make sense for certain comparisons like every one of them can be an equals but you know when it comes to the city a greater than or less than doesn't make sense for city so we also want just the allowed operators as well now postal code i'm going to leave with greater than if we have greater than i guess we need less than as well but that's the idea so with that in place i'm just going to paste in all of the fields for our customer so everything will allow equal postal code includes the greater than and the less than but really we also need something that's going to transform our fields into the database columns and for the most part we don't need to worry about that but our postal code is one of those that we do so it would make sense to have a column map as well and for this particular case our column map is just going to have that one for the postal code so the key could be the field from the query string and then the value could be the actual column name that we would need to provide the database and that is postal code not postal column but then we also need some way to transform the operators that we are using from the query string into the operators that eloquent is going to need and we can do that with another map called an operator map so i'm just going to paste that in because there's quite a bit there now this is just the bare bones there are many different operators that a sql database supports such as the in operator and the like operator so if we wanted to supply that functionality we can do that as well so then it just comes down to the actual functionality of transforming the request query string into an array that we can pass on to eloquent so here we are going to accept the request and we are going to build an array which i'm just going to call ello query the idea being that this is going to be the array that we are going to pass to eloquent so we will create that then we will return that and then we just need to build that array so the first thing we're going to do is iterate over our safe parms because it makes more sense to iterate over what is safe as opposed to iterate what is inside of the query string so we want to iterate over our safe parms as and we need both the field name which i'm going to call parm and the set of operators that are allowed for that field because we need to check not just the parameters but the operators that are being used and then we will get whatever is in the query string for this particular field so we'll do that by passing in the parm which is our field name and it's entirely possible that we don't have anything to work with in which case there's nothing else to do so we'll just simply continue otherwise we have some actual stuff that we need to work with so let's first of all get the column that we are going to use in our query so we will use our column map and we're going to pass in the field name now it's possible that we don't actually have a value there and in fact in this particular case most of the time that will be the case so we do need to provide a default value which is of course going to be the field name itself but then we need to filter the operators so we're going to use another for each and we need to check if we have that operator so we'll use is set to do that and if this operator is allowed for this field well then we want to add an element to our ello query and it needs to be in the format of an array where the first element is the column followed by the actual operator that we want to use so we'll use the operator map here we'll pass in the operator that we need and then we need the value that's going to be used for that comparison and that is from our query and that should do it we return the yellow query array and we're good to go so all we really need to do then is import this class inside of our controller and everything should be ready to go i guess we'll find out so we'll have the customer query there we are creating that filter we're transforming it we are checking if we have any valid filter options everything looks good so let's go to the browser let's first of all refresh make sure that we didn't break anything and of course i did not use the use statement that's kind of important there so let's refresh and now we have undefined variable query item that's because it's supposed to be query items let's refresh there we go okay so our basic query which is not a query works just fine let's do some filtering so let's first of all filter based upon the postal code is going to be greater than let's see what we have here we have something that's eight two two uh let's do greater than thirty thousand that is five digits long so the item like nicholas lockman there and adrian brackus those should not be in the result set so let's hit enter let's see what happens and something just blows up operator map and that's an array that's not a method so let's change that and then we'll go back let's see is this the third time i don't remember whatever time this was was the charm because there we go we can see that we no longer have those entries but let's do this if we look at the type we have several there's a business there's a business there's a business let's do a filter just for an individual so we will add that as another query parameter to where the type is going to be equal to i and all of the business entities should not be there everything is an i so it looks like our filter is working just fine so there's a few things that you might be screaming at me first of all uh we don't have the ability to do an or filter and that is correct unfortunately the query string in the url lends itself to only allow and queries so if you wanted to include an or there would need to be some way that we do that through the syntax of the query string and frankly i don't have a good way of doing that but that's also just the nature of urls and query strings it is implicitly an and query so there's that second of all is this is not reusable at all and you are correct i mean we could copy and paste but really what we want to do is break this out into a base class so that we could then use that base class to implement a much smaller class for the invoices and we will do that in the next lesson in the previous lesson we created a class so that we can filter the results of our customers endpoint and it works rather well if i do say so myself and so we want to replicate the same functionality not just for the invoices endpoint but for any other endpoint that we would implement in the future so really we want to extract all of the key functionality from this customer query class into a base class so that we can use that base class whenever we need to but first i want to make a few changes instead of calling the services i want to rename this folder to filters because we could make the argument that this is a kind of service class but i think we would be better served if we keep all of the filters together and separate from everything else especially as we implement more versions and things like that so there's a few changes i want to make such as the namespace instead of services we'll have filters let's change the name of this class as well to customers filter that way we know that this is a filter for customers let's also change the name of the file so that that is apparent and so the idea is that this will extend we can call this api filter and ideally api filter will not be versioned we'll start with that and if later on we decide that we need to version this base class then we can do that so let's create that file outside of our v1 folder so this is going to be directly inside of filters and really we can start by copying and pasting everything from the customers filter class so let's do that and then let's just make a few modifications the first is of course going to be the namespace we don't need the v1 there the name of the class is api filter and as far as all of our arrays are concerned we just need them to be empty so we will delete everything inside of them you know you could make the argument that operator map should have at least these base operators but we're going to make this empty as well again if we decide that we want to implement that we can implement that and there we go we have our api filter now we do need to import this inside of our customers filter so let's go ahead and do just that and then we will need to modify our customers controller so let's do that before we implement the invoices because after all this is going to be very quick so let's open up that file we will change the services to filters the customer query to customers filter and then we will use that class now one thing about this class you know we could make the argument that we really don't want to new up the constructor so that we could use the filter and you know it would be nice to have a facade instead however there's a lot involved with creating a facade in fact we would have to then weigh what is more valuable as far as our time is concerned setting up a facade so that we can not new up this constructor or to just new up the constructor of course we could also use the service container so that it would be injected but you know there once again we are dealing with a lot of setup for not really a big payoff i think i'm just going to leave this as is it works and there's a lot to say about code that works as we can see here so we're good to go now we just need to essentially recreate customers filter but it will be for the invoices so let's just copy and paste that file let's rename the copy to invoices filter and it's been a while since we have looked at the fields that we have for invoices so after we change the name of this class let's open up the migration for the invoices table so that we can take a look at those and there's several so let's just copy this so that we can have a reference to it inside of our invoices filter and then we just need to set up the map and we will start with the column map and we have three we have customer id build date and paid date so let's just make the necessary changes and then let's set up the safe parms which i think we called allowed parms or something like that i probably pasted in the wrong variable name but oh well that's going to be fine oh we have our customer id we will have amount we will have status build date and then paid date we do not need anything else now as far as the operators are concerned equals is going to work for all of them however i would say that amount needs well all of the operators because it makes sense to want to be able to filter whether or not the amount is less than or greater than or less than or equal or greater than or equal and really we could say the same thing for the build date as well as the paid date and it would also be useful to have a not equal operator for like the status because it would be useful to say that we want to find the invoices that are not paid or that are not void or that are not billed so we will call that in e and we of course need to set up a map for that which is going to be the traditional php not equal and that will work so now all we have to do is reference the fill oh we need to delete the transform method don't we so that means we need to do the same thing inside of customers filter so let's delete that and then we will open up the invoices controller so that we can use our filter there and really we can just copy and paste from our customer controller so let's do that and then we will make the necessary changes because we do need to implement you know the index method in fact let's just copy all of the index methods so that we don't have to manually type in the request parameter but of course the name of our class is invoices filter and then we will just paste that wherever we need so let's hop on over to the browser let's duplicate this tab so that we can have both the customers and the invoices up and we will do just the straight up invoices so that we can first of all see all of the results there we go let's say that we want to find the invoices that are not paid so that means we are going to filter based upon the status that is not equal to p and the reason why i picked that is because the invoice with the id of one was paid and we can see that that is no longer there so our filter for our invoices is working just fine now there's one other thing notice that the links that are provided do not contain the query string so if we navigate to any one of these then we're going to lose the filter and that's easy enough to fix what we will need to do is first of all do the same thing that we were doing before by calling where passing in the filter and then calling paginate but what we're going to pass to the invoice collection in this case is going to be invoices and we are going to call the appends method because we want to append the query string so we will get that from the request we'll call query and that should work let's go back let's refresh make sure that that does indeed work it does so now we just need to replicate that on the customer controller so let's do the same thing where we are going to first of all get our paginated results we'll just store that in a variable called customers and then whenever we use our customer collection we will use our customers call appends to where we will append the query data so let's go back to the browser let's go to our other tab let's make sure that that is indeed the case that yes we do not see the query string included so by refreshing that should be there and it is so we are now successfully filtering both our customers and invoices endpoints in the next lesson we are going to implement the ability to include related data such as including invoices with our customers sometimes it makes sense to include related data like for example i think it makes sense to include the invoices whenever we request our customers but we don't want to do that for every request we want the client or the user to specify when they want that information so we could do something very simple by having this include invoices query parameter if it's true then of course we include it if it's false or if it's not there then we of course don't include the invoices and there are many different ways that we can approach this type of functionality but i want to caution you it's very easy to get excited about implementing features but it's very easy to over engineer your application and develop features that well they just aren't used so if my experience has taught me anything it is that build an application or build software with basic functionality and let the users tell you what features they want because you could spend a lot of time implementing features that aren't used and then you end up with a bunch of code that's just cluttering up your code base and it's just a mess so in this lesson we're going to take that approach we're going to build the basic functionality of including the invoices or not including the invoices so let's go to our controller and let's start implementing that feature so we'll just call this include invoices and we're going to check the query string for that now we are already branching our code once based upon our query items which really that should be called filter items that makes a lot more sense so if we don't have any filter items then of course we just straight up call page nate if we do have query items then we pass that to where and then we call paginate but here's a quick little tip when it comes to querying with eloquent if you pass in an empty array there is literally nothing to execute as far as where is concerned so in this particular case if we passed an empty array to where it's almost like we aren't calling where at all and we are just calling paginate so we could simplify this so that we don't have to check if there's any filter items instead we'll just pass the filter items onto where if there are any filter items then great if not that's perfectly fine as well so this is going to give us the ability to essentially build our query and include the invoices if we need to so the first thing that we will do is simply call where we will pass in the filter items and then we will check if we have the include invoices in which case we will simply include them we do so by calling with and then specifying our relationship invoices and then when it comes to calling page nate we will do that down here whenever we pass our collection to the customer collection so we will have customers and then paginate and then the pins and there we go so that should be great let's go to the browser let's add true to this and let's execute and column.found column 0 where clause and that's the problem on line 26 uh we are wrapping filter items with an array that's that that's not right so there we go we refresh and we don't see the invoices well the reason is very simple because remember that the payload that is being sent in the response is actually being controlled by our customer collection resource class and and really by extension the customer resource and we don't list the invoice here at all so of course we want to include the invoices but we only want to do that when we have that data to display and laravel makes it very easy to do that we are simply going to use our invoice resource and we're going to call the collection method now this is going to essentially use the invoice collection that we wrote which is of course going to use the invoice resource but what we're going to pass to this collection method is indeed the invoices but we only want to do that when the invoices have been loaded so we will use this when loaded method and that's the magic behind this working so if we go back to the browser and refresh we are now going to see our customers with the invoices if we take out that query parameter then we only see the customers so now we have that functionality and really we want to implement that for the individual customer as well so let's duplicate our tab and let's just go to the customer with an id of nine because that's what's on top and there we go of course we haven't implemented this code but let's go ahead and let's include invoices here we'll set that to true and we will essentially do the same thing so let's copy this line where we are going to include invoices and let's go down to our show but this is going to be a little bit different because we aren't building a query here we already have our customer so we are going to do this we'll first of all check if we want to include the invoices and if so we are still going to return this new customer resource but then we're going to call this load missing method on our customer object we want to load the missing invoices and that's going to make that work and then of course if we don't want to include invoices we just return the customer as normal and everything is fine and since we've already changed our customer resource we are going to see this works so that we have an undefined variable of request and that's because we don't have request so let's use the request function here let's go back let's refresh and here we have the customer information and then we see his invoices if we take out that query parameter then of course we will only see the customer so of course the magic is inside of our resource class by using this when loaded method we are able to conditionally include the related information for our resource we are finally to the point to where we can start manipulating our data so in this in the next lesson we are going to be looking at creating editing and updating our resources now before we begin it would be useful to have a client that we can use to issue http requests now yes we do have a browser and we could technically do that we could pull up the console and then issue requests using javascript but that's a little bit more work that i want to do now i'm going to be using postman which is cross platform however there are many other options available i'm a windows guy so when it comes to doing anything with http i typically go for fiddler because fiddler classic is free and it's windows only however there is a fiddler version that is cross-platform it's just not free unfortunately but of course feel free to use whatever tool that you are used to using all that matters is that you are able to issue whatever type of request that you need and provide the data for that request so with that out of the way we are going to start with our customer controller and remember that this is an api controller so there are a couple of methods that are not set up as far as routes are concerned and we no longer need them such as the create method and the edit method so let's just go ahead and get rid of those and one of the great things about using laravel to develop a restful api is that we can use all of the tools that are available for typical laravel applications such as request classes so we can create a class called store customer request and we can put all of the validation rules inside of that class and laravel is going to automatically handle all of that for us so that at the end of the day all we really need to do is return a new customer resource because that's typically what we would do for a post request we would return the new entity and then we would just need to call the create method on our customer model pass in all of the data from the request and we would be good to go now of course there's a few things that we need to do to make this work like for example we need to specify the fillable array inside of our customer model so let's do that now always be careful when you specify the fields that you want to be fillable really think about it because it could have some pretty damaging effects if you're not very careful in the case of our customer model it's okay if all of the fields are fillable i'm fine with that so let's just say that we'll close that and we'll be good to go then we're gonna hop on over to the command line and we're going to use artisan to make a new request called store customer request now this does need to be versioned because this is data coming from the client and that means it's data for a specific version and of course artisan is going to create that folder create the request class inside of that folder so that we don't have to do that so inside of app http requests v1 we have this new store customer request now the first thing i'm going to do is flip this authorized to true now of course yes we only want authorized users to create a customer however we don't have that capability yet so we need to be able to test this first and then we want to specify our validation rules and i'm going to paste this in because it's quite a bit of typing but for the most part it's very straightforward all of the fields are required because we have no optional fields when it comes to creating a customer but the type and the email are a little bit special the type is special because we only have two types really we have an individual in a business so i'm using the n rule here so that if the client provides anything other than i or b then it's going to be rejected and then the email field is well it needs to be an email so we're using that validator as well now because i'm using rule here we do need to import that class that is inside of illuminate validation and then rule but we're not done here because remember postal code now it's fine that it's camel case for our rules because that is the information that is coming from the client however we need to be able to transform that into postal underscore code so that we can properly store that in the database and one of the ways that we could do that is by using the prepare for validation method because this allows us to merge in other values such as postal underscore code so we're going to use the merge method the array that we're going to pass to it will have a single key called guess what postal underscore code and we're going to say that it's going to have the value of postal code that came from the request so there we go that's going to allow us to get that information into the database and so now we just need to import this class into our controller so let's go over there we will add the use statement for our namespace and then the store customer request class now we've already used that in the store method so we are good to go we can hop on over to postman and we should be able to create a new user let's first of all make a request for just the customers endpoint just so that we can make sure that everything is going to work okay it should because we really didn't change all of that much there we go we see our data but now we're going to change this to a post request which means that we need to include a body i'm going to choose the raw option and choose json i'm going to paste in a valid json payload however before we actually store this new entity in the database i want to test some of the validation rules like for example i want to make sure that we cannot create a customer with a type of z so whenever we send this what we are actually going to see is the result of viewing the welcome view that is the markup that we see and if we look at the preview here that's exactly what we see and at first we might think that we did something wrong and we kind of did but remember that the basic functionality of laravel's request validation is to redirect usually it is to the create or the edit action and we don't have those inside of our controller so it's just going back to index now we could change our code however really what we need to do is address the headers that we made with the request because if we look on down at the accept here now our content type was correct it was set to application json because i chose json as the data type however the accept is set to accept everything and we don't want that we want to only accept jason so we're going to disable the accept that it added to that request we're going to add it back but in this case we will say that we only want application json so that whenever we submit that same request we are going to get a response back that says that the given data was invalid and the selected type is invalid so that makes sense we do not allow any other type then i or b but let's also remove the name and let's see what happens there we will essentially see the same thing it will still say that the given data was invalid but then it says that the name field is required so our validation appears to be working we could test all of the fields but we can just assume that everything is going to work just fine so with a valid payload let's submit that request and this should work although we get a response back that the column not found postal code where was postal code used i know that we used it inside of the customer request to merge in the postal code so that means we need to look at the model and i bet that's where it is and yep that's it so we are going to change that to be the actual column name as it should have been to begin with we can go back to postman and we can resubmit this request and we should get back the entity that was created and there it is so we now have a new customer the id is 231 so if we wanted to hop on over to firefox and let's make a request for that we should see that customer and we do but let's say that's uh oh we made a mistake we need to change some of this information well that is where we can issue a put or a patch and we will see how to do that in the next lesson in a typical web application we provide the ability to edit an entity or a resource by loading the data into a form so that the user can make whatever changes that they need to they submit the form and then we of course save those changes into the database but a restful api is very different we actually have two types of edits the first uses a put request which is to replace an entire entity so this is the customer that we created in the previous lesson it has an id of 231 so if we wanted to edit this customer with a put request our request would need to include everything the name the type the email address and everything we cannot leave anything out because we need to provide the values that are then going to replace the values for name type email address city and so on now the values could be the same thing as what's in the database but the idea is that we have to supply all of the values now the second type of edit uses a patch request which is more along the lines of what we typically think an edit is so that if we only wanted to change the name type and email then we only supply the name type and email and those are the only properties that would be updated and that's all well and good except that when it comes to laravel applications we have just a single update method and this method handles both put and patch requests so the request class that we are going to create needs to handle both of those types of requests which sounds difficult but it's not at all so let's first of all make that request we'll call it update customer request now we do need to version this and we are going to start by copying and pasting pretty much all of the code from our store customer request because the rules that we have defined are the same rules that we need for a put request because remember a put request requires us to include all of the properties so we need all of the rules so let's copy everything from authorized to rules to even prepare for validation because we need all of them and then we are going to simply paste those into our update request and then inside of the rules method what we are going to do is check the http method and we do that by using the method method this gives us the http method that is in all uppercase so if you want to compare with put or patch or whatever you need to make sure to use all uppercase here so for a puts request we want to perform the same validation that we did for creating our customer and that's all well and good now from here we could assume that if we are inside of this class then chances are pretty good that we are in an update and so we could just go ahead and use an else here because the idea being that it's either going to be a put or a patch and i think it's safe to do that so we are going to leave it like this and we are going to essentially use the same rules however we are going to add the sometimes rule to each one of these properties the idea is is that if name is not there then it's not going to be validated however if name is in the payload then it is sometimes going to be there and when it is then we will validate it according to the rules that are specified so this is really it all we have to do is use the sometimes rule for each one of these properties and that is going to allow us to validate for a patch request everything else is going to be the same so we just need to import this class and then we of course want to use this class for the type hint for our update method and there we go so that now all we need to do is use the customer that we get we will call update and then we will simply pass in all of the data from the request and that's going to work just fine so let's head over to postman let's first of all issue a request for the id of 231 because that is what we need to make our patch and put requests for we want to send those requests to whatever resource that we want to update so here is our customer that we created id231 and let's start with a put request so i'm going to paste in the payload that we used from the previous lesson but let's test everything let's change all of these values so this is going to be me too at tutsplus.com we'll change the address to 4321 wherever some town can become some city let's change texas to something else like oklahoma o-k-l-a-h-o-m-a oklahoma and then we will uh change the postal code let's just reverse it 5-4-3-2-1 let's submit oh we need to change that to a put request we need to make sure that we have the appropriate headers because we don't want to recreate the issue of being redirected because validation failed it shouldn't fail but still the accept is application json and let's just send this so that we shouldn't get anything back from the api because a put or a patch request typically doesn't give us anything however we need to import that rule class don't we so we need to copy that from the store customer request class we will paste that into update and then we will go back let's re-issue this request and everything should be fine then and it is we're good there however let's go to firefox and let's take a look at our user all of the values should be updated because we changed them all so that's great but let's say okay we made a couple of mistakes it is an individual so we need to change the type we don't want to live in oklahoma so let's change that back to texas everything else we can leave the same so and i guess we should change the name so let's do that we'll change the type back to i and then the state is going to be texas so we're changing only three things we can get rid of everything else actually or we could include them if we wanted but that kind of defeats the purpose of testing a patch request so let's change the type to patch let's submit that request once again we should not get anything back from the server however the status should be 200 but it's not it's 500. and we run into an issue integrity constraint violation column postal code cannot be null that kind of makes sense because if we don't have postal code then we don't need to change anything about the data that we are working with so let's just do this if we have postal code then we will merge in the new array that has the postal underscore code key otherwise we won't do anything that should fix that so let's go back let's resubmit this request that error should go away and in fact we shouldn't get anything back looks great so let's go to firefox let's request the data once again and we can see that the name changed the type change the email stayed the same as did the address the city the state changed and the postal code is the same and so now clients can update data in our database using our api by issuing both a put or a patch request sometimes we want to provide the functionality for inserting records in bulk now not every api needs to provide that however i think it makes a whole lot of sense for hours especially when it comes to invoices in fact i've worked on accounting systems where the person scanning and indexing those invoices would do so in batches and then when they were done with that batch they would submit it and then it would be saved in the database that's really an oversimplification but that's the general idea now because we are using resource controllers there isn't a built-in method for handling a bulk type of request so we're going to have to write our own which is just fine there's nothing wrong with that i'm going to call it bulk store the idea being that i'm using the verbage that's already here so bulk store means that we're going to create multiple records in bulk and we will have a custom request class to validate this information because this is very important because now that we are accepting data in bulk we want to be very sure that before we even try to store something in the database that we have our data exactly how we want it but we're going to start using just this generic request object and then after we get everything set up we will use our custom class let's go ahead and set up our route for this new endpoint which i guess we could just call bulk so it'll be invoices bulk and then we will specify where to route that request that is of course our invoice controller and the bulk store method now we could use artisan to create our request class but i think we'll be better served by just taking one of our existing requests and copying and pasting and modifying i'm going to use the store customer request because it is primarily designed for storing stuff and let's call this bulk store invoice request that's rather a mouthful but it is very descriptive as to what it is now before we start defining our rules i guess we need to talk about what the payload is going to look like so it's basically just going to be an array that has objects representing the invoices so there will be customer id and so on and so forth if we have multiple which i would hope we would have multiple then it would just be another element in that array so one thing to keep in mind is that we are going to validate the individual objects inside of an array and i'm using json terms here so we're going to have a json array that contains a bunch of json objects and laravel gives us the ability to handle the validation for an array of multiple objects and it's going to look something like this so that we want to validate the customer id property on each object but remember that we are working with an array so we're going to prepend that property name with an asterisk and then a dot and we will essentially do the same thing for all of the other properties so let's just copy and paste a few times i think we have five fields we have the customer id the amount then we have the status then the build date and the paid date now the customer id is required and ideally we would also check to see if the actual customer existed in the database because if it doesn't then well we're going to run into problems but we do want to make sure that we have an integer value because the id of the customer is going to be an integer then we want to validate the amount which is of course required but then it's going to be a numeric value the status will be required but it's going to be using an in rule where we need to change these values so that we have b for build p for paid and v for void let's also go ahead and have the lowercase versions of those and then we have the build date so this is required and we also want to say that we need the data in a specific format which is going to be year month day space hours minutes and seconds and we essentially want the same date format for the paid date however paid date is not required in fact it can be null so we also want to say that this can be nullable and that is going to be it so whenever you want to validate an array of objects the keys in your rules need to reflect whatever data structure that you're going to have since all we have is an array we're going to use a star dot and then the property name now if our data structure looked like this where we had data and then that would be an array where we would have the individual objects then it would be simply data dot star dot and then the property name now as far as the prepare for validation you know this is where we were taking the values for the camel cased properties and assigning them to the underscore properties we will still need to do that but it's going to be a little bit more involved here that essentially means we need to iterate over each element in the array and make those same changes so what i'm going to do is build a brand new array we're just going to call it data and we're going to use a for each loop and we're going to turn the input that we get into an actual php array so that we can use it in a four each and then we are basically going to add the underscore fields here so there will be customer id now remember that this is prepare for validation so validation hasn't occurred yet and it's very possible that the payload does not include the customer id in which case this would actually cause an error so what we want to do then is if there is not a customer id in the payload well of course that's invalid but we need to get to the point to where we can validate the payload so we're going to go ahead and assign customer id as no and if validation fails then that's okay nothing's lost or gained and essentially the same thing for the other fields and then once we set these new keys we are going to add this child array into our data array so that after the loop is done then we will merge in that new data that we have and that's going to give us exactly what we want except that we're going to run into an issue inside of our invoice controller because now we're going to have an array that contains not just the actual column names in the database but we're also going to have these camel case keys and the method that we are going to use inside of our bulk store which is going to be insert it's going to assume that everything that we're going to pass to it is going to have a column in the database that means that it's going to try to insert something into a column called build date all in camelcase and that's a problem we will run into an error there so now we need to take those out so i'm going to do this i'm going to create a new array and i'm going to turn the request information into a collection so that it makes it a little bit easier to work with because collections have the map function so that we can essentially create a new array that can change just the columns that we need and that's going to look a lot like this so that the inside of our closure we're going to use the array accept helper and we basically want to say we want all of the columns except these customer id build date and then paid date everything else is fine we want to store that in the database and we will be good to go so that once all that is done then we will use invoice we will call insert and we will pass in bulk but we can only pass in an array we can't pass in a collection so we will call the to array method and that will get us what we want now there are a few things that we need to import here first is that array facade and i don't know if we call it array or not i don't want to call it r because that sounds kind of piratey but i will just call it arr how's that we need that we also need to import the request class and we need to use the class name for the type hint on our bulk store method so let's do that there that's very important because otherwise we would not get our validation so now we just need to test this out now i've gone ahead and pulled up the record for the first customer we have 10 invoices and we're going to add two more so we will have 12 after this is done so let's hop on over to postman and let's make a post request to invoices slash bulk and there is our payload let's make sure that we have the application json accept header we still do and let's first of all make this invalid so i'm going to remove the customer id on one of these and we will submit if everything was typed correctly then we should get a validation message back saying that the first customer id is not there and there it is the zero dot customer id field is required so i find that this is very helpful because whoever is submitting this will know exactly where to look for the customer id let's put the customer id back and then let's submit this again and we shouldn't see anything in the response because we didn't return anything because i don't really know what we should return for a bulk insert but let's go to firefox let's refresh the page we should have two more and we do and so that's how you can add the functionality for bulk inserting records to your api it's not as straightforward as other types of requests but thankfully laravel gives us the tools that we need to validate and modify the input payload to make it all work laravel 7 introduced a new feature called sanctum it is a token authentication scheme primarily for apis and single page applications and that makes a whole lot of sense because single page applications typically use an api in order to work with and manipulate data however token authentication has always kind of been a thorn in the side of developers because a lot of authentication packages want you to use oauth and that just complicates the heck out of everything so thankfully sanctum doesn't it is an easy to use easy to implement token authentication scheme that ties very nicely into laravel's default authentication scheme and sanctum is installed by default and the later versions of layerville you can check by going to composer.json and inside of the require section there should be laravel sanctum now if it's not there then you will of course need to install it you can use composer to require laravel sanctum and then after that is installed you would want to publish it and you would use artisans vendor publish command to do that you would specify the provider of laravel sanctum sanctum service provider and then once that's done you will want to run the migration that is created because that is going to add a table to your database called personal access tokens and it ties in very nicely with the users table now of course we haven't done anything with our users but a typical application would have some kind of interface so that the admin can manage those users or at least the users could sign in and assign themselves tokens now we aren't going to implement any of that because that would take quite a bit of time instead we are going to have a url called setup and we are basically going to check to see if a user exists and if it doesn't then we are going to create that user so that then we can assign some tokens and of course if the user does exist then nothing is going to happen so i've already written the code that is going to create that user the next thing we need to do is sign in with that newly created user so let's do that with our credentials and then we will retrieve the user because we need to use its create token method and it would be a lot easier if we just assign that to a variable so we're going to create three tokens one is going to be the admin token the idea being that this is going to be the token that admins would use that give them all of the capabilities that they would need so we're going to use our user and we're going to call create token then we're going to give this token a name now the name really doesn't mean much it's just simply a name and we could leave it like this and create a token however we can assign capabilities to a token so that whenever we receive a request that includes a token we could check to see if there are any capabilities and we can determine whether or not whoever issued that request has authorization to perform whatever action that they were trying such as creating something or editing something so since this is an admin we want to essentially be able to do everything so we can create we can update and we can delete now i know we haven't implemented deletion because well it's just kind of straightforward you receive a delete request and you delete the record there's really nothing else there and then we would want one for a user that would be able to update so let's call this update token we'll call the name update token and they will be able to create and update but we don't want them to be able to delete and that would be fine and then finally we'll have our basic token which is just going to be read only access we don't want to provide any functionality except being able to read and we won't assign any capabilities for this token now whenever we create these tokens we of course are going to get the plain text of that however what is stored in the database is actually the hash of that key so if you want somebody to be able to use their token you need to output the token because this is the only time that we can get that plain text so we're going to return an array with three things we'll have the admin token and we are going to get the plain text token that is the actual token that will be used with the request then we essentially want the others as well so we'll call the second one update we'll call the third one basic and of course we want to use the update and basic tokens there so that's going to give us the tokens that we will then want to include within our requests and we will talk about how we can do that but first i want to open up the routes for our api and i want to go ahead and i want to use the sanctum middleware to protect all of the endpoints in this api because i think that that makes good sense we don't want just anyone to be able to hit our api and get whatever data that they need they need to have a token so we are going to use the sanctum middleware for our authentication scheme and that'll be good there so let's close out of our routing files and let's go to the browser because we want to make a request to that setup url so let's duplicate one of these tabs let's go to setup and then we will get our api keys and we will of course copy these so that we can use them later on so let's look at the raw data let's copy and let's put them inside of a notepad just so that we have them now when it comes to using these tokens we want to use the value we of course have these names admin basic and update but we want to use the values starting with the numeric value followed by the pipe and then the token itself and we are going to include this with the authorization header so let's first of all try to make a request just a simple get request for the customer's endpoint and we should not be able to because the api is now protected with sanctum and we can see that we were redirected to login and can't find login so we are good there as far as our authentication is concerned so we want to go to postman because this is going to allow us to add the authorization header let's do essentially the same thing to where we are going to try to retrieve the customers and of course if we send this we are going to get the same thing uh it will be an error that says unauthenticated so let's go to the authorization tab here we want to set the type to bearer token and then we will provide the value of that token so let's start with the basic token we're going to copy that value we will put it inside of here and then we will submit this request once again and we should see the results of making a request for our customers and there they are in the next lesson you will learn how to use sanctum's capabilities feature so that you can either allow or deny authorization for certain types of requests laravel sanctum has a fantastic feature that allows us to assign capabilities to a token this is a great way of authorizing whether or not a client can do something that they are requesting to do such as creating updating or deleting so we of course want to provide that functionality for our create and update requests so let's open up those files and we want to open up all three of them the bulk insert the store customer and the update customer and let's start with the store customer and basically what we want to do is first of all get the user because the user object is going to have a method that will allow us to determine whether or not the token can do something but we need to check to see if we have a user first because if we don't and if we try to call this method then our code blows up so the first thing that we want to do after getting the user is checking if it is not null because if it's not then we want to proceed and use the token can method which is going to check to see if our token can do a certain capability in this case we are creating something so we want to see if the token or the request that has the provided token can create and if it can't then of course we return false otherwise it's going to return true so there we have protected this request and we essentially want to do the same thing in the other requests so let's go to the bulk store invoice request let's use the same code for the authorize now if we wanted to break this down even more we could do something like this to where we could have a capability of customer create or invoice create so that only certain people could create invoices certain other people could only create customers i didn't want to go that granular we're just creating updating and so on so this is going to be just fine here now for the update customer request this is going to check for a different capability called update once again we could have done customer update or invoice update or something along those lines but you get the idea so now these requests should be protected so that our basic token should only have read access it should not be able to manipulate anything so let's test that out i have the token for the basic user here let's change the request to a post we are going to try to create a new customer so i have a payload ready to go for that and let's add that payload to the body and whenever we submit this we should see that we are not authorized to do that however we get the response back indicating that it was created now the question becomes how because whenever we created this basic token we did not specify any capabilities whatsoever let's take a look at the database we want to go into the personal access tokens and we will see the three tokens that we created now we have the admin the update and the basic tokens we have the hashed tokens notice that these are not the actual values of the tokens themselves remember that i said that we only see those whenever we create the tokens so you need to remember that but look at the abilities so admin has the abilities of create update and delete that makes sense update has created an update makes sense basic token has an asterisk and i find this kind of interesting because if you're going to go through the process of setting up abilities for some tokens but you don't for others then you would expect that like in our case the basic token wouldn't have any ability whatsoever that's not the case if you don't specify an ability whenever you create a token you are essentially creating a token that has all abilities so the way to fix this or the way to avoid this is to assign abilities to your tokens but of course we want to fix this so that we don't run into this issue with these tokens that we have so we can simply edit this record and we can change that so that instead of an asterisk we have a string of like none or basic or whatever we would want to refer to an ability i think none kind of makes sense so we're going to run with that so now if we go back to postman and we try to run this same request we are going to see that the basic token will not be able to do so this action is unauthorized so that was perfect but let's test one of our other tokens let's test the admin token and all we need to do is change the value for the authorization header token so that we will use the admin token we will send this request we should get the response back indicating that the customer was created and it was and just for the sake of completeness let's test the update and instead of creating a new customer let's edit this one that we just created so that was with an id of 234 let's use a patch and let's change the payload so that all we are going to do is change the name from john doe to jim doe and if we issue that request we should get nothing back which is what we wanted but now let us request that customer and there we go we can see that the update was successful that we have gemdo now so protecting requests is very straightforward when you use sanctum your user object has a method called tokencan and if you have assigned abilities to token then you can check if that token can do that certain capability just remember that if you don't assign any abilities to a token then that token can do anything so if you plan on using that feature define an ability that is the most restricted that you want laravel has the tools that we need to build usable and scalable applications including restful apis in fact laravel does a lot of the hard stuff for us making it so much more enjoyable to build software for example you learned how using laravel's form request classes not only work for incoming json payloads but their built-in validation capability saves us so much time and sanctum it's authentication and authorization features makes it so easy to protect our resources it's these types of features that help us write better and more feature-filled software laravel has a huge community so if you get stuck on anything not just writing an api don't be shy hit the web and ask questions we are all happy to help thank you so much for watching this course please feel free to contact me through twitter or the tutsplus forums if you have any questions from all of us here tutsplus thank you and i will see you next time
Info
Channel: Envato Tuts+
Views: 192,262
Rating: undefined out of 5
Keywords: Tuts+, Envato Tuts+, laravel, php, laravel rest api, rest api, laravel tutorial, laravel api, php tutorial, laravel rest api tutorial, laravel api tutorial, laravel for beginners, laravel restful api, laravel 8 tutorial for beginners, php for beginners, laravel rest api for beginners, laravel rest api authentication, laravel full course, learn laravel step by step, laravel 8 tutorial, envato tuts+, envato, envato elements
Id: YGqCZjdgJJk
Channel Id: undefined
Length: 109min 26sec (6566 seconds)
Published: Wed Jun 22 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.