Session Based Shopping Cart on Rails

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
okay so this is the app that we're gonna be starting with I sent out some instructions for if you wanted to cut along to clone it before class you could try if you didn't do it before class you could try quickly getting it up and running based on those emailed instructions right now it's sitting here as a repo up on github and yeah after I'll do the work that I'm gonna do right now within a few branches and then I'll push those branches up to the repo so you can see the work there and I'm starting in the the master branch and what the master branch has we can just quickly review this app and sort of what it looks like it's a very simple application if we look at the schema we have some active storage stuff or image storage and really just one table for products and a product has a name product has a description and a product has its price in as an integer so price in whole number of cents that's how when we bring in some credit card processing through stripe stripe is expecting to see your price in whole number of cents because there's no such thing in in sort of commercial math as fractional cents if you're doing like FinTech kind of stuff you might have to deal with fractional cents but in sort of e-commerce kind of places you don't you don't go down to a fractional cents so we're gonna be using whole number integers if your application doesn't store its prices and whole number integers you can just convert it to that when you send the price to just stripe you can just multiply by a 100 and then turn it into an integer okay so that's the that's the look of the schema along with that we have a single product model it's got a few validations on it it's got an image attachment ad by way of active storage this was just me playing around with something that that image URL is not important and then currently it has a single controller that just has an index just opening up all the products into an instance variable and if we go to the Associated view we can see that we are looping through those products displaying the name if an image is present displaying the image displaying the price and here we have to convert it back into something that looks more like humans are going to expect a price to look like so I take my full number of cents divided by 100 I divided by 100 point zero because I wanted to become a float and that's just like a little Ruby trick because if I divided it just straight up by 100 it would stay in integer math and it would round away to cents so I take my integer number divided by floating point 100 and then I passed it to group rails is number two currency helper puts a dollar sign on the front of it and it pads any zeros that might need to be padded at the end of the number so if the number was like four dollars and fifty cents if we didn't use number two currency it would display us for point five but we're not used to seeing four dollars and fifty cents represented as four point five so number two currency puts in that extra padded zero for us and all of this data was seeded into the application by way of faker but I also used the unsplash website which is just a source of really nice free to use photography and they have a simple API that you can pass a search query to so for each of the faked products I requested to download that product and then I handed it off to active storage and active storage like slurp that in saved it to my my system and that got me the images into my into my application so that's what we're that's what we're working with that that's what gives us this what I would like to do is I would like there to be a link or a button for each one of these things so that I could Add to Cart I'm going to build a simplistic cart one that does not concern itself with quantities so it's simply going to be the products in the cart or it's not in the cart and it's gonna be up to you if you want to implement the card on your project to add the ability to deal with quantities if you if you google around or if you go on YouTube and you find like rails shopping cart tutorials they are probably more effort and you need to put in compared to what you need to do to get the baseline marks for the project so by all means if you want to do a really fancy shopping cart you can follow a tutorial that you find online mine is going to be simplified and that I'm not using the database at all my carts entirely in session and so the deficiency there is if this was a real store the users cart would not follow them devices or anything like that it'll just simply be stored attached to a cookie in their in their browser so it's not the ideal cart but it's sort of a simplified version of a cart that will get us the marks from the project and so we're gonna work with session once I get that worked in we'll talk about credit card processing but we'll start off just working with sessions so I'm gonna go to my command line and I'm gonna create a branch called session cart so I'm gonna get checkout - be for give me a new branch lets me and do it a new branch and as I said I want a little little button there but I'm gonna start with the what I consider to be like the hello world of using session which is just like a visit counter so that will give us an idea of how session works in rails and show that we can attach some information to a particular user by way of their browser by way of a cookie because that's really what session is tied to in in rails it'll work in a similar way to what you're used to when you work to a session back in PHP but there are some slight differences and I'll go over those when we hit them so what I would like to do is I'm going to bring up the the one view that we have and at the top of that view I'm just gonna say you have visited this site and times but I want that end to actually be a number and I want that number to go up over time and so what I would really like it to be would be to like echo in some number I also would like to properly PluralEyes the word time depending on what that number is so I use rails is PluralEyes helper I'm going to pluralize some kind of visit count that variable does not yet exist and I'm gonna PluralEyes the word time so that if visit count is one it'll say one time if it's two were greater it'll say two times three times that kind of thing so if I went to to go display this it wouldn't work yet I need to go into the Associated controller so I'll type like on broad ducks there's the Associated controller and I'm gonna set up a a visit account initially just hard-coding it to the number one so we've got visit count in our controller and then over here we are displaying that visit count and pluralizing with time you have visited the site one time so the pluralization works nothing else is working yet right I could refresh this forever nothing would change session in rails is just a hash called session I don't have to initialize it in any way so if you remember back to PHP we had to sort of start session up and when you started session up it would inject a cookie uniquely identifying you in your browser and then the session data in PHP would actually be saved on the server and a flat file the way it works in rails is it still makes use of cookies but it actually stores the session data in the cookie itself so PHP just set stored like an identifier a unique identifier in the cookie with rails it stores the data itself and the cookie it makes that data tamper proof so you can't just open the cookie and alter the data because it does some cryptographic signing of that data so that if you tried to alter it rails would be able to detect that that alteration so I'm gonna have session count or session apposition visit count and the very first time someone visits the site we have to initialize visit count and then after that we want to increment it and so there is a nice operator in both Ruby and JavaScript which is the or equals operator and the or equals operator will assign a variable or in this case a a hash value it will assign it only if it doesn't already have a value and so the very first time our users come to our site visits account is going to be set to 0 and after that it won't it won't be affected by this line and then I'm going to grab that visit count and increment it by one so I'm going to say like session this account plus equals one we don't have plus plus and ruby's we just use a plus equals 1 and then the visit count that I'm passing to the view will just be session so here initialize the visit count first visit and that should be enough for our hello world of session so I should be able to go over to my browser now reload the page it should still say one visit the first time and then with each reload that number should go up and up and up so there's the one there's two three or notice that cookies have this property that they're shared across the browser so if I opened up a separate tab you know I could go five six seven eight and I could go back over here and should go right up to from four to nine and it does if I open up like a private window so privacy mode in browsers have a separate cookie store a disposable cookie store and so this does not affect the other browser so I can go up here to reload it a bunch of times to like twelve here and that has no effect on this this should just go straight to ten so these are two independent two separate independent tabs within a privacy browser do share cookies and then if we went into a completely separate browser well that would have its own separate cookie store as well if we want to look at this cookie we can do so I can hit ctrl shift I to bring up my dev tools I'm in Firefox but there's something similar in Chrome I don't exactly remember where to find it but in Firefox I go to my storage tab and there is a cookies section in there and I can see for localhost so a cookies always identified with the domain that set the cookie and then the cookie gets a name and you can see this is though this is the one that we're working with right here it's the name that my application was given what I do rails new and so I did rails new and I said straight it up and so its underscore stripe it up underscore session and so if I deleted all of these cookies and I went back here I should be back at one back at one and then the cookie comes these cookies come back into action notice the cookie itself is this long sort of unreadable thing so it's gonna be rails that's gonna deal with sort of being able to decode that that's the sort of the cryptographically signed value so like the number one is stored in there somewhere doesn't really matter where rails is gonna handle that for us rails cookies are also set to HTTP only which means they're not gonna be shared with JavaScript on the page and that's a security thing you don't want JavaScript necessarily to be able to read cookies that are set by the server because javascript could potentially be injected into the page through a cross-site scripting attack and you would then not want that cross-site ripping attack to be able to get access to your cookies because of what's called cookie side jacking right if someone has access to your cookies they could potentially use those to impersonate you on a site because cookies are usually used as a an authorization or sort of an authentication mechanism once you've given a username and password often a cookie is set on your on your site the other thing you want to make sure of if you are using cookies for that kind of thing for you know authentication sessions is that you are deployed on HTTPS right you want to have encryption enabled on your connection so that the cookies are not flying across the internet in plain text again so that they're not vulnerable to cyber attacks okay any it's fairly simple but any initial questions about session in the context of rails and its use of cookies the one other caveat that just came to mind is that there is a maximum size for what can be stored in a browser cookie and rails you can just store like anything in in session right you can you can store like a raise of objects like a raise of rails objects in session and you could eventually hit that maximum size and you get an error and so just be cognizant about you want to keep what you're starting the session fairly small rails can also be configured to store just like PHP just a unique identifier in the cookie and you can have the cookie the actual session data stored even in a database server side if you've needed a lot of data stored in that but usually you don't need a lot okay so I'm gonna refactor my code a little in anticipation of my decart functionality and also to make this a little bit more flexible in terms of this visit count by now it only works for a single action on the page like what if we had multiple actions so or multiple views I guess it would be so if I go to my products and I add a file here I just call it a boat HTML DRB so that's in my views products folder and then I go into my routing file and I create like a get request to the about should go to the products of boat Oh what I'm going to do is I'm gonna take that message you have visited sort of however many times and I'm gonna put it outside of a single view into the layout so that it should appear across the entire site just like you would want like a shopping cart to appear across your entire site you don't want it just to be limited to one of your actions so I'm gonna open up the layout here the application layout and I'm gonna move this message here into the layout above the yield and the yield is where the view is injected so it should still work over here but if I go to slash about it always just says zero and so the reason for that is that the visit count instance variable is currently only available for my index now I could sort of naively go and create separate abode action and like take these three lines and paste them into here and be like done all right now the functionality should exist across the various pages right I can go back to the home page but that's that's a little bit lame right we've got some repetition that we don't necessarily need in our application and if we started to grow the number of pages on our site we have to repeat that over and over and over so instead what I'm gonna do is I'm gonna create I'm not gonna copy it again I'm gonna create a private area inside of my class so inside of controllers all of our public methods are routable things that we can route to with the routing system private methods aren't there just little helper methods and what I want to do here is I'm going to create a initialize session method and I'm going to create a increment visit count method and in to initialize session and to put that line and into increment is account I'm gonna put that line or those lines so same code just redistribute it across to helper methods and then I'm going to remove that code from the actual actions currently it won't be executed now at all so my I've sort of got my stuff in a broken state at this point in time so if I save this code and I went back to my application doesn't matter what page rot it's always saying zero rails has this thing called before filters and it's a way of hooking into the the lifecycle of a controller and having the ability to hook in things like oh I would like to run initialize session and increment visit count before any of my actions that would be nice so I say at the top here before action initialize session and before action increment visit count and now before any one of my actions in this particular controller the session will be potentially initialized if it hasn't been already and the visit count will go up and then an instance variable will be created in every one of those actions called at visit count if I wanted this to work across an entire application that had multiple controllers that's the time that we would bring the application controller into play because that's the place like all controllers inherit from application controller which in turn inherit from action controller base so this is a place where we can put something that will be inherited across all of our controllers so I won't I won't go ahead and do that but just know that that's a possibility okay so that's that's the end of this this busy count I'm also going to eventually be adding to this to do my add to cart and so I only want this visit count to increment when we're visiting the index of the about page so that's another modification that we can perform for these lifecycle filters is that like okay we always want the session to be initialized everywhere but we only want the visit count to be executed here and here okay now let's make a simple shopping cart I don't have a partial here I'm just working directly inside of a loop in this index at HTML DRB and below the product description in this view I'm going to put a paragraph tag and a linked to I call it add to cart' and currently I'll just make it go nowhere we could do this in multiple ways linked to my default is gonna build like your standard a href kind of link this functionality though has an effect on the state of the page right it's gonna change session in some way anytime you were changing session in some way or the backend state of your server in some way like your database that should not be done by way of a get request get requests are supposed to be cacheable there's supposed to be item potent meaning you can run them over and over again and not have them have a cumulative effect so we don't want to rely just on a regular old link for that to happen we would need another verb so we're gonna use the post it's okay for a post at HTTP post to affect the state of the application or the state of the server it's not okay for a get to do that some I don't know if browsers still do this but for a while chrome was actually trying to predict which links the user would click on next and load them up behind the scenes and so chrome was actually like going through and sort of like crawling sites that you were on anticipating that you were gonna go to a next page and so that when you click the link it was already pre-loaded but it would only do things that were get requests cuz it assumed that they were item potent and cacheable but if you had something like an Add to Cart functionality that was a get request chrome could be going around and clicking and adding a whole bunch of stuff to cart for you so I don't think it still has that functionality but still there's all sorts of caching proxies that exist on the Internet and even like things like Googlebot visit you know so actively crawl a get request you just don't want it any kind of action permanent action to occur on a get request so so what does that leave us with that means that we're gonna create some routes and one of them is going to be a a post route I'm going to just so that we can be very clear about it I'm gonna manually make these routes I'm not gonna use like a resources kind of command I'm just gonna manually make the routes I'm gonna say like a a post request to slash yeah Add to Cart or maybe even products slash Add to Cart some ID should go to the products Add to Cart auction which hasn't been created yet and I want that to be known as products Add to Cart so that when I referenced it as a path for something for a forum or a link but I could say like products Add to Cart path or I could even simplify that and just call it add to cart path and then I also want the ability to remove something from the cart so I want to be able to go to products / remove from cart also with an ID I want that to go to the products move from cart so that's going to use an HTTP delete also going to provide an ID which products should be removed from the cart and then I'm gonna say as remove from cart that needs to be in quotes both of those things okay we're only gonna work with this one initially and then we'll bring in the delete functionality and so I'm gonna do that back over here so where do I want this link to go to you I want it to go to the add to cart path that is coming from this name right here the fact that this is called Add to Cart means that I get and add to cart path to reference this route so the link to is going to add to cart path the route needs an ID so I need to provide the path with an ID or a thing that has an ID and I have a thing that has an ID I have the current product by default link to does get requests I want a post request so I have to say that the method is post so now on clicking this link the browser should by way of JavaScript so rails rails will change this link to use JavaScript to make a post request to the add to cart path and providing the ID in the URL so this is the product or the ID of the product that we want to add to the cart that means over here in my controller I need an add to cart method and I also need to add to this initialize session because I want to have a cart in my session a shopping cart so session a position cart I'm gonna use an or equals just like I did before because the very first time the user visits they won't have anything in that position and we're just gonna make it an empty array and so that when we get to this add to cart we're gonna say session opposition cart and we're gonna add to it we gonna add to it grams a position ID so the ID coming to us by way of the routes of this ID right here that ends up in the params hash so I asked for it and I'm shoving it into the array with the shoving operator all right looks like two hands shoving something into the array and so after that happens I just want to redirect back to my root path and that gets around the problem of like usually what happens when rails gets to the end of an action is it tries to load up a view with the same name right it would go to the products views products folder and look for an add to cart view but we don't need an add to cart view we don't need a special view we just want to add that product to the cart and so we're just redirecting the user right back to where they were which was the root path you could change where they were depending on where they actually were I think there's even some capability of doing this I can't remember if that's the actual thing but it just takes them back to where they were there's something to do something similar to that all right it's been a while since I tested anything I've lost confidence that anything that I've done here is even going to work so let's go test this out I'm gonna go here inside of my index view and I just want to get an idea if this cart is working at all I'm gonna use the debug command to debug my session cart so I'm just doing like an echo with the less than percent equals debug is just like print R in PHP it's going to print out like a human readable version of the thing that you give it and then the thing that I'm giving it is the session card so let's go see if it's everything I did or nothing I did works so if I visit reload this page start off with an empty cart just like a set of square braces that looks okay for each one of these products if I hover over the product I can see down below it's going to slash products Add to Cart three for that one and for this one it's going to like products Add to Cart for and this one products to card five so it looks like it's building the URLs correctly and so I'm gonna try clicking on one of these so this is product number three so ID three should then end up inside of my my card here and it does and I can go to other things and I can add them and they go in there as well there's a few problems one they're being added as strings you can see that it has cool surround it so that that's problematic as their IDs which are supposed to be numeric so I'm gonna need to convert them into strings the other thing that's problematic is that when I click Add to Cart multiple times for the same product it's adding that product multiple times to my cart so I have to fix a few things here I'm gonna do it in the controller one I'm going to create an ID variable that is this params so I'm gonna take this params here delete it and paste it here and then I'm going to chew I it to convert it from a string into an integer that'll parse the string doing it and then I'm going to add that ID to the cart unless the session cart includes the ID and just as a little hat to clear my card out I'm gonna load the page up once where I remove the or in front of the or equals so just a little way of clearing out the cart so I'm going to reload the page once clear my card out put the ores back in and now when I click Add to Cart we should be able to convert the ID that's coming from the routing system into an integer save it as ID add that ID to the array unless the ID is already in the array and in all cases redirect back to the root so let's see if that functions clicking Add to Cart here I get the number three in there click it again doesn't go in there click another one it does you can access we've seen here that you can access the session directly inside of use I don't tend to like to do that I like to turn everything that I'm going to interact with inside of a view into an instance variable so inside of here I'm also going to create a load cart method and it's simply gonna create a cart from the session not really a requirement and I'm gonna add that up at the top to my actions here so before action the reason I wanted the cart loaded into an instance variable is so that inside of my view here like maybe I only display Add to Cart if it's not already in my cart and so I could have something like if cart dot include question mark Product ID Oh better yet not even let's do this let's make this add to cart even better instead of just loading the session up let's actually load up all the products from the from the model so let's do something like product dot find when you give find a single number it gives you back a single product if you give find an array of numbers it gives you back an array of objects of that type and so now my cart will include all of the products that are loaded up in that cart and so over here I can just say like is this product included in the cart if it's already included in the cart this would say like product is in your cart else I can have it let me test that out product is in your cart Prada goes in your cart my economic iron knife is not in my cart yet so I will click it and now it is in my cart okay the last thing I would like to do in terms of cart is maybe display the cart somewhere on the page and also um what I guess remove items from said cart so I can get rid of this this debugging here I want to display oh yeah there's that cool HTML you know does nothing details we use the details element it's a really cool thing the details element does like a native HTML accordion meaning like it'll like reveal information and hide it based on a click without needing any JavaScript or anything like that so we can put our cart inside of that so here I'm gonna put details as it details yeah details and then summary is what you want the clickable thing to be so let's call it shopping cart and then below it I will put the the cart itself inside of an unordered list and I will say at cart dot each do product and and then Li product dot name I'll come back to my corner I just want to make sure that this is gonna do a the thing that I'm thinking it might do there's the shopping cart I can click it and it reveals the car is not a nifty HTML element I just found out about that one and I was so excited yeah no JavaScript needed what a lovely life and then here I could have like removed from cart functionality as well right so that's maybe beside each one of these things I would say remove remove from cart I'll go back and show you that code though there it is so there's the detail the summary at the top and then the the high table stuff inside of it anyone need to see any of my code across either the views or the controllers or anything you're good let's go and implement remove from card functionality dad I could even have that here as well right so if the products in the cart I could have a remove to cart I'm gonna implement it in a slightly different way I'm going to use a button just to show that there's two ways of doing this I could have you also used a button for the Add to Cart functionality as well rails has link to for building links and those links can be augmented to do different kinds of methods what button to does is it builds like a little form for you and you can have hidden data associated with that form and you can also have that form submit using different methods and so I'm going to use button two here and I'm gonna say remove from cart and I I built a route for it already it's my remove from cart route and if that's going to go to my yet to be created removed from cart action and it needs an ID so I say move from cart the method needs to be delete delete and I've got some grams specifically I've got oh yeah how do I do this is it like so like that we'll try that out and see button to the text on the button the this is Add to Cart path the method and then the params that I want and I gotta close that up it's different because of its button to so when you use button to you you use params and it's gonna actually in my form that it's gonna make for me it's gonna put a hidden input in the form that has that ID stashed away in it I think let's see if I remember how to use button - oh no wrote matches remove from cart missing keys oh ok so maybe I do if they do it the same way it might need this and then I should be able to get rid of this paramus then yeah so if I wanted to use the parameter route without the ID in it and passed it by way of a parameter in this case I'm just passing it by way of the URL go back over here there you are so the things that have been added to the cart have a removed cart from cart button if we view source on that we'll see that it's a form that's creating that the form is going to those particular products remove from cart four or three it's got some hidden stuff inside of it to say that this should really be like a delete method and it's got a submit button that has our value in it so now I need to actually go ahead and implement in the controller the thing that's going to deal with that I'm also going to take this thing right here and I will put it into up into my cart as well so like this whole thing and be copied and pasted up here I think that should still work so inside of my shopping cart yeah in the shopping cart the button two looks ugly let's make it a link inside of there yeah put a dash there you go so it's it's a button down below but here in the shopping cart it's just a link that says from move from cart it's because I changed from button to the link to when I put it up there everything else is the same it still got the same path passing in the product and the same method override and now I need a remove from cart action inside of my controller and here I'm just going to go to my session cart and I'm going to delete the Rams now I'm gonna go do what I did up here grab it as an ID and turn it into an integer and chem remember if delete is destructive I'm thinking it would need an exclamation mark to be destructive if it'll actually affect my cart we'll see in a moment if that's gonna if I'm gonna need to like put an exclamation mark or save it over the existing cart regardless I need to still redirect to the route path after deleting it let me test this out trying to remove the rustic bronze chair click and it's gone and then the ergonomic knife and it's gone or that and then my shopping cart has nothing in it it should probably should probably say as much right so there should maybe be some like if art dot empty no items in cart else and back over here shopping cart is empty there's no items in it and when I add an item to it I've got a knight a minute there we are anyone need to see any aspect of my code at this point yeah the controller yep same for you or the actual item list what do you mean by that yeah it should it should remove from the list be good or oh the before actions there you are so I've got three of them right now and what what are you having an issue [Music] Oh like from here okay let me see cuz I don't know if I've tested that so this one's not working properly let's see it seems to be for me um what do you need to see do you have the same okay let's see the index here the button to looks like this so it's got to remove the cart path we're passing in the product the method is being override with the symbol delete yeah oh yeah but before save is a similar thing but it's part of the model lifecycle methods so you can like hook things into your model to trigger before the model saves so it's the same idea giving you the ability to like inject something into the rails lifecycle as a hook but it operates at the model level whereas before action operates at the controller level okay let's let's take a break where we're done the Add to Cart functionality will take like ten minute break and then we'll return and we'll talk a little bit about credit card processing with the stripe API so
Info
Channel: Kyle Geske
Views: 4,579
Rating: undefined out of 5
Keywords: Rails, Ruby, Ruby on Rails, RubyOnRails, Session, Cart, Shopping Cart
Id: CKskGYQEst0
Channel Id: undefined
Length: 52min 20sec (3140 seconds)
Published: Wed Nov 20 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.