How to Build Modern Laravel Apps With Inertia - Full 3 Hour Laracasts Course, with Jeffrey Way

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what's up everybody my name is Jeffrey way and I'm so excited to show you inertia.js in this series which was created by Jonathan renink I think you're gonna love it it's so good so as it says on the 10 inertia allows you to build single page applications without building an API instead you reach for classic server-side routing and controllers and that's specifically why inertia labels itself the modern monolith but now real quick before we dig in and install it and have a look around we first need to figure out what the parameters are here so is inertia linked to laravel because it looks like it a little bit here in this example and the answer is no well is inertia linked to view and again the answer is no but it does offer an adapter for laravel and it does offer an adapter for view but if you instead prefer something like react or felt that works as well inertia is not dependent on any one technology but that being said if you are a laravel user you're going to feel right at home here now you can think of inertia not as a replacement for view or it's not a big framework on top of you it's really in many ways just a client-side routing library that connects a server-side framework like clarifel to a client-side framework like View and really that's a good way to think of it inertia is the glue that connects your server-side framework to your client-side framework and the great thing is when you do this it allows you to continue using traditional controllers traditional routing traditional middleware traditional authentication traditional authorization all of that stuff remains unchanged even though you're building a single page application and effectively what this translates to is if you use inertia then no you don't need to build an API no you don't need to use oauth no you don't need multiple repositories for your API and your client-side application instead you're building a traditional but modern my it's all contained within a single application again I think you're really going to love this I do and in fact laricas under the hood uses inertia.js I live it that much okay so let's have a look at the demo application before we finish up it's called ping and it's a traditional CRM okay so the first thing I want you to be aware of is how when I click on these links notice that we're not performing a traditional post back to the server so if you have a look you're not going to see any loading icon as I click here and that's because instead inertia is intercepting the click of these anchor tags and instead it submits an Ajax request to the server so notice if I click on a link yeah we make an Ajax request to the given endpoint and in response we get a bit of Json that contains everything our view component needs to load or refresh the current page so to speak we have information about the user any potential flash messages and then the data that this particular page requires and that seems to be filters in organizations let's do another one let's go to the contacts page which you see here well again we make an Ajax request we fetch the content and then inertia uses this response to dynamically swap out the current page component with the new page component and then view as a result we'll refresh the page automatically due to standard reactivity of view props so notice the advantage here is you only have to load your basic assets once you're not refetching the CSS in the scripts in the header or in the footer for every single page request like you traditionally would with a standard server-side app and what that translates to is a much faster application okay now the last thing I want to show you here is let's go to inertia jgs we'll go to the GitHub page and we're going to have a look at the Ping CRM demo application okay so you'll notice if we look at the rounds here these are standard larifelle routes this should all be very familiar to you if you work with laravel let's have a look at maybe how about this one for organizations so that corresponds to what you see here okay when you visit this endpoint we're going to load an organizations controller and then the index action okay now the index action returns not a traditional blade view like you might normally do with layervo instead we're asking inertia to render a client-side view but again notice it's the same basic shape don't be confused here this is just a standard eloquent query so we are loading a view but not a blade view a client-side view and then we are passing a set of data to it so notice we're passing filters and organizations well we saw that earlier that's one more time go to the organizations page and you'll see in response we pass through the filters in the organizations so this is precisely what inertia allows for and again that's why the creators refer to it sort of like glue it connects the server side to the client side and once it's done doing its job it gets out of your way it doesn't completely take over your application which I really like about it okay I think we're now ready to install inertia and begin configuring it now we're going to use laravel in view in this series so why don't we start by pulling in a fresh laravel app I'll just call it demo all right let's CD in there and if we now visit this in the browser of course we see the standard laravel splash page okay now let's install inertia if I switch back to the website again we need to grab two adapters inertia has an adapter for your server side framework like laravel or rails and then it has another adapter for your client-side framework like view 2 or view 3 or react or svelte we're going to start with the server-side adapter so we'll install the dependencies okay next we need to set up a root template think of this as your layout file for an inertia app and it should look pretty familiar other than this inertia directive and we'll talk about that in just a second so I'm going to copy this and let's open this in my editor and let's go to resources views and let's do this I'm going to rename the welcome page to app.blade.php and if I select everything we can replace it with what we got from the documentation okay and let's add the link okay so a standard layout file the only unique thing is this inertia directive and really this is just a convenience ultimately it expands to something like this a div with an ID of app where we pass to it the initial page data so yeah that would be something like and you've probably done this in your own projects at some point or another where you Json encode a piece of server-side data and then you pass it as a view prop and yeah really that's it so this is how we pass the initial page data to inertia anyways you can stick with the inertia directive same thing now if I switch back to the documentation the next thing we need to do is set up the initial middleware and we'll talk about this more a little bit later but it effectively tells larifelle how to provide the proper response to inertia so let's go ahead and run that and if I switch to my editor that's going to create a new app http middleware handle inertia requests file okay but of course we have to first register it in our kernel class and let's scroll down to your standard web middleware group and we'll add it here okay and that's basically it for the server side setup so the next step is to set up the client side adapter and again inertia provides first party support for react View and view 3 as well as felt okay so I'm going to use view3 here we're going to pull in the inertia package and then specifically The View 3 adapter so let's run that now and while that's doing its thing the next step is to initialize your app now I'm going to assume you have a basic familiarity with view even if you're not yet familiar with view 3 we can work through that but mostly you're creating a view app the way you normally would you then use the inertia plugin and you mount it to your root element so that sets up view then we need to set up our inertia app and then instructed how to find your current page components and we'll talk about all of this so you don't need to feel overwhelmed if you don't quite understand what's going on here for now just copy and paste and then move on so I'm going to grab this and of course if I'm using view I need to go ahead and pull that in through npm and then for view 3 if we also want to support single file components which we do then we also need to pull in one additional package and this is just standard view stuff it's not related to inertia at all so let's do installation through npm and yeah we also want to pull in this package here okay so now in my project we'll go to resources.js app you'll see that requires a bootstrap file but we're not going to use any of this right now so I will delete that and then paste in the code we got from inertia's website now real quick before we compile everything down notice when we create our inertia app we tell it how to track down the current page component and at the moment and by Common convention we're asking it to look in a Pages directory so why don't we go ahead and create that directory now and again this is what I do for layer cast as well it's very common now the final step is to compile this down and we're going to use laryfl mix as this is a layer of l site we're compiling the JavaScript here we're going to turn on View support and it will try to figure out which version you're using or you can hard code the version in this case View 3. and then lastly let's turn on versioning support and that's basically going to add a hash to your compiled files to deal with cache busting and actually on that note in app.play.php you can see this example is already configured to use layervo mix so it's going to look for this file and that will expand to a cache busted path to where that asset is located okay so a little bit of setup but you only have to do it once for a project so let's install any remaining dependencies we have and then we'll compile this down all right and now I can run mix or npx mix and that will pull in any required dependencies we have let's do it one more time and that should compile everything down according to our configuration and it did so yeah notice it compiled these two files down those will be referenced in your mix manifest and notice each one points to a path to the file including a unique query string so then when we use the mix helper function that will expand to the location of those files okay and that's the entire setup process we're now done so of course if we were to come back and give this a refresh because we deleted that welcome page we see an error here but that's okay we entirely expected that so in the next episode we're going to return to our routes file and figure out how to return and render a client-side view and Page component stay tuned so now that we've installed and configured inertia we can now create our first page so we'll start by going to my larifelle routes file and here you can see it's returning that old blade view that we deleted now if we were to reproduce this I could swap out the view helper function with an inertia helper function and then by convention we often capitalize these names but it's effectively the same thing as when you return a blade view the only difference is we're now basically returning a client-side view now again by default these pages will be stored in the Pages directory so I'm going to create that now welcome and just as a little refresher don't forget in your main entry point here's where we instructed inertia where to find our Pages look in the Pages directory for that file so notice if I switch back yeah you didn't have to say Pages slash welcome that base directory is assumed okay so within here let's start off really simple hello world all right so let's compile this down and then view it in the browser and it works this is a good feeling and in fact if I take a look at the source you'll notice it was inserted into our layout file as we'd expect but let's have a deeper look I'd recommend installing a tool called view devtools you can see I've already done it but if you want to do it all you need to do is search for view JS devtools and there should be extensions for Chrome and Firefox so if you use Chrome I'm actually in Firefox in this case then you would add it here okay so then come back to your browser sometimes you need to exit out and bring it back but then when you open up the inspector you should have a new tab and this is pretty sharp I think you're going to like this and this is your top level component this is where information about the initial page will be passed through so notice we want to load the welcome View and here's the props that will be shared with it and sure enough if we scroll down here is your welcome view so it's very much like a blade view but on the client which means you get the full benefit of JavaScript and View interactivity and reactivity you get it for free but this all still feels very familiar all right let's now pass some data I will return to my routes file now actually a quick note here yes we can use the inertia helper function and that's fine if you prefer or you can import inertia and then you can instead say inertia render and this is going to achieve the exact same thing so in the same way that you could say if you're rendering a blade view you could say view make or view the same is true you could say inertia render or inertia now the docs usually demonstrate this approach so I'm going to stick with that just to be consistent now notice the the API here so to speak is very familiar and on purpose it's familiar with how you would load a blade view you provide the name of the view or the page in this case and then ask the second argument you would pass an array of data that will be extracted and passed as props to your page component so as an example if I were to pass a typical name here you know the thing we all do when we're learning something new I would then go to my welcome page component and I will now accept it as a prop so our props are we expect a name that should be a string and I'll reformat and that's all we have to do here this is inertia doing its thing so why don't we say hello comma name now don't forget if I come back and refresh it'll still say hello world because we haven't recompiled our code so instead of running mix usually run mix watch and this will keep an eye on your files for changes and then recompile so now if I come back and give it a refresh sure enough it works all right so here's the rule whenever you want to pass data from a server-side controller to your view page you need to declare it as an accepted prop and then you pass that through when you return and render your inertia response okay so now why don't we tweak this instead of welcome maybe we'll have a home page so I will change that name and then update the corresponding view page so that should have recompiled so if I give it a refresh we still see the same thing next why don't we pass through an array of data and again as an example we'll just pass through some of the technologies that we'll be using in this series so Frameworks and we're going to be using of course laravel and View and inertia all right so back to our page components and we're now going to accept a list of Frameworks all right let's give it a shot come back to Firefox give it a refresh and now if I open up view Dev Tools in my home component sure enough I've received that array of data I mean how cool is that it's so seamless and again if I go back to the top level inertia component you can find all of the props that are passed down here and by the way if you're curious about errors how is that being passed through we'll talk about that a little more a bit later so let's finish up by iterating over that array in this series we will use the following Frameworks and then we'll do some basic view iteration this has nothing to do with inertia the four framework in or of Frameworks then set vtext to framework all right and that should do it so it compiles down behind the scenes we give it a refresh and we've now successfully rendered a client-side page and passed data to it from the server side that's pretty neat in the next episode we'll keep going all right next up I'd like to create a few different pages and then learn how to seamlessly link between them let's do this back in my editor I'll go to my routes file and this data here this was only an example so let's get rid of all of it and now let's create two new pages so maybe if we visit slash users that will show a page to browse your users and then if you visit slash settings that will load a settings page all right now let's create those page components so let's do this that was only an example bring it back okay let's duplicate this to save some time so we'll have users and then another one for settings great so settings we'll say settings and users will say users okay so I have mixed watch running behind the scenes why don't we see if this still works give it a refresh there's home next we'll have users and next we'll have settings it works okay now of course we don't want to manually edit the URL we want an anchor tag that links between them now we're going to talk about layout files a little bit later so for now we'll have to use a little bit of duplication but that's okay so in our home page let's do this we'll say nav and then we'll have an unordered list we'll have three of them I'm just doing a little shorthand here where each one contains an anchor tag that links somewhere okay so this would be home this would be users and this would be settings okay next let's update the uri's users and settings okay let's see if that works so if I come back to my home page give it a refresh yes I see those links and if I click on one it sort of works but what's going on here I go to settings and what I want to point your attention to is we're actually performing a full page refresh so one more time I want you to focus on this page load here ah did you see it why don't we make it a little more clear in my routes file why don't we sleep for a little bit sleep for two seconds okay so come back now if I click on the users page one two we're performing a full page refresh which defeats the entire purpose that's not a single page application that's a traditional application that loads a view component now the solution is to not use a standard anchor tag where we don't have much control but to instead replace it with a slight wrapper that inertia provides and it's called the link component the link component is ultimately an anchor tag but when you click on it inertia will intercept that and instead perform an Ajax request to the server the server due to that middleware we had installed remember in episode one handle inertia requests that will understand that it needs to return a Json response that contains all the information about the new page okay so let's do that now so I will import this link component from inertia View 3. and again if you're using view 2 or react then you would pull it in through that package next standard view work here we're registering it as a component and now we can use it in our template so right here replace them with link okay so now if we give it another shot now don't forget when I click on this user's link the server side is still delayed it's sleeping for two seconds and we're going to talk about how to deal with that uh in the next episode but for now the important thing is that I'm not performing a full page refresh maybe this will make it more clear let's open up the dev tools and I'm going to listen for xhr requests okay users one two and there's our response right here and we get a Json response that contains the new component we want to use so then inertia will read this Json response and it will see oh when you're trying to load the user's component let me go ahead and swap that out and then view will automatically re-render it so in effect if I go back to the inspector everything up here this will remain it's not going to get reloaded because again we're not performing a full page refresh so one more time when I click on say settings the only thing that's being swapped out here is this page component here and this is exactly how we can allow for a very responsive feeling website okay but of course right now one of our issues is the navigation is only on the home page so like I said we're eventually going to extract a layout file but until we do that let's take baby steps why don't we move this nav into a new component called nav so I'm going to add a directory called shared again this is a standard inertia convention any shared components that your whole website will reach for can go here okay let's add one called nav and I'm going to paste that in but now don't forget to this component doesn't know what link is we would still have to import it again so if you want there are ways to automatically share link across all of your page components so if you don't want to re-import it every single time that is an option if you want it but for now let's just pull it in like we did before inertia view 3 or at least building up the muscle memory and then register it as a component great so now we have a dedicated component for our navigation we only need to swap this out now so we're no longer currently using the link component though we might pull it in instead I'm going to pull in nav and notice we're in the Pages directory so we go up into the shared folder into nav and then we will update that great so now we'll say nav and reformat okay now we're going to update these as well so I'm just going to do some copy and paste here and again like I said once we learn how to extract a layout file you would only have to insert that nav a single time but for now we're going to do a little duplication and that's okay all right so cross your fingers with any luck we should have the most basic form of a three-page website that is in fact a single page application so we'll go to settings and again notice no full page refresh it's very very fast now of course users the server side is delaying that by two seconds so notice it does work but when I click on it there's no feedback so one two what's going on here I have no idea and then the page updates so the next step is I'd like to give the user a little feedback that hey we're doing some potentially long running process in the background so just sit tight to allow for that we're going to pull in a progress bar and we'll do that in the next episode now if you're working along and I hope you are return to the inertia documentation and scroll down to the bottom and we're going to take a look at progress indicators you'll see right here inertia provides an optional progress Library which shows a loading bar whenever you make an inertia visit that is what we want okay so to use it we need to install it through npm so let me exit out of mix pull that in and then what next initialize it in your app so we import it and then we call inertia progress dot init and also take note while we're here you can override certain options if you want like the color of the loading bar or whether a spinner will display okay so let's go to phpstorm into our app.js we're going to pull in inertia progress and then finally down here at the bottom inertia progress dot init and I think that should do it so let's boot up our webpack Watcher again and back to Firefox refresh the page and if I click on users we have a two second delay but notice you saw that little indicator at the top one more time go to users one two and now the user has just a little more feedback now like I said if you want you can override some of the options like maybe you want the color to match your site branding let's just say it's red for brevity refresh go to users and now you'll see a red loading bar okay why don't we also add a spinner so show spinner is true by default it's false and we should see it right up here in the top right refresh to the home page go to users and have a look right there so now you have a loading bar and then also a spinner in the top right and in fact if you go to layer cache for any long running pages that are a little more complicated you will see that loading indicator there useful now let's return to inertia links for one more episode and figure out how we can create non-get requests for example if you want to make a post request or a patch request how do we do that with inertia and there's actually a couple ways now let's do this I will return to my routes file and let's create a dummy route for logging out so if you visit slash lockouts we should log out to the user but of course as you may know we should really perform that as part of a post request it adds just a little bit more security if it were a get request then over on John Doe's website he could create a link that when clicked would log you out of my website so when we switched to post we protect against that a bit and we do add csrf protection as part of the request okay now we don't have any authentication system here I haven't pulled in laravel Breeze or anything like that so I'm just going to die and say logging the user out all right we now have our endpoint The Next Step will be to return to our nav bar and let's add another link here to logout now of course in future episodes I will show you how to conditionally display links like this based on for example if the user is signed in or not but that's getting a little ahead of ourselves right now we're still playing around with inertia links okay so yeah if I did this well of course it's going to make a get request to that endpoint let's give it a shot so back to Firefox I give it a refresh and here's her logout link and yeah of course if we run it well two things of course it fails because we haven't registered a route to respond to a get request to log out but second notice when we have errors in inertia they display in this nice modal and we're going to set this up so that it only displays in your local environment but nonetheless it's incredibly useful I like it a lot and I think it actually was inspired by laravel Livewire which is pretty cool when the two can Inspire each other so anyways how do we turn this into a post request well as you know traditionally you would have to wrap the whole thing in a form that submits a post request but inertia has a little bit of sugar to clean this up let's add the method attribute and set it to post okay that's all we need to do so if I come back and give it a refresh I'm going to bring up the network tab to xhr and this time when I click log out notice we do submit a post request and we do get the response from that endpoint all right but let's see what's going on here if I view the source here yeah it's still just rendering an anchor tag but behind the scenes inertia will have an event handler to listen for that click and then it will submit a post request to the endpoints and behind the scenes inertia uses the axios library to perform the requests which means by the way if you're using layerfell passing through the csrf token is automatic because wherefl will automatically include it as part of the response and then axios will automatically check to see if a cookie exists okay but now there's one a little snag here because it's an anchor tag that means on a Mac I can command click on it and notice when I do that of course I can't submit a post request as part of opening a new tab so we once again get this familiar error so really in this case I don't want to render an anchor tag I really just want something like a button okay no problem in those cases let's declare that we want to render this link as a button and now notice if I refresh the page we have the default styling here but we are rendering it as a button and we get the same thing when I click on it okay and that's usually what you will want in this case now in terms of styling you may run into this where you want the button to look the same as the links so generally you will want to reset the styling for a button or pull in a CSS library that has a normalizer that that automatically resets this for example if I were using something like Tailwind which is really popular let's do it right up here I have a little snippet to pull that in version two of Tailwind but notice that automatically will reset it so if I give this a refresh I lose a little of my automatic site link here and that's okay but it does render exactly the same as the links so actually real quick if we want to fix that let's go to home and force some styling here maybe text for XL and font bold next to the navigation will have some margin on top and then let's be explicit that we want list disk finally I think that's about it why don't we add real quick a section here and we'll add a class of and for all sections maybe we'll have PX I don't know eight or something like that or actually in this case let's just do padding eight all around anyways if we close that out and return to Firefox here's what we get next we might want to add some initial styling for all of our nav links or if you want to remove some duplication you could even extract each of these into a nav link component which I often do uh so for example maybe we want the most basic form of a link almost the default styling so text blue and then when you hover over it underline it uh something like that but yeah if you want to remove the duplication extract it into its own nav link component anyhow come back and there we go whether we have an anchor tag or a button it all looks and renders the same and this time if I command click to try to open it in a new tab it's not going to work because it's not an anchor tag it's a button now in closing of course you can submit any request type you want so if you want to submit a patch request or delete request you can do so and you can even pass through the data as an attribute which is kind of cool so for example if I wanted to say Foo is bar now if we come back and refresh have a look here when I make the post request notice that it will pass through that data as part of the request so then of course on the server side just fetch it the way you always would so for example if you want to die in top and grab Foo this would do the trick one more time and you get the value for Foo now in closing I think you'll find if I switch back to the nav component this is incredibly useful for simple interactions like this where typically you'd have to create a form listen for it to be submitted and then make the Ajax request you can now do it as part of the link component but for larger things you might want to instead reach for inertia's form Helper but for now this should get you going now while we're on the subject of links another useful feature is the ability to preserve your scroll position let me show you what I mean why don't we visit our users component and as an example down here and a div let's simply render the current time so I will say the current time is and then we'll pass that through from the server side mostly to show that we are fetching new data from the server and view is reacting to the change in props all right so let's declare our prop time and that will be of type string now the next step is to return to our routes file and I can go ahead and get rid of sleep but now I'm going to pass through the current time and for that I can just use carbon so I can do something like now to time string and that should be fine okay so now this route will load the user's component and pass through the current time our view component will accept that time and then render it in this paragraph tag so if we come back to Firefox refresh and visit the users page I can see the current time and of course every time I refresh you'll see that bump up okay but now let's push it far down the page let's say March and top is something like 400 pixels just to force a scroll bar okay so now if I come back and refresh I have to scroll to see this at the bottom all right next let's add a link here that will make a request to the current page to fetch the latest data and in this case the latest time now what you'll find when we do that and first I'll have to import it or again remember you can share this globally if it's kind of annoying to you to always import a link component anyways we can register it here and then I'll add a link to the current page and we'll say refresh and that should be good we'll give it a blue color and I think we're ready to try this out all right so back to Firefox give it a refresh and here's our link so here's what I want you to notice when I click on any typical link it's going to behave the way you'd expect it reloads the page and it Scrolls back up to the top and this is often what you want but not always in plenty of situations you will want to maintain the current scroll position here's a simple example maybe you're reading a blog post that has a like button and when you click on it well of course it's going to mark that you liked the post and then it returns a redirect back to the current blog post and then as part of that maybe it will display and reflect that you did like that particular post anyways when it performs that redirect you probably don't want it to send you back to the top of the page no instead you want that scroll position to remain unchanged so we can do that in inertia by adding The Preserve scroll attribute and that's all there is to it so if I come back to Firefox give it a refresh and if I click on this link again I want you to notice that the time will update but the scroll bar will remain unchanged there we go 45 46 47 and do note every single time I hit this link here we are making an HX request there's another there's another there's another so I promise you you're going to find so many different use cases for this another typical example Before I Let You Go might be something like a table layout maybe for a CMS and when you click on any toggle to adjust the order or filter it again you don't want to effectively reset the user back to the top of the page every time they change one of those filters instead you want to maintain their current scroll position and you do that by adding The Preserve scroll attribute next step is active links so notice if I click around to the navigation here at the moment on the link itself there's no visual indication that it's currently active and of course this is something we should offer so let's see what we can do here I'm going to visit my navigation component and on the home page itself why don't we set a conditional class for example let's make the link bold and underlined if some condition happens to be true for now I'm going to force it and that'll reformat alright so not quite right but if I switch back ideally this is how we want it to look if I'm on the home page it's bold and underlined but yeah at the moment as soon as I click away it doesn't update so in inertia we have access to this page property and actually if you want to take a look at it let's open up View devtools and you can find it here the initial page now I want to point your attention to two properties here first step is the current URL and second is the current page component that we're on okay we're going to leverage both of those to set these active links so what if I said check to see if the page URL is the home page only on that condition should we make it bold and underlined so now if I go to the home page it works but if I click away to users or settings it turns off this is what we want okay so yeah of course I could select all of this and duplicate it here and update the link to users and then one more to what is its settings give it a reformat and this would do the trick so now if I come back and refresh we do have more visual feedback it works but now one thing to be a little careful what if for whatever reason when you click on the users link it adds some kind of query string maybe a filter or something like that well now our active links will break because we're checking to see if the URL is precisely users but now it's users and then a query string so notice if we come back give it a refresh and now click on users yeah we don't get that visual styling and again that's because of the query string okay so remember we're dealing with basic JavaScript here so if you want we could just say all right well I don't want to do a perfect match of users but how about if it starts with Slash users that's enough all right basic JavaScript here users okay come back and now if I give it a refresh we fixed the issue now it'll remain active no matter how much we have here refresh and that still works of course it does okay but now here's another thing to consider if I bring back few Dev tools we did see it marks the current components or the current page component we're using so if you think about it there's nothing preventing us from using that instead think about it if I switch back now instead of basic string matching I could say check to see if the component is users and I bet that'll do the trick now so if I come back and try it again we click on users with a full query string but it is still bold because now we're matching not against the current URI we're matching against the currently active page component and that page component just to make sure we're clear is the user's page component okay so yeah if we wanted to take that approach we would say something like if the page component is home and then down here check to see if the page component is settings and that would work as well come back refresh and we have our active links okay so that mostly does it but real quick before we wrap up what we have here I think is perfectly fine I don't personally have an issue with it but if you'd like to remove a little of this duplication again there's nothing preventing you from extracting any number of components to wrap things up so for example maybe you want to set up a nav link component that is a wrapper around the link which is a wrapper around the anchor tag you can wrap this up as many times if you want if it makes you feel better and then you could even add custom attributes like um I don't know active and that will determine whether or not we make it bold so the logic would be here so if we do things right we want this to be identical to this all right so let's see if we can make it work I'm going to add a new view component here called nav link and I'll paste in what we have there next we'll declare our props and we know one is active that'll be a Boolean and then of course I also have to import my link component so I'll do that here and then register it finally I can replace this hard-coded conditional with whatever active evaluates to okay last thing the href is going to be passed through automatically because the component will inherit any attributes you send through that are not registered as props so I can end up with something like this and then I'll turn this into a slot all right I think that'll do the trick so now if we come back up the only remaining thing I need to do is set the text here so home all right so let's get rid of that and give it a reformat and then I'll do the exact same thing to these as well this will return to slash users and we'll set that and then finally one more for the settings page this is Now settings check for settings update the text and whoops update that as well and yeah here's what we get so notice we have cleaned it up a little bit and we have removed some of that class duplication the only remaining step is to import the nav link component import nav link and then update the component reference all right and then finally this Old Link we don't need any more all right let's give it a shot refresh and we get the same thing as we had before so that's just a slight refactor if and only if you think it's worth it okay we can finally move on to layout files so if I switch back I want you to notice how all of our Pages at the moment are manually importing the nav and then adding it here so it's in home it's in settings and it's also end users as well so if I end up with 20 different pages that's 20 different times I have to pull in the navigation and render it where it needs to go clearly you already know this that's not quite right so instead let's prepare a layout file and I'll add it to my shared directory so the very first thing I'll do here is pull in our nav and then of course I need to import it all right and then register it as a component all right so now our nav will exclusively live within this layout file okay so now for example if I go back to home let's swap this out with our layout and then we'll wrap it up here all right and then put everything in except the navigation of course okay but this still isn't quite right if I switch back to Firefox and give this a refresh I've lost the main contents of the home page and that would make sense we've added a layout here and we've added some content there but at no point do we make use of that so again right now I'm just going to put it below the nav all right that recompiles and if I give it a refresh we get what we had before but again now the nav exclusively lives within this layout so next while we're here maybe we'll have a header and we'll have our main app name my app and maybe we want this to render like a typical navigation bar that you've seen a million times so right up here it's it's a different background color The Heading is on the left or the title and then the navigation is inline on the right okay well first I'm going to make this maybe font bold and text large next the header itself will have a display of flex and then while we're here in the nav section the unordered list should also be a display of flex and then the nav why not we push that away from the header so maybe Martin left and I'm just throwing out numbers here maybe six all right we give it a refresh we're getting there but this is too close we don't yet have a background color and then the navigation bullets are on top of each other so for the bullets we either remove them completely or there's a couple things we could do first we could use this Tailwind class called SpaceX and then we give it a figure like four I really like this one it will automatically add margin where necessary to the direct Children of the elements that you apply it to so notice you have margin if we scroll down here you have marching on that second list item and the last list item but not on the first which would be correct okay but again the bullets are still not quite right so there's also what is that list style position and we could set that to inside so again that's just expanding to this CSS property list style position inside and again that's a little bit closer to what we want so let's come on back and paste that in okay next we did say that we want a background color so for example if we end up very very basic with a very light background of gray it's closed but now we have this huge white border and that's because you'll remember on our main blade layout file we added a section with a bunch of padding so it sounds like that's probably not quite what we want anymore so if I remove that yes everything extends to the edges of the window but of course we will manually need to reapply it so what I usually do is add sections for this so my section represents the thing that adds the padding that pushes it away from the edge of the window so if I did something like that and again I'm just throwing out a number here padding six well then I could add our background color there and then for our slot maybe that also is a section and right now I'm just duplicating that padding which is fine if you want you could also extract something like an app section component and that would contain your default padding maybe it even has a container within it but I'm going to hold off on that for now okay so now I have a section for my header and a section for my main content the last step I think is let's just get rid of that section entirely and let's check in come back to Firefox give it a refresh and we're a little bit closer there I think we could probably reduce this font size a bit but other than that we're not trying to build anything specific here we just want it to somewhat resemble a site you might build so here maybe our header is 2XL and huh that's not horrid maybe three and get rid of the Bold but now one quick thing notice if I zoom out all the way to simulate a widescreen device everything's up against the left Edge and you may want your main content to be centered so you may want to wrap your slot in a div give that a reformat and maybe we'll set a maximum width of I don't know 2XL that's 42 Rems if you want to calculate that in pixels multiply that by your root font size which is 16 and that gives us 672 which is still fairly narrow but that's probably fine for what we're building why don't we give it one more 48 Rams 48 times 16 is 768. that's fine and then I'll set the margin to Auto to center it on the page and that's what we get or if I reset it we get something like this so yeah if it helps right here background of Gray and you kind of get the idea your main website would go there okay so with that in mind why don't we return to our header and let's say justify between I've changed my mind here let's push the navigation to the right Edge so I will remove that margin left and there you go all right so excuse that tiny bit of design but I do want to get back to inertia here so I want you to notice now our layout file typically contains anything that should be repeated from page to page so your header the wrapper around your main content if you have a site footer you would probably do it down here as well and then all of your pages would effectively extend from it like you can see in our home component all right so now I'm going to update settings as well and because it's the same process I'm going to speed this up like so so now users extends from layout settings does as well and home does a third time so come back to Firefox give it a good refresh and now as I click between the pages I'll need to adjust the font size there but nonetheless everything is working like it did before but now we are using layout files so now that we're using layout files how do we deal with shared data that should be passed for every single page request for example if you're signed in maybe it should say welcome back John Doe right up here all right well yeah if you think about it inside the sidebar if I go to my home page yeah you could pass it here so for example I could say username and of course in real life you would check if they're authenticated but assuming they are all right well then if you think about it I'd have to go to my home View we would have to accept it as a prop so username is a string and then I'd need to pass that to our layout component so already this is feeling a little cumbersome but yeah this would be the manual way and the down here same thing we're accepting that prop again finally I can use it so maybe something like welcome back username and then finally because we have a display of flex on this header let's wrap this within a div and reformat all right let's have a look and sure enough we do see welcome back John Doe now actually real quick why don't we do display of flex and then I will align the items to the center and then finally on the paragraph tag we'll make a text I don't know small and then maybe a little margin left to push it away from the logo quote unquote all right that should be enough so if we switch back you get the general idea but yeah you're not wrong this is pretty cumbersome we have to pass the data to your page component the page component needs to accept it and then it also needs to pass that data to your layout components the layout component needs to accept it and only then can it render it but here's the worst part as soon as I switch over to the users page we've broken it again because then the users page would also need to pass through the username and very quickly you can see how this falls apart okay so this is all to illustrate that it can be useful to provide shared data that's available to all components and information about the authenticated user is a perfect example of this so let's figure out how to do it I'll start by getting rid of that data prop there and then in home I'll get rid of that like so and then I'm basically just resetting what we already did okay so now we're not passing any information about the current user instead we're going to do that as part of a middleware now you remember when we installed inertia we added or inertia added a handle inertia request middleware I think it's time to have a look at this so if I scroll down this is the interesting part Define the props that are shared by default and you'll see we have an empty array which makes sense for a new project but it does merge in any data from the parent method so if I go on up we can see ah so initially any applicable validation errors will be shared by default which is why if I come back and take a look at view Dev tools in the initial page you'll notice that you always have information about the errors now you know why it's being passed automatically by inertia okay but we can add our own set of data so if I set Foo as bar and come back and give this a refresh now I can access it here Foo is bar and notice if I go to let's say settings give it a refresh I'm still going to have access to Foo okay so incredibly convenient but also at the same time you need to be very thoughtful about this you don't want to pass more data than you actually require because again this is being shared for every request okay so we could do that username thing but it's also quite common to namespace it and that's because you can imagine for for another page on your site at some point the username prop is being passed and in that case there will be a conflict of course so if we name spacesits and again this is a common inertia convention you might end up with something like this the auth user and then maybe username and again I'll hard code that to John Joe okay this is looking good so now think about it regardless of which page I'm on I now have access to an auth prop that contains information about the current user and of course if you're building a lyrical app you could do something like this but but you need to be super careful about this remember anything you pass here is being sent to the client side so if you have columns on your users table that you don't want the end user to see maybe information I don't know maybe a token or information about their subscription status or notes or things like that well with what you have here it's getting sent to the client side which means they can see it if they want to inspect devtools so again as a rule only pass the data that you absolutely need you can do this by being explicit like we've done here or you can leverage larifelle's API resources and we'll have a look at that as well anyways hard coding ad for now will do the trick so if I switch to my layout file now again I don't have to declare a prop or anything like that I can access it directly off of the page component again notice initial page and we'll use page which is a shared property that inertia provides and gives us access to that initial page go into the props go into off go into user and grab the username and that should do it so come back give it a refresh and sure enough we see John Doe and yeah that's confusing remember we're going right down the list there give me the page then go into props then go into auth then go into user and then go into username or don't forget if you're going to be accessing this in multiple places you could also have a computed prop so for example username would return and same thing page.props.auth.user.username and just remember right here it's squawking because we want to access it off of the current instance if we're using it here okay so now you could bring this back to username and you're going to get the same thing so that's an option as well give it a refresh and now we get welcome back John Doe regardless of which page we're on so what we have here is a very easy way to work with shared data now before we move on to persistent layouts in the next episode first I'd like to quickly touch on global component registration for example if you find it a little Annoying or at least cumbersome that every time you want to render what is effectively an anchor tag you first have to import it and then add it as a component I don't think it's a huge deal but I do agree it can be a little cumbersome and it does take you out of the flow now the reason why it's not available automatically well it's probably a few reasons one there are potential benefits to tree shaking when compiling down your code two it can often help with your editor's static analysis of the current component and then three it's generally just a good practice when you're building view plugins to without the user's knowledge register a set of components globally through the entire app so for that reason you have to manually do it or if it makes sense you can register it globally yourself and here's how in your app.js file where we create our app and we use the inertia plugin as part of that let's also register a component called link and I'll pull that in here and I can do that automatically so import your link from inertia view 3 and then we will register it as a global component and if you want to do more just call component again so maybe you want to do a head component or something like that here is where you can take care of that okay so now with this small change you no longer have to manually import it you could get rid of this like so and everything is still going to work if I come back to Firefox and give this a refresh notice I still have my link here and it does update the time when I click on it yeah so that's an option generally I would say be thoughtful about what you register globally you can often run into some traps here now for a link component you use it so often personally I think it's worth it but again make up your own mind on this now one more thing to consider let's bring this back and then on users I'll bring it back to what we had before another option if you're using view 3 and Views composition API is this new script setup style this makes the process of using the composition API just a little more friendly think of it as sugar for manually exporting an object that declares a setup method so for example I could get rid of all of this and simply by importing the components here I then have access to them automatically now you'll see here my editor is squawking a little bit this should work so my guess is PHP storm doesn't quite yet understand how to parse views script setup I bet they're working on that right now but nonetheless if I were to switch back to Firefox and refresh I still see my link but do notice I've lost the time and that's because if I bring it back we did accept time as a prop so again it's a slight change to how you build your components but if you're using script setup you could use this macro called Define props and this would be the same thing and again because this is all relatively new at the time of this recording my editor is having a little bit of trouble but if I give it a refresh this is all to assure you everything still works so I just have to wait for my particular Editor to catch up but yeah I bring this up to illustrate that if you use script setup it then becomes that much easier to locally register your components so if I want to use a layout or a link it's as simple as importing it and I'll use my editor's auto completion and at that point you can instantly use it so one more maybe in settings if I wanted to migrate this over I would say script setup and get rid of that and then in home and by the way I'm not even sure if we're going to keep this it entirely works but it's relatively new and it might feel a little foreign to how you typically create your single file components but nonetheless it is an option and it makes things a lot cleaner so there's your home page there's settings and here's users so you could take this approach or you could take the approach where you manually register a global component or you could combine the two the choice is yours extracting a shared component can be incredibly useful but in its current state we do have one problem have a look in my browser if I open up view Dev tools you'll see that the layout is actually a child of our page component not the other way around so what this effectively means is every time I browse from page to page the layout is being destroyed and rebuilt now if that's confusing let me give you a little example here in my layout component let's do something that changes the state so we'll make this as simple as my app but I will store it in an input okay so if we give it a refresh yes of course I can change this to anything I want but notice as soon as I browse to a different page it resets and again that's because the layout is a child of the page so when you browse to a new page that layout gets destroyed and my guess is you don't want this so let's reset this and I'll show you a more practical example if I bring up another tab here this is the layercast podcast that I host at simplecast.com why don't we grab an embed link here like so and I'm going to paste this into our project so why don't we put it I don't know maybe right up here give that a reformat and get rid of the height but maybe I will push it down a little bit something like that just as an example come back to Firefox give it a refresh and we can now see my podcast at the top of the page and if I start it up you'll hear a little audio in the background but notice if I click on a new link it resets for the exact same reason the podcast is rendered in our layout file but as soon as I visit a new page the layout gets destroyed which means the player is reset okay so what if I want the podcast to continue playing as I browse through my site and in fact you might even notice this at lyricas if you're watching a video and then you click away the video will keep playing in the bottom right corner and I do that through persistent layouts and luckily it's really easy to implement the solution is to effectively reverse it so rather than the layout being a child of the users page I instead want the users page to be a child of the layouts and that way the layout won't be destroyed each time we do that by visiting any of our pages and I'm going to switch to this layout property that inertia provides I'm declaring what the layout is for this component now when I do that I no longer have to manually wrap it like so it will automatically be applied so if we do that for all of these Pages it's a simple process of getting rid of layouts and then switching this like so one more get rid of the wrapping layout and instead register it like so all right so believe it or not that's really all you have to do give it a refresh notice and view Dev tools that the layout is now apparent of the users page and if I go to settings once again layout is the parent of settings and one more layout is the parent of home which means this shouldn't be destroyed every time I visit a new page start the podcast go to users and it keeps playing and I'll turn that down and you can see it's not being reset because it's locked at five seconds it works and what's nice is things like this are basically impossible with a traditional server-side app but if we're using View and inertia and inertia's persistent layouts it really couldn't be simpler so now that we have persistent layouts working if you want we can even remove the need to import the layout into every page component let's play around with that if I were to delete this entirely and switch back to the browser give it a refresh of course we lose our layout okay now I'm going to go into my app.js file and when we resolve the current page component we're going to tweak this just a little bit now what we have here is a dynamic import so that's going to return a promise which means I can't just do something like this because again this is currently pending so Dynamic Imports can be incredibly useful especially for larger projects and we're going to talk about that in the next episode but for now why don't we switch this over to a common JS require now when we pull this in we'll actually need to grab the default component off of that okay now of course we ultimately want to return that page component but yeah we do want to set the layout so before we were manually setting it here on each page but now we're going to do it automatically for every page page.layout equals and we'll need to pull in the layout component import layout and we can tweak this a little bit but this should be enough to get it working so let's switch back to Firefox I give it a refresh and it's back all right so we have a little more work to do here but real quick I'm going to visit all of my page components and remove the import entirely down here get rid of layout like so okay that recompiles so if I switch back and refresh now everything should work like it did before but yeah it's just a little less code to write okay the last thing I want to do here if I switch back there could be situations where certain pages have their own layout components and the problem is right now we would be over writing it so what you could do is say something like well if we don't have a page layout already then let's assign it or another way to write this would be the I can't remember what it's called it's like the the logical or operator or something like that but it would effectively be set the page layout equal to what it currently is or the layout import so this and this are functionally identical and this is what we get all right one more time give it a test and it all works just like it did before so yeah that's a nice little convenience welcome back now before we move on to something else I'd like to quickly touch upon Dynamic Imports and how that might affect your bundle so let's do this I'm going to visit my webpack.mix file and here we are compiling JavaScript we're activating a view we're compiling some post CSS we're versioning our assets but I'm also going to add this extract call and what this does by default is it automatically extracts any common dependencies from your node modules directory into their own file and that way you could have a single vendor Javascript file that can be cached for a long time but then your main app Javascript that might be a little more volatile well that can constantly be refreshed without forcing the end user to pay the penalty of re-downloading all of that vendor JavaScript code you separate them and again we call this simple vendor extraction now take a look at this if I were to compile everything down you'll see that I now have an app.js file that's our application code but then I also have two more JavaScript files one is your webpack manifest file and then the other is your vendor JavaScript code and again have a look how big this is now granted we haven't compiled for production which will drastically bring that size down but nonetheless you can see how useful it is to extract this vendor code into its own file okay so the first step is when we add that extract method all of your code is going to break until you update your script Imports or references so let's do that now obviously app.blade and we'll duplicate this to pull in those other two files manifest and the vendor code and that should do it so if I switch back to Firefox and we give this a refresh there we go okay so standard vendor extraction is useful but now have a look at this if we go into my app.js file here like we discussed in the last episode we're performing a standard common JS require which means all of that code from the page component is being included with your standard app bundle and in fact why don't we play around with that and yeah why don't we look for how about just this tag right here text 3XL let's see if we can find that in our app code JS app text 3XL and sure enough we can find that again your page component code is stored within your app.js bundle and you know what for for lots of projects I think that's probably the way to go but as your application grows you might find that it's useful to dynamically import these page components as they are requested on the Fly okay so here's how we might do that let's go back into app.js and I'm going to return this to a dynamic import but yeah as we discussed that returns a promise which means I can't call default off of a promise I have to wait for that to resolve so what we could do is make this an asynchronous function and then I can say oh wait for this import to complete and only after that's done do I want to grab that default property off of it so now it's a subtle change but I want you to notice something notice the assets from our previous compile now if I run it again it's going to look different so yes you have your app and vendor code but notice you have these three new files and notice each one represents a single page on your site these can now be dynamically requested only when you visit that particular page have a look I'm going to come back to Firefox give it a refresh it still works but then if I pull up the network tab I only want to take a look at JavaScript requests okay so let's go to how about users and have a look there only when I requested that page did we make a request to fetch the necessary data now again for really small projects like this honestly the the benefit is negligible but as your application grows you can imagine situations where certain page components have a bunch of their own dependencies that are needed to make things work and those could be fairly heavy dependencies well in those cases it's a shame to make the user download all of that code especially if they may never even visit the page that uses it so now pretty easily with this small change we're able to pull in all of those dependencies only when you request the corresponding page so once again if I go to my home page then we will request the home Javascript file so yeah this is absolutely something to be aware of and you'll need to decide for your own projects is that something that would be beneficial or would it be easier to turn off vendor extraction and create a single Javascript file that you can cache for a really long time and there is no right or wrong answer here it really just depends on what you're building and how big it is next up let's figure out how to make the head section of your site Dynamic and of course by head I'm referring to the head of your HTML document where you set the title and The Meta tags okay let's have a look but first real quick we don't need this old podcast in bed anymore that was only for an example so I will get rid of it here good and you know what also while we're here give me 15 seconds to clean that navigation up real quick Let's see we have a nav link why don't we change the text from Blue to plain black and then I don't think we need list items here so let's get rid of that increase the spacing and get rid of that okay let's see how that looks and yeah a little more traditional I think that looks fine okay let's get going so yeah if you think about it right now our head is being defined in my main blade layout file and notice that's separate from the root of our view in inertia app which means if I set the title here yeah how exactly do I make that Dynamic when inertia doesn't know anything about it okay let me show you how to solve this inertia ships with a head view component that we can reach for so right here I'm going to use standard script setup and we will import inertia's head component okay so now at the top I can use it here and I can define a title my app dash home all right so let's try it out we come back we refresh and sure enough I am setting the title here now a quick little tip if all you ever need to set is the title you can instead add it as a prop app home and that would be an option as well as a self-closing tag so we come back refresh and that works as well okay but anyways I'm going to bring that back to what we had before okay so yeah if you want for every page you could import your head tag and then add it at the top or of course don't forget if you want to register that globally you now know how to do that and that might be something you want to consider anyways there's settings and then finally one more for the users page and this is not script setup why don't we switch that over real quick and I will Define props like that okay now at the top I can paste this in and this is our users page okay so cross our fingers and I think it should work again pay attention right up here so we have home users and settings it works now here's another cool thing we could have a fallback of sorts so for example what if we forget to set the title on a particular page well right now if I click the users yeah we just don't have a title on that page now if you want we could set a default in our layout file let's see what that might look like let's go to layout I will import it here and again this one is not using script setup so I will have to manually register it as a component but yes same thing right up here at the top I'll paste it in and this will be generic we'll just say my app or again we could use this syntax here and reformat okay so now yeah again we have a fallback of sorts if we don't set a title then we will stick with what is declared in the layout file but if we do set a title and I'll just undo to bring that back well now if I give it a refresh you'll see that takes precedence because again you can only have one title on the page but this is where you might fall into a little trap so what if you want to do the same thing and I'm going to bring this back where the title is declared inline what if you want to do the same thing but you also need a description so we'll say type is description and the content is you know information about my app whatever yeah that's going to work so if I come back and refresh and Dev tools if we have a look around sure enough we have our meta tag but notice if I want to override that from a particular page maybe the home page home information notice you're going to end up with a duplicate when I refresh okay let's go to home give it a refresh yeah notice we have two meta tags for the description and that's not what we want so yeah if you want to allow for a sort of inheritance here we need to give inertia a little more information we do that by adding this head key we'll call it description because that's what it is then I'm going to do the same thing on the layout file an inertia will detect this and ensure that we only ever have one okay so now if I come back and give this a refresh notice that we only have the single met attack and notice how it does that it adds this little unique inertia attribute but yeah for any other page where you're not setting the description it'll fall back to the default okay so that helps but now one last thing notice that for any page we begin with the app name whatever that happens to be same thing here and again this is such a small site it just doesn't matter if you need to tweak it then you update a few files and you're done but again as an example for a larger more real life project if you later want it to be the page name and then maybe the app after it again you would have to visit every single page where you define a title and that could potentially be 50 pages so there's another way you could do this by defining a template of sorts let's bring it back to what I had before if we visit app.js where we create our inertia app let's add another one here at the bottom the title so this is a function and whatever we return will be set as the title for the app so if I hard code that to Foo and if I come back and give this a refresh sure enough the title is set to Foo but this will accept a argument here which is whatever your current title is so notice here that would be this right here okay so if we accepted that title and simply returned it well we haven't really done anything here but just to show you how that's working sure enough we do get my app users okay well now maybe you see where I'm going we could bring this back to Simply the title of the page and I'll do the same thing here and users and just to show you okay now it's only showing the page but now in my template so to speak I could add it here I could say my app and then tack on the title come back refresh and now I'm defining my app's name only in one location or or whatever the prefix happens to be you see how that works and that means later yeah if I do decide no we want it to be the title and then my app you can see how easy it is to update that and now it works okay and of course if you want you can switch this over to a template string like that now I generally prefer my app dash so we'll stick to that and I think that looks good okay so now you know how to make your head tags dynamic the main steps are to import your head component Define it at the top and if you happen to be overriding a default that you might be declaring in your layout just make sure that you assign a unique headkey so the only remaining thing I might personally do if I were building an app here is again I'm probably going to have a head on every single page so it's a little bit of a shame I think to have to import it over and over every single time but you may disagree so I'm going to pull this in right up here and I'm going to register it as a component here like that and now I have it globally registered which means I can once again get rid of that and get rid of that and then get rid of that and I think that should do the trick so again last time I'll bring that back reformat and maybe I can do the same thing here all right final sanity check give it a refresh that looks good the home page looks good and the settings looks good all right onward to the next episode now if you've never built a single page application before there's one very big security concern that you need to be aware of so let's have a look at that in this episode I will switch to the users Tab and why don't we turn this into a list of users that we fetch from the database and then send to the client all right so let's go into users and yeah I think we can get rid of this old section here where we show the current time and instead we're going to accept a list of users users is an array okay so as you can imagine we'll have some kind of unordered list and then I'm going to Simply iterate over our users so for user and users next it's a good practice and view to always set a key on your Loops so that'll be the user's ID and then finally I will set the body of this list item to the user's name and reformat okay so now at this point of course we're still not going to see anything because we're not yet passing users from our controller that's the next step but to set this up I do need a database so why don't we quickly whip up maybe an sqlite database something like that and then within my database directory I will add our database database.sqlite which is the default okay finally and this is all basic laravel if I run my migrations included in those will be a users table that has a name an email address a password a few other things so let's do that now switch to the terminal run PHP Artisan migrates there we go and if I quickly open this here we go and sure enough I do have a user's table but it's currently empty okay let's quickly seed it so if I switch over to my Cedars folder you'll see this is what larifelle includes out of the box and it has a little snippet here to quickly whip up 10 users why don't we change that number to a hundred now I can run PHP Artisan DB seed and if we did everything correctly we should now have a hundred users in our database table and we do okay so that setup is out of the way the next step let's go to our routes file again and let's see it right here is where we access the users so no longer will I be passing through the current time instead I will send through a list of users so let's say user and at least for now we're going to say user all but there's a bit of a red flag here and you'll see why shortly okay I can refresh I think I'm going to see 100 users and I do great it's working but yeah that red flag let's come and look at view Dev tools and let's see what exactly is being passed from the controller here's our users it's an array of 100 items but yeah notice that every item is an object that includes a lot of data now again in this case it's a brand new project so this is fairly minimal but you can imagine for any real life project your users table probably includes a dozen different columns maybe related to their subscription status or maybe unique billing tokens that you don't want to expose to them but the problem is right now all of that information is being passed to the client which if you think about it presents two problems the first problem is you're passing through more data than the page actually requires and that means more data is going over the wire but the second issue and the really big issue I think is that again the entire structure of your users table is being passed to the client including any fields that you don't want the user to see now I imagine this is pretty clear but just to make it crystal clear yeah maybe your users table includes some kind of token maybe for stripe or something like that I'll just say stripe token and that can be nullable okay then real quick in my user Factory think of this as the blueprint for a dummy user record and we'll save right here actually I can just duplicate this create a dummy stripe token okay so now I'm going to refresh my database from scratch and I'm also going to seed it in the process okay so again if we reopen that file we should have a new stripe token column so now think about it any and every time you add a new column to your users table well because we used that user all method all of the data is being passed to the client and we can access it again right here props users and notice there's the stripe token being exposed to the user and it's not just their token it's the stripe token for every single user including the email address for every single user so if I wanted to I could access this page and plug the email address for every user in your system so yeah clearly not a great idea okay instead it's better to be explicit so if I go back to my routes file instead of blindly passing user all to the client let's first map over it so that we can return a subset so for example maybe we want well actually at the moment the only thing we're making use of is the user's name so why don't we keep it as basic as we can possibly get and then later if we need more fields we can pass it through at that point okay so have a look give it a refresh and now if we take a look and this time we'll go to the actual users page component yes we still have our user's array but notice that each user object contains only the data that we actually need and none of the data that we don't want them to see so this is a really important pattern that you need to follow in your own projects when you're building a traditional server-side application there's not a big deal with passing something like user all to a view and that's because that data isn't being exposed to the end user but when you're building a client-side application yeah the rules are a little bit different it will be exposed through an Ajax call and again you can always review those if you want to see the actual call taking place switch to your network tab turn on xhr visit a page and sure enough we make a request to the user's endpoint and that Returns the Json for all of our users but luckily because we were thoughtful we're not exposing anything that we don't want the user to see so now that we've used eloquent to fetch information from the database and send it to the client The Next Step would be to implement patination but before we do that let's take one minute to clean this up now because I'm using tail 1 CSS why don't we save ourselves a little bit of design time and instead pull in one of these free components from the tailwindui.com website maybe something like this so I can see the code here I can copy it and I'll switch to phpstorm we'll go to that users page and yeah we're going to swap out the UL with this table layout so I'm going to paste in the whole thing there's a lot to see here but it's really not that complicated and if I switch back and give this a refresh now we can tweak this to our needs for example I don't even think I need a table head so let's get rid of that all right and then next I don't need any of these so let's see here's our table rows here's the first one here's the second one let's get rid of that okay and then two more that one and that one and now we have a name an avatar and the edit link but I don't yet have any Avatar so I can get rid of that all right and then it looks like I still have some leftover margin here because of the Avatar that we deleted and then I'm not going to show an email address so that gives us this all right so this is what we end up with a standard table element with some classes to make it look nice and pretty so I think we're all set let's now migrate to this section so I'll get rid of it and now for our table rows this is where we will perform our V4 V4 user and users we need to give it a key and actually on that note I'm not sure we passed through the ID let's check that real quick let's go to my routes file and yeah notice I accidentally forgot to include the ID so let's add that as well or if the ID is something sensitive that you don't want the user to see you could always reach for a unique ID anyways let's go on back and now I can swap this out with the user's name finally we don't have any kind of user edit link but if we did it would take the shape of something like this where the href would be potentially users slash the user's ID slash edit and we can even make that look prettier if we switch to a template string like that all right let's have a look give it a refresh and now we have a nicely formatted table of all of our users and notice that each user includes a link to where we can edit that specific person okay design portion done next let's Implement pagination now if you're familiar with laravel you already know that there is a paginate method we can reach for but this isn't quite right so if I come back and refresh yes I still see the same thing but if we have a look at view Dev tools let's go down to our page and we have our users but there's nothing different it seems like it's partially working because we're not fetching 100 users anymore we only have 15. so for example if I change that to 10 at a time like this so yeah it seems like it's partially working but I'm missing information about the paginator what current page are we on what are the links things like that so here's what the issue is we called user all and then we mapped over it and when you map over a patinator you're effectively replacing that collection entirely so for example if I were to delete that and switch back I bet we're going to get some errors give it a refresh and yet now it doesn't work anymore as you see there but this does mean we're on the right track so have a look at this if I were to just return this directly from the routes before we even touch inertia have a look what happens no longer are we returning a collection of only users but instead an object that yes contains all of the user's data that's now behind a data property but it also again has information about the paginator what page are we on what's the page to the first URL what are we starting with what's the last page and even cooler there's this links property that contains all of the relevant links to build up your patronator on the Fly and I can even tell you what page you are currently on all right so this is pretty cool I love how easy larval makes this if I bring this back now I just have to update my users component to accept not an array of users but an object that represents that paginator okay now if we scroll back up we're going to iterate over not the users array but the array that's in users.data and you saw that just a minute ago okay so I think that should do it if we come back and give this a refresh we now get the same thing as before if we take a look at this and view Dev tools on our page yeah now this is what's being sent to our component we have the array of users but we also have a list of links to build up the patinator okay let's see if we can Implement that so let's see maybe to start we'll do it here at the bottom and I'll say this is our paginator okay so we'll have this in a div maybe we'll give it a little Merchant top and you'll see effectively what we're going to do here so down here at the bottom we want a list of links okay so let's see once again how do we build this up we want to go into users dot links it's an array so we iterate over it and for each one it looks like we have a URL and a label okay so maybe for each one we should display a link link V4 link and users.links next the href is going to be link dot URL and then the text or the HTML will be link dot label and reformat and let's see vhtml it can lead to X yes I'm aware of that is not a problem here okay so I'm just grabbing that information here let's come back and give it a refresh scroll down to the bottom and it's not styled but notice I think it'll work so let's go to page 10. and notice the URI reflects that and we have fresh information so Bella at the top let's go to page five and now we have Odessa at the top everything's working so we have just a couple things to wrap up first if we're on page one well what would the previous link be if you're on page one let's see what that does well right now we are rendering a link that goes nowhere okay so let's see how that's rendered on the first page users links well actually most of them will contain a URL but yeah on the first page there is no previous link so there is no URL in that case which means I shouldn't render a link and the same would probably be true for the very last one or I'm sorry at least if we are on the last page there won't be a next link of course so if we take a look at that now notice the URL is null in the cases where there's nowhere to link you okay so how do we want to do this I'll show you the long way and then maybe the cleaner way we could start by wrapping this in a template so we iterate over all of the links and for each one we render a link so we get something like that but then within here we could say well if we have a URL only in that case should we render a link tag otherwise we should render a span tag and then here we'll set the HTML to link.label and yeah I think that will do the trick let's give it a shot refresh scroll down to the bottom and notice if I'm on the last page the next link is a span which is what we want so yeah at this point style it however you want to make it clear this is muted it's not clickable and the way that these other ones are but notice the previous link if we have a previous page is a anchor tag or an inertia link but if we're on page one sorry we don't have much real estate here in that case it would become a span okay so that works and this is entirely fine but another option is to use a view Dynamic component to do that I'm going to bring this back to what we had before like this but instead of explicitly using a link component we're going to use this Dynamic one and then I can use the isprop to declare what kind of view component it should use so for example if I wanted to repeat a link then I could do that and the same thing is going to work just like we had before okay so now we can build this up dynamically I can say if we have a URL then it should be a link otherwise it should be a spin and in fact this should do the trick so if I come back and give it a refresh notice we're going to get the same thing as we had before but notice we're using general spans where relevant and now finally we can add some basic classes here to make it look pretty like a little bit of padding around each and then actually why don't we do one that's Dynamic if it's a span maybe the text should be gray to to Signal you can't click on this so I could say once again link.url text or actually nothing in that case but otherwise text Gray 400 or maybe 500. all right let's give it a shot come back refresh scroll down and there we go and that's good enough for our little demo here okay one or two more quick things and then I will let you go uh if you'd like we can extract this to a reusable view component and that way anytime you need a paginator it's up and ready to go okay let's go into resources JS share it I'm going to add a new view component here called pagination and then let's grab to start everything you see here and I'll paste that in but we don't want to assume Merchant top so I will pass that in and then let's see what does it need here to function I guess all it really needs is a array of links so let's do that here and then iterate over the links rather than assuming that we have some kind of user's object because it could be links for for anything that can be paginated all right I think that is good enough for now so if I switch back we can pull in our pagination component and we can send through the links like this and by the way notice PHP storm automatically imported that for us all right cross our fingers come back refresh and it failed okay let's see what I did wrong user is undefined oh I'm sorry users.links all right one more time give it a refresh and yeah now it's working I did lose my March in there though so this is where I can pass it in on the Fly and we should have exactly what we had before and I think that looks pretty good so finally the only remaining step is to return the mapping from our controller because right now once again our users array contains everything from the database so we can fix that by returning to our routes file and right here yeah remember how before when we called map we had something like this where we returned the ID like this and the name but when we called map it replaced the paginator completely so we get a new collection we can fix this by changing it to 2 through is is almost the same as map but it's applied to the the current slice of items rather than returning a brand new collection so I think that should do the trick if we give it a refresh we have a look at users cross your fingers and yet now it's working just like it did before but now we have a working patinator and I think that we'll do it I'm sorry actually one last little thing I keep telling you we're almost done 10 seconds and then I promise we're done if we go back to the paginator let's make sure that we highlight whether we're on the current page so we can do that maybe I'll rewrite this into the object form let's do text Gray 500 if we don't have a next or previous link and then I'm going to say let's make it bold if we are the current page and you'll remember there was an active property available so now we have a little more indication as to what page you're currently on and with that we are finally done and ready for something else so I think the next step will be to add a layer of filtering right up here when the user types into a search box we will dynamically update the results to include only the names that match that query okay let's get started I'm going to open up my users component and right up here at the top we're going to add some kind of input here and the placeholder will be search okay let's see what that looks like give it a refresh and yeah but we want to get it right up here so it sounds like we should wrap the H1 and the input within a div I'll give it a reformat and then on the div I'll say do a display of flex and set it to justify between which means this will be pushed to the left and this will be pushed to the right okay now if I come back and refresh it's a little better but I do see a bit of margin here so why don't we migrate that right up here okay next on the input itself why don't we give it a class of border and maybe a little padding on the left and right and then finally I will make the input rounded okay let's see what that looks like pretty good so now yeah as I type into this I want to update the results here which means some kind of request to the server needs to take place okay so it sounds like step one would be keep an eye on this input for changes how do we do that let's set V model and I'll call it search and then if we scroll down here if I'm using script setup all I have to do is Define search and make it a ref which I will import here and we'll initialize it to an empty string and do note that we pulled that in on the other hand if you're using the options API like you might be then use that data method where you return an object like you normally do so as an example if I set Foo bar here and I come back and refresh the default value will now be Fubar okay so that's working the next step like I said is to keep an eye on that input for changes so let's pull in we could do watch or watch effect let's do watch and now we'll say keep an eye on search for changes and when that happens at least to get us going why don't we consult outlog changed and then the value and let's see how that looks so we come back to Firefox give it a refresh open up Chrome and notice as I type here sure enough I get the current value of the input great now we will need to take care of situations like this where we perform too many updates if the user types quickly but we'll deal with that in just a little bit the important thing is we can now respond to when the user types into that search input so let's perform a request now up until this point we've been using inertia's link component the the wrapper around an anchor tag to perform these requests but we can also trigger it programmatically when you need to and this is a good example of that so again if you're using the options API you know that you can do things like this dot inertia but if you're using script.setup you don't have access to this so instead we have to import it manually like this import inertia from inertia okay so now I can say inertia dot get nice and clean so let's make a get request to the current page and that would be slash users now as the second argument we can include any data that should go along with the request so because this is a get request that would be included as part of the query string so let's include search like this okay so as you know at the moment on the back end we're not doing anything to filter the result set so that's not going to work but if we did everything correctly I do want to at least see an Ajax request take place so I will type Q and sure enough inertia immediately makes a request to the server and now it's including what we typed but yeah again on the back end we're not doing anything we don't even know that exists so we return the usual result set no change now before we fix that though notice that there's one weird thing here if I type it does make the request but then it immediately resets the search input and that makes sense if you think about it when we run inertia.get that is equivalent to clicking on one of those link components which means the whole page is going to re-render and as part of that it's going to clear out any state like you see here because usually that's what you want to happen when you visit a new page but in situations like this it's a little bit different right I want to preserve the states I don't want to lose what I type here okay easy fix all I have to do is ask the third argument set The Preserve State option to true now we're saying when we perform this request don't get rid of the existing state on the page all right let's give it another shot I'll give it a refresh and now notice as I type in here are a couple of things I don't lose focus of the input and I don't lose the state value itself which means again if I open up the network tab and let's start looking for ernestina yeah notice as I type in there we are performing a lot of queries so again we need to deal with throttling there but again the important thing is we are making a request to the server with the given filter okay so now let's move over to laravel let's go back to our routes file and let's see here's where we load that users page so it sounds like at some point here we need to filter the incoming query based on whether or not we have search in the request all right let's get going now this could get a little messy so a little tip here you can begin a query by calling this query method and that just means start a new query so often you'll see developers do this because it allows you to clean things up and put each method call On Their Own Line like this notice how that reads A little better next we're going to make this call to win when is a cool method it's basically a object oriented conditional we can say if the given conditional turns out to be true then append to the query in this fashion okay so let's say look in the request for that search key now you can use the request helper function if you want or or kind of use some facades here so I could say request input search so if you found something there only on that condition should you trigger this closure here or in other words when you find something for the search input append to the query in this way so now it's very easy think about it how would you filter down the users table what query would you write well you would do something like query where and we're effectively saying give me only the records where the name matches what the user typed into that input so I could say where name like and I could retype request input search or that will be passed to me as the second argument so I can do it like this so yeah we could do this but we really want to say anything can come before it and anything can come after it so as long as that search string exists somewhere in the name column it's going to be a match so we can do it like this or why don't we use string interpolation like that okay let's see how that works so I'm going to come back to Firefox give it a refresh and now you see it's working we have a couple issues because maybe that search input should show what we previously typed but nonetheless it seems like it's working so let's delete this and start from scratch and now why don't we look for anything starting with an A and then a u and then a b and we get Aubry everything is working and it's really nice and quick let's look for Sophia s-o-f-i-a and it looks like we have two isn't it neat how easy that is but we're not quite done we still have a couple little issues here so let's come back and just look for again uh a now if I scroll down to the bottom it looks like we have nine pages worth of results but yeah if I tweak that again now we only have one page okay so that part is working but notice if I click on a new page here hmm I lost my search query here and the input was reset so let's figure out why let's go back give it a refresh we will search for something but yeah now if I have a look at any of these links notice that they link to page two but they ignore the current query string okay well luckily laravel makes this a really easy fix if I come back after the patronate method here I'm going to include this call to with query string and this is nice because it reads pretty cleanly paginate the results and include the query string as part of that okay so with that one change when laravel generates the links down here it's going to take into account the current query string so notice if I give it a refresh and have a look at them now they do include that search query which means that should fix our current issue let's see we're searching for s the search input is still not reflecting what's in the query so we'll fix that shortly but if we did everything right when I go to page 2 it will be page two of the results with this current search so that's looking good all right just a couple more things and then I'll let you go what about the situation again when the user maybe lands on a page that already includes a search query so if I get rid of this what if they land on this page right here okay well it's showing search results for anything containing an S but the input does not reflect that and that makes sense again if I go back to my users component we initialized it to an empty string so of course that's the case how do we fix this uh well you have a couple choices one on the client side you could read the query string and say well if there is already something for search then we'll set the initial value to that or you could pass it from the server side so let's do the server side approach I will visit my routes file and now we're going to pass through users but also the existing filters on the page like this and that would be request and you'd include any filters that you respond to in this case the only one is search all right so now when we load the page the server is going to send the client a list of approved filters basically okay now let's accept it from The View component right up here our props are now users as well as filters which is an object all right let's have a look come back give it a refresh open up view Dev tools and if we now look at the users page there we go here's our two props we have our users and we have a list again of the approved filters that we want to work with okay so now we only need to set the searches initial value equal to what we sent from the server so we'll say props.filters dot search all right let's see if it works notice that we're currently searching for S but it's not reflected in the input but if I give it a refresh now it is okay so let's look for Enola that works it's in the crease string and now if I give it a refresh we remember it let's see if General patronation works we have w we go to the next page decreased string is updated the input State remains unchanged which we want but the results update okay so now we are just about done the only remaining step is if I well actually real quick let's open up a brand new window and now I have no history here okay so notice if I slowly search for ernestina this is all working however if I go to the back button here notice it created all of these history records and each one will take you to the previous search which might be what you want or it might be kind of annoying notice if I'm on the home page and then I go to users and I search for something and then I decide ah I don't care about that let's go back to home I would have to go through every single keystroke which you've probably seen this before and again it might be what you want but in this case I think it's kind of annoying so we fixed that with a single change right here where we perform our request let's add the replace option and that should do it so one more time let's start with a brand new tab and this time if we go to home then to users then we search for something and I go back notice it takes me right back to home and that's what we want in this particular case notice again as I search here we're not creating new history records and that's because each time we make a request here we are replacing the current one okay so I think that's about enough for this episode in the next lesson we're going to deal with the situation where when the user types into this input we're currently making way too many requests to the server stay tuned okay so why don't we move on to forms in inertia which I think you're really going to like so I'm going to begin by setting up a new endpoint so we'll do it how about right here when the user visits users slash create we're going to load a view um we could name this something like create user some people will do the the most important word first so they'll do something like user create user show or we could name space it something like users slash create or dot create all three of these are super common conventions just pick one you like but yeah this is traditionally what I would do so if we're going to take that approach I should probably update this one to be in a user's directory and if we're listing users I'd probably call that index okay let's make our updates here so end pages I now have a new directory for users and that will go in there and then finally I will rename it to index so now let's add a new one for creating a user okay so as usual I'm going to say hello world just to make sure it's working I'll come back to Firefox give it a refresh and if I manually visit user slash create there we go all right let's get going first up we need our head tag and that'll be create user keep it simple and then we'll have our H1 that will be 3XL and it says create new user okay come back give it a refresh there we go let's set up our form right here so think about it what do we need to create a user well you need to give us their name their email address and a password now I have a little snippet that I often use here for inputs and we'll go over this real quick so we have a div with a little margin we have a label for the person's name and that label is for this input here again very simple stuff just some simple Tailwind classes to make it look a bit more pretty all right back to Firefox and this is what we get okay why don't we set a maximum width and then push it down from the heading so we can do that right here and we'll say maximum width I don't know medium we're going to center it and then push it down from that heading Maybe by seven or eight yeah something like that okay so what else do we need here we need the user's name we need their email address so I'm going to do the exact same thing again for the email but the only thing I will change is set the type to email all right come back give it a refresh there you go what else do we need a password all right one more time for the password but of course we want to set the type to password so if you want if you want to get a little more fancy you could even add a toggle to change it to regular text if you actually want to see what you're typing in finally I want a submit button so I'll do that right down here and I do have I think a form submit uh snippet there again I'm not trying to go too quickly here but it's mostly CSS and we don't care about that too much for this series all right so I think we're looking pretty good here now as you know when working with view in order to submit this form asynchronously we need to track what the user is typing into each of these inputs so with that in mind let's set up a data property so again we could do it here with the options API or if you're using the composition API we could switch to script setup and then Define it like so let form equals an object that form will contain the user's name their email address and their provided password but of course with the composition API this isn't quite enough we need to make it reactive by pulling in in this case we have an object so I'm going to pull in reactive from View and then we'll wrap that okay so now if we scroll on up think about it we can set V model here to form.name so I bind the value of this input to that field or property we're going to have another one here V model to form dot email and then down here B model to form that password and I'll reformat okay cross your fingers we come back to Firefox give it a refresh we're going to take a look at view Dev tools and if I look at our page component here's our form object and notice as I type into it here we go this is what we want okay so now we're successfully tracking what the user types into the form for each of these fields so the only remaining step is to listen for when the user submits the form and use inertia to make that request rather than the way we would traditionally do it with a server-side framework okay scroll on down and let's see why don't we set up a method called well submit web cement equals a function now how do we submit a form with inertia well it's really easy let's import it import inertia and then I can simply say inertia dot post now if you're curious in the next episode I'll show you how to use inertia's form Helper but for now we're doing it more of the manual way okay so what are we posting to well if I'm creating a user that in point would be a post request to slash users and then the data I'm sending through would be this form data and that should at least get us going all right so come back up in honor form we don't actually need any of this because we're not traditionally submitting the form so instead I'll say when you submit this form prevent the default action and instead call that submit method that we created right down here all right let's give it a shot so to review this again I'm going to open up my network tab so I can see exactly what happens okay John Doe John at example.com gibberish and if I click submit well it does fail with a 405 but we expected that the important thing though is we did hook into that form submission and make the proper request we made a post to slash users and we did send through the relevant data okay so let's return to the service side and we're now going to listen for a post request to slash users route post to slash users and now this should be laravel 101 stuff like validate the request then create the user then redirect somewhere okay so how do we validate the request well you might do something like request validate or you say the name is required so is the email and then the passwords required but also for email why don't we say make sure that's a valid email and then for the password you also might want to say it's a minimum number of characters or something like that but let's keep it nice and simple okay so assuming that the validation passes on the server side we could then move on to creating the user and that would take the shape of something like user create so once again we could manually fill these out or I would have to say something like request input name and that's totally fine another little tip when you validate your request the validated attributes alone will be returned from that method call so you'd get something like that so if you want you could even pass that to user create and remember we would only get to this point if validation has succeeded next we just need to redirect somewhere okay so let's talk about this for just a minute traditionally when you're building an Spa when you respond to a request like this you might return a Json response or something like that that includes information about the user that was created but remember we're building an inertia app which is a little different it's a bit more similar to how you would traditionally construct a server-side application so in this case when we're done we don't return a Json response we just return a typical redirect where do we want to go now well why don't we redirect return redirect to users something like that and what's nice is even though inertia is still performing a traditional Ajax request it's going to pick up on this and automatically follow that redirect okay so again let's cross our fingers and give it a shot give it a refresh I'm going to create John Doe submit and if we did everything correctly he should now be included here and there he is let's have a look at the database open database database.sqlite and let's see right here there's John Doe so it's working but we do have that one glaring issue where we aren't yet hashing the password all right so obviously we shouldn't ignore that now you could do it here as part of your create call but it's such an important thing I will often do it as part of a mutator so if I visit my user model I might do something like set password attribute that'll accept the value and then here I will automatically encrypt it so I would say this attributes password equals and then I will pass the value to bcrypt alternatively you can do something like hash make cool okay so let's give that one more shot I will delete John Doe's account and try again okay so one more time I'll say John Doe two submit all right back to table plus give it a refresh and there we go we have John Dos account again but now notice that we are properly hashing his password okay so this is good it's working but what about the situations where we try to create a user but the validation fails okay we'll deal with that in the next episode all right so in the last episode we got the happy path for creating a user up and running but now of course we need to handle failed validation okay first up let's add a little link here to create a user so we'll visit user slash index and yeah there's that H1 so why don't we add our link here and that'll go to user slash create right and we'll say new user okay why don't we make it blue [Music] and if we have a look yeah maybe it's a little too big and it should go right over here okay let's make it smaller and then I'm going to wrap this whole thing within a div reformat and then on this wrapper alone we'll make this a display of flex align it to the center and then we probably need to push this away from the heading a little bit so maybe Martin left of two all right how's that look yeah pretty good maybe three and you know what it's fine so now we at least have a link to create a new user okay so think about it if I now switch over to user slash create we have some automatic browser validation and that's because I added this required attribute so what this means is it physically won't let me submit the form or trigger that submit event if I haven't filled anything out have a look and that's useful but again remember you can never exclusively depend on browser or client-side validation you still have to do that second layer validation on the server side and that's because of course the user can always bypass the front end entirely so for things like this they are nice to haves they're little helpers but the point is you can never depend on them okay so to demonstrate that temporarily I'm going to remove that required attribute from there there and right up here okay reformat now if I come back and refresh and I try to submit the form we don't get any feedback at all so let's try one more time but this time with the network tab open submit and we did make the request but we got a 302 redirect back to this page Let's see we made our post request we didn't send any relevant data through and then we got a redirect response but notice now because inertia will automatically share the errors we can see that the validation failed the name is required the email is required the password is required so it looks like everything is rendering properly we just have to conditionally display the errors all right let's do it what if we added to start maybe right down here I might say V if and how do we look into those errors well I'll show you two ways first we can look into the page component into the props into the errors and then look for an actual property name so page props errors dot name so if that's not empty or null then let's set the text equal to that then we'll set a class of maybe red and extra small maybe italic whatever you want and actually maybe we should also push it down from the input just a little bit okay let's give it a shot so one more time I refresh I submit it and now we have feedback cool so we could extract this into its own little helper component but for now I'm just going to duplicate it and that's okay so this would be for the email and then finally this would be for any validation related to the password all right let's see there we go that's what we want so why don't we set it up where the name passes there we go now only these two fail let's make that pass as well and everything is working just the way we want okay so that would be fine another way if you don't want to reach into the page component which I don't see any issue there but you could also declare it as a prop so again we're using the composition API so we would do Define props and I could accept the errors here so now we sort of have access to them locally that would allow me to then replace every occurrence of page.props.erres with simply errors like that it's up to you come back to Firefox refresh give it one more shot and it still works okay so now yeah The Best of Both Worlds would be to have some automatic browser validation right there and here but then of course you have server side validation on the back end so you have two forms of production and if that does fail then we redirect back to that page view will automatically re-render it and in those cases we'll display a little bit of feedback to the user all right but we're not yet done with forms in the next episode let's have a look at inertia's form helper which is even cooler now at the moment our form is working which is great but it's still a little naive anyone who's ever deployed a form to production before knows that people will take advantage and do all sorts of stupid things so to illustrate this what if I were to create a dummy user but before I submit this we're going to simulate somebody who just spams that submit button so what I'll do here is because it's so fast even with throttling I'm going to go to my routes file and artificially add some throttling here so right here I'm going to sleep for three seconds okay so yeah what will happen if I visit the network tab submit submit submit submit submit submit submit you can see they've made about seven requests in that span but inevitably of course you're going to get some kind of query constraint because you're trying to create too many users on the back end okay so the common conviction for dealing with this is to automatically disable the button just assume that the user is guilty and they're going to do something weird so disable the button immediately okay so if we were to do that manually which is what we often do we might go to our user create form and we'll write down here we're now going to track if and I'll pull in ref here we're going to track if the form is currently processing has it been submitted okay we'll say let processing equals ref and by default no you haven't submitted the form it's not processing and now why don't we just say to start right when we submit the form and we call this method let's update processing to true so processing dot value because again we have that ref there so to get the underlying Boolean we have to call that value which is kind of annoying to be honest but that would do it all right next if I scroll up to our button let's say disable the button if the form is currently processing okay last little thing let's just comment this out temporarily so that we don't redirect too quickly because I mostly just want to see that button deactivate okay we come back to Firefox try to create a dummy user and when I click submit and it looks like the button doesn't have any disabled styling but if we select it sure enough it is disabled and if we come to our component itself processing should be set to true and it is okay so this prevents the situation where we are submitting the form too many times notice as many times as I click on it we're not performing that HX request again okay but of course we want to turn this on and off conditionally so one way we could do this is to bring this back and as part of the third argument we can provide options for inertia and some of those options are event hooks for example when you start the request or when you finish the request all right let's use those so when you start why don't we say processing that value is true and when you finish let's make it false okay so let's test it out first let's do something that will fail the validation so maybe on the password I'll disable that required attribute just so I can bypass the browser validation okay next let's fill this out but we won't provide a password okay now real quick we're going to inspect this button and don't forget on the server side we have a sleep for about three seconds so I click on it and you see it's disabled one two three we redirect back to this page but now the button is once again clickable it works so we try it again but notice I can't click it anymore because it's currently disabled okay so you've probably done this a million times if you've ever built a client-side form but what's nice is if you want inertia can automate some of this stuff for you now the first thing we're going to do is pull in use form the inertia adapter and I'm using view 3 here then I can replace this call to reactive with use form so this is how we would do it with the composition API if you're using vue2 or something you might instead do this dot inertia dot form and then you would provide your Fields here so it's just a slight tweak to how you do it with the composition API versus the options API okay so that will automatically be reactive but now we sort of have this form on steroids which means our inertia will automatically collate and track information about the form including whether or not it's currently processing which means I no longer have to do that myself and that means I can get rid of that import and even better I no longer have to manually track this processing property instead well to start I can just remove it entirely but because we're already using form I can instead rewrite this like so and now I can remove that inertia import kind of cool isn't it so the only thing we need to update now is we don't have processing at the top level we now access it through the form helper so form dot processing or if we want to grab the errors we can get rid of that we can just do form dot errors so let's grab all occurrences of errors and I'm going to replace that with form dot errors like so okay let's give it a shot so I come back to Firefox we give it a refresh and first I want you to have a look at view Dev tools so if we look here yeah notice in our form object and notice here we have the the restful endpoints you'd expect like making a delete request or post request or patch request we have information about any errors we can check if the form is currently dirty if it's currently processing what the progress is if it was recently submitted we can even reset the form so for example why don't we play around with that real quick let's just say set timeout and I'm going to reset the form just as an illustration form.reset after three seconds as you'd expect that's going to clear out the form so refresh let's quickly fill it out and that should be about three seconds and it resets so you get all of this stuff for free when you pull in inertia's form helper okay so the only remaining step is just to make sure this works so I'm going to turn off that sleep come back give it a refresh and we'll set up Jane Doe's account this is the happy path it works we can find her here or if we now do Susan but she doesn't provide a password submit then of course we still pick up on those validation errors we fix them submit it works and now everything is up and running now that you're a bit more comfortable with forms let's move on and discuss debouncing and throttling so you'll remember a few episodes ago we set up real-time search which is great but yeah have a look at this if I go to my network Tab and we run this search again notice we're making 10 requests for a single word way too much it's just not necessary so here's how we might fix that I'm in my users component and if I scroll down yeah right now we are watching that search input and when it changes we immediately make a request so it sounds like we should say well make a request but don't do it too many times and we can use loadash for this now I believe out of the box in laravel yeah lodash is included as a dependency but otherwise you can install it like this okay so to use it let's start with how about throttle import throttle from low Dash and if we want to grab one specific function we can access it as a file like this okay so now we're going to tweak this just a little bit now we're going to say throttle like so and to start why don't we set it pretty high something like 500 milliseconds okay so with this change we're basically saying at most I want you to trigger this function once every 500 milliseconds or in other words throttle it to once every 500 milliseconds and to make this even more clear let's console.log triggered and we'll have a look in Dev tools okay so let's see go to the console give it a refresh and now notice as I'm typing here I'm not getting 50 or 60 different requests like we would have earlier instead I got one request every 500 milliseconds let's do it one more time I'm going to look for ernestina like we did before all right let's start from scratch ernestina and now it looks like we did three requests instead of what did we have before 10 or 20 that's throttling okay so that's a that's a good Improvement now if we try out D bounce I'm just going to swap this out entirely debounce is similar but just a little bit different debout says no matter how many times you call this function I'm only going to do it once after at least 500 milliseconds has passed so take a look at this give it a refresh and now as I'm typing here notice we're not logging anything to the console because I'm still typing but as soon as I stop after 500 milliseconds then it gets triggered okay so sometimes you want throttle and sometimes you want d-bounds so again just to make this Crystal Clear now when I look for ernestina when we didn't have any throttling or deep bouncing we made dozens of requests right but now when I look for ernestina I typed the whole word and then after 500 milliseconds which was defined here only after that did we make the request okay so in situations like this you have to decide which one do you want do you want to wait for the user to finish typing before you make the request or do you want the results to update while here in the middle of typing that might be useful and really there's no right or wrong answer here I will often just by habit reach or debounce and do something like 300 milliseconds but yeah throttling is a good way to go as well so let's see what this looks like refresh I'm going to look for ernestina again and yeah when I'm done typing I get the results after a third of a second now compare that to throttling and we'll come back and refresh notice now as I type it you're going to see some updates while I'm still finishing that name so it's up to you now the general rule might be if you're very sensitive to your server getting hit too hard maybe you should reach for debounce on the other hand if you can handle it and you want to provide a reasonable amount of instant feedback to the user then you would reach for throttle instead in our case why don't we stick with debounce and this is what we get all right so this is an extremely common pattern that you'll use all over the place whenever you're making requests as the result of what a user is currently typing in you'll just about always reach for one of these functions we can now move on to authentication and you know what I think you're going to like it because it's going to feel just like traditional authentication in laravel or in other words you're not going to deal with tokens or oauth or any of that stuff so I think you'll feel right at home so let's do this at the moment for all of these Pages we've just sort of assumed that we had an authenticated user but now let's make that an actual thing so if we have a look here let's get rid of the comments we have all of this here users create a user host users settings log out really all of that should assume a logged in user so let's say route middleware we want the auth middleware and we're going to group that for all of these routes within it so I'll paste that there and reformat okay so now every single Round Here will inherit the auth middleware which means if I come back and I refresh the settings page we get an issue but it is actually working so it's trying to redirect me to a login route but we haven't yet created that okay let's do that now we can do it right at the top we'll say listen to a get request to log in and that's going to load some new controller called login controller and a create action there but it's not quite enough you'll see right here it's trying to find a route with a name of login so not a URI but a name so let's make sure we give this a name of login so basically What's Happening Here is in laravel's auth middleware on the condition that you are not authenticated it tries to link you to this route with the name of login and up until this point that route didn't exist which is why we saw that error or exception okay so let's switch back and go ahead and create this controller PHP artisan make controller login controller all right but you know what come to think of it why don't we instead place this within an auth directory so we'll add that here and move it over and then of course I just need to update the namespace like so and then re-import that okay so we said we need a method called create and we'll say please log in just to make sure it's working okay only remaining step is to go back to our routes file and officially import it at the top all right cross your fingers come back give it a refresh and sure enough it redirects us to the login page because we're not authenticated all right let's get started now you will see that I'm using a controller here but up until this point we've been using route closures which is perfectly fine especially for small projects but yeah for most of the things I build uh I usually will reach for controllers across the board so maybe at some point near the end of this series I will quickly migrate all of these but yeah from now on I want to start moving toward the way I would personally construct these things okay so let's go into our login controller so let's return and we'll ask inertia to render a view called auth login and get that a reformat all right let's create that now so in my Resources directory I'm going to create a new view component in the auth directory called login okay once again just to make sure it's working okay so come back we give this a refresh and there we go but now notice one thing we see the navigation bar here that really assumes you're already signed in so for example welcome back John Doe and then users and settings we could of course conditionally display those things but really if we're logging in we probably don't need to see any of this at all so it sounds like we don't want to use the layout file in this case and you'll remember many episodes ago we automatically set the layout file just to refresh your memory here's where we require the page component and then here we say well if you didn't already set a layout let's do that for you but yeah it sounds like I want to turn that off so we may have to tweak this just a little bit let's go back to our login View and here I'm going to set layout to null okay so now if I come back to Firefox oh we have an issue here even though I try to effectively disable it it's still being applied and again that's because of the way I set this up here what we have here basically means if layout is null then just set it to this layout component so in our case layout was null so it's set at 2 the default component okay why don't we tweak this just a little bit this is no longer quite right let's say if page.layout is undefined only on that condition do we automatically set it but if it's something else or if it's null then we should leave it alone okay now if we come back and give it a refresh I think that's going to work yeah now we've disabled it entirely but yeah if we were to remove this it should still be applied okay that's what we want okay so let's quickly do a little bit of styling here now to save us time we already created a form and the user create page so I'm going to grab a lot of this here switch back and then we'll just tweak it a little bit but first because I disabled the layout we'll have a main section maybe an intersection here and that section would be like our white card so maybe I could say background is white maybe padding all around make it rounded something like that and we'll do swap these out here and then update this to login you know something like that so let's see what we'll need here when you're logging in you don't need to give us a name but we do need your email and your password and then we'll update this to log n and then finally down here and if we're using the options API we would do props like this or just again to keep it a little consistent we've been using the composition API so I'm going to do script setup here and then let's create our form and you'll remember we need to use inertia's form Helper and that gets imported automatically okay so what do we have here an email and a password that's what you have to give us now a little note here you'll see that I have created two script tags at the time of this recording I don't know of any way to disable the layouts from within script setup that might change when you're watching this but yeah again at the moment the only way to allow for this is to create two script tags which is admittedly a little Annoying just do it or stick with the options API okay so if I switch back to Firefox here's what we get close but no cigar let's go back up and maybe I will put that login heading within the section and then on the form give me just a moment maybe I can put this up here let's see what that looks like switch back and get a little closer but I would like this perfectly centered on the page so on the main section I can use standard flexbox for that Flex or actually you know what why don't we do a grid and we can say Place items to the center and that should achieve the same thing but for that to work we do have to make sure that this main tag takes up the full height so let's say minimum height is screen which basically means at minimum it needs to be a hundred percent of that Windows height all right so if I switch back and give it a refresh yeah now it's centered okay just a couple of things and I think we're ready to go let's give it some margin bottom on the heading and then maybe we could add a border around this section yeah and maybe even increase the rounded to rounded Excel and I think that's good we might want to make these borders more consistent so real quick let's get rid of all of the Border gray and just stick with the default color and then on these inputs and by the way these could also be their own view components so that you don't end up with this duplication and then maybe we could we probably don't want to make them full Excel but maybe a little rounded uh input might be good okay and if we switch back that's good enough for me so think about it how should we submit this form well we already have the event handler set up from the previous page so I'll scroll down and we'll declare this method and what should happen when you submit well we're already using inertia's form helper so can I just say form dot post to login all right let's see what happens there come back give it a refresh I'll try to sign in John I don't think he exists login and yeah now we do get a method not allowed exception we're trying to post the slash login but we haven't yet created that okay that's our next step so right up here let's listen for post to log in and that will hit a store action all right let's do that now store and this is where we log in the user all right so let's do this to save a little bit of time I'm going to go to the laravel docs and if I search for authentication in the security section of course you may know that laravel offers a bunch of starter kits or quick starts and by the way many of those will have inertia options or flavors or adapters that you can use but in our case we're just doing it manually to illustrate the basic process anyways when we manually authenticate a user here's a full example of basically what needs to happen you validate the request you try to sign in the user if that was successful you regenerate their session and then you redirect them where they intended to go otherwise you redirect back with validation errors so let's save ourselves some time and just grab all of that and paste it in here and then I will just update store and import the request and also the auth facade all right saves a little bit of time now this should be fairly easy to understand the only thing that might be confusing is redirect intended so imagine a situation where you try to visit your say settings page but you're not signed in well in that case you were trying to go to settings but it's going to redirect you to the login page well what redirect intended does is after you log in it will remember oh you were trying to go to the settings page so let's just go ahead and send you there that's how that works in our case we probably just want to go to the home page which would be fine but as you can see here the home page is also the default so let's get rid of it entirely and I think that'll get us going so let's go back to Firefox and try to sign in again now I believe a couple episodes ago I set up a Susan account but real quick just to show you that the validation is working if I log in we do see a validation error okay there we go so now we are officially signed in and do note that it redirected us to where we intended to go okay so now we can make a lot of this more Dynamic before we were hard coding a user but we don't have to do that anymore so let's go into our layouts and let's see welcome back username and let's see ah so we were already fetching that dynamically so it sounds like I just need to go to handle inertia requests and let's see uh yeah we must have hard-coded this a number of episodes ago now we can change this to off user name but one little thing this is gonna work so if I come back and refresh sure enough we see welcome back Susan but yeah if Susan is not signed in then auth user will return null and then when we try to access name on null it'll throw an error so what you might do is something like this check to see if we have an authenticated user and if so grab only the information you care about otherwise make auth equal to null and that should do it okay now before I finish up here a common question people will often ask is something like well why are you doing it like this instead of auth user and then the only method to grab only the the fields that you care about so the username or their name or their email or things like that and you can do that it's totally fine just the one thing to be aware of is that now you have a direct one-to-one relationship between your table column names and the properties that you pass to the client and you may not always want those to be the same and actually often you won't want those to be the same so that's why if we switch back to this example here we have a little more control actually in this case notice I happen to be calling it username even though it's pointing to an actual name and that's just a mistake on my part it doesn't really matter but again it's an example of how there might be situations where what you want to use on the client isn't necessarily the name of of the column in the database table so with this approach we are separating the two okay but anyways if I come back to Firefox and give it a refresh this is looking pretty good so now that we are logged in we need a way to log out so let's add a logout link to the navbar all right let's go to layout and let's see looks like we created a nav component settings yeah I'm just going to add another one here and this will log out now this will go to a log out endpoint and it doesn't make sense for this effort to be active so let's get rid of that and have a look refresh and yeah now we have a logout link but when I click on it of course it's going to make a get request to log out and generally it's a better practice to instead submit a post request to log a user out and luckily inertia makes this really easy so real quick notice that on our nav link component that's actually just deferring to inertia's link component so anything I pass to nav link will be sent to the link component okay so inertia's link component includes a method prop we can use to declare the request verb or method so in our case if I wanted to make a post request it's as simple as that which is really cool usually for anything that's not a get request you'd have to create a form or do something weird like that but this should do the trick so now we haven't created that logout endpoint yet but do notice when I click on it we oh actually you know what I think a long time ago we did create a logout yeah okay I'm sorry about that okay just to show you what this would look like so if I open up the network tab we should now see that we're making a post request to log workout okay so let's get rid of this entirely and I'm going to move it all the way up here if you make a post request to slash log out that will hit the login controller and maybe a destroy action but actually this is the only case where you do need to be signed in in order to access this route so I could put it within here but I just want it to be near all of the other login routes so I will manually add this on like so okay so now we'll set up our destroy action and this is very simple we can just say auth log out and then let's redirect you to the login page so I could hard code it or I could say redirect to the route named login or in real life maybe there's a splashed page for your app in which case you'd redirect there okay let's see if it works log out and it works so you see what I mean how authentication in an inertia app is really no different from how you would authenticate in a traditional laravel server side app it's the same basic process which is one of the big wins when you use inertia all of that overhead of tokens and oauth and figuring out how all of that should communicate it goes out the window you don't even need to think about it at all right let's wrap up this series by having a look at authorization so you remember a few episodes ago we added this link to create a new user but now let's say only certain people in the system are authorized to create users and the problem is right now anyone can create a user which you may not want all right let's see what we can do I'm going to return to Route web and if we scroll down here's the user's end point and let's see we'll add a new prop here called can can you create a user create user and right now I'm going to hard code it we'll say false nobody can create a user right now okay so now if I go to my users index component we will accept can which is the authorization and now if I scroll all the way to the top where we add a new user we can now conditionally display this with if can create user all right let's see if it worked come back to Firefox give it a refresh and I no longer see that link cool so now if we go back to the routes file let's make it a little more Dynamic let's check if the authenticated user's email happens to be somebody who's the administrator so I think I'm signed in as Susan and her email is Susan doe.com okay that's a simple bit of authorization so now if I come back and refresh only on the condition that you have that email will you see this link but if it were somebody else maybe John well now I as Susan will not see that link perfect okay but now don't forget that only gets us half of the way there yes we're not displaying the link but we're also not protecting the endpoint which means if I try to access user slash create well we can still create a user even though that's supposed to be unauthorized okay so now if I've switched back okay so how do we do this right now we're sort of hard coding this logic within the route but we also want that logic to be applied to the accessing of the routes itself in these situations I like to reach for a policy like this PHP artisan make me a policy called user policy and that will be a policy for the user model okay so now in the sidebar we will find a new app policies directory right here and there's our first one okay so laravel gives us a bunch of these actions we can use but to keep it simple I'm going to get rid of everything except how about just create okay so now think about it I'm just going to grab all of our logic before paste it in but this time rather than checking the authenticated user we instead receive the current user that laravel passes for us so let's check if the user's email address is John Doe okay great so now we have a policy that we can use in a number of places first up we're going to remove this and check if the authenticated user can create a user and that should do it so now if I switch back and refresh we still don't see it because only John Doe is authorized let's bring that back to Susan and now she should see it okay great so now that we've extracted that authorization logic into its own policy we can now protect this creates endpoint in the same way all right so we come back scroll down to where we create a new user and I'll show you two ways to add this traditionally you would use the middleware method and you would use the can middleware so can we create a new user and because we don't have an existing user record we provide the class path app models user okay so now Susan can access this link but if I bring it back to John she can't and she'll get a 403 unauthorized okay this is what we want so now assuming Susan is not an administrator she doesn't see a link to create a user and even if she tries to access the endpoint to create a user it still won't work but of course if she is authorized then she can't access it and she will see the link that's how we can allow for this okay One Last Thing Before I Let You Go so that handles sort of top level authorization but what about other things for example right now we're just assuming you can edit any user but again maybe it's a little more complex maybe you can only edit certain users maybe users who are on your team or who report to you or or something like that so in those cases if you think about it we have to sort of deal with per record authorization okay well let's just do the same thing this was at the top level but if we also conditionally have authorization for each record then maybe we can add it here and I'm going to do the same thing now you don't have to wrap this within a can array or object if you'd rather just say something like creatable or editable you can do that as well it really doesn't matter just by convention though I learned it this way so it's kind of what I stick to but there's no real right or wrong here okay so can we edit the given user here and I'm going to use the exact same logic auth user can and this time I'll call an action edit the given user okay so let's go back to our policy we had one for create now we're going to do another one for edit now remember this is the current user the second argument would be the user that the current user is editing so I kind of want to call that user as well but I'll just stick with model or other user or whatever you want okay so now in this case it's a demo app I don't really have any actual logic we don't have teams or hierarchy or anything like that so why don't we just make it random for the demo so let's return a random number between 0 and 1 and I'll force that to a Boolean okay but yeah of course in real life remember according to your apps logic you would reach for these users to figure out if one can edit the other okay so now if I come back to our users slash index component let's find that edit link and once again I will conditionally display it if the current user can edit and you can see why I often use that can array or the object I think it just reads a bit better in your view component show this TD if the user can edit and again just to be Crystal Clear we are accessing that here so for each user we're going to add can and then edit all right so let's come back to Firefox give this a refresh and now some of them are turned on and some aren't and in this case because it's Dynamic it'll be different every single time which is a bit weird uh but not a great idea but you get the idea for the demo and let's just do this okay so I think we are about done oh yeah last little thing you can call the canned middleware like this or in the latest version of laraphal you can call a can method directly off of your route declaration so that would change to can create and then you provide the model name here but just a little note if you're working along and that's not working for you you just need to update laravel because it was recently added at the time of this recording so I can just do a quick composer update I like that now if I come back and give this a refresh it will work all right so I think that's about enough to wrap up this introductory series but rest assured if you do want to dig deeper we will be providing a bunch of content at lyricas to help you out so if you want some homework right now we added these edit links but we haven't implemented them so your job would be to add a new endpoint to edit a user and if you want some basic ideas on how you would do that well think about it first you would need to add a new route to edit a user next make sure that you add the necessary authorization then you would of course create a new inertia component so users slash edit where you would add your form and your form can probably snatch a bunch of this logic here to save yourself a little bit of time see if you can get that up and running and if you need any help of course ask questions in the comments below alright thank you everybody I hope you enjoyed it
Info
Channel: Laracasts
Views: 60,460
Rating: undefined out of 5
Keywords: web development, php, programming, laravel, laracasts, jeffrey way, coding, inertia.js, inertia, web app
Id: QyqrYdhSku0
Channel Id: undefined
Length: 172min 2sec (10322 seconds)
Published: Sat Oct 01 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.