Add Comments with Ruby on Rails

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's up welcome back in this beginner series on ruby on rails we are building out a blog application and in today's episode you'll learn all about comments so we're going to talk about two different kinds of comments to start first we have this kind of hacker news style comment that is very threaded so you have a comment and then it can have a reply and that reply can also have its own reply and they can be sort of threaded and nested very deeply and then a separate kind of comment here that we see on the quoting horror blog at the very bottom here i would say these are like sort of flat style comments where we have just a comment and then another comment and another comment and none of them are necessarily replies to previous comments or if they are the the reply is just sort of like a has to reference a previous user um and so what we're going to start with is we will build out this more simple version of comments where we have sort of a flat view of each comment related directly back to a specific post and then later on we can add nested comments so let's jump into the terminal here i'm going to generate a new model so i'm going to say rails g model comment and when we're thinking about a comment this has a couple of different things the first thing that we're going to need for a comment is some field in the database to store the text that was typed in for the comments so we're going to call that content and that's going to be a text field because we want that to be sort of a blob of text so that someone could write a lot it doesn't necessarily need to need to be limited to the size of a string or whatever the next thing is a comment has to be about a specific post usually all right so we're going to say this is going to reference a post or we're going to build a a relationship between a comment and a post so when we're thinking about the way that we're going to relate to different models we want to think about sort of the one to many or the many to many relationship here and then we also need to think about where that foreign key belongs right and so in this case when we're thinking about a post having many comments or a comment having many posts just saying it out loud kind of gives you a little bit of intuition about where that foreign key might be and so in this case what we're doing is we're going to have it set up so that the foreign key is in the comment model and the reason we want to do that is because if we stored the data about which comments were on a post we would need some sort of like array or something in the database that had all of the different ids for the comments for that post so instead of having this like array data structure inside of the database what we're going to do is we're going to rely on the column in the database for the comment for each comment having reference back to which post that comment is for and that will create a relationship where it's we can say that the comment belongs to a post and a post has many comments now similarly any user who wants to comment on a blog post can do so and we want to be able to tie that comment back to the author of the comment so here what we might say is user references we could say author and set up our foreign key a little differently but i'm just going to keep it simple and say user and so we're also going to have a user has many comments again generally if you think about that it doesn't really make sense for a comment to have many users necessarily all right so let's open up this this migration here and take a look so we've got our content we should probably make this null false uh so that folks can't just submit blank comments or empty comments and then we're creating relationships between the user and the post those will both have foreign keys inside of the comments table that point back to the post table and to the users table and we'll just run this regression rails db migrate the next thing that we want to do is figure out how we want to actually display these comments and so if we call if we recall um actually let's fire this thing up so we'll jump into our blog and say rails s and we can head back to the browser here and go to localhost 3000. uh in the previous episode we scoped all of the posts to a specific current user so for now i go to uh let's see posts should show me an index of all the posts so if i create a new post here this is like testing with some comments comment on my post and then we say create a post so now we have a post this is the show for the post so this is what we have for our show page which really demonstrates or doesn't demonstrate anything it just like shows what the the blog post is right we have our title and we have like the content of the post and what is most common is if you're looking at this blog for instance at the very top you'll have your title then you'll have all of your content and then generally at the end you'll have some list of comments right and so here we see how many comments there are and then all of the comment content below that so let's go edit this show page number one so that um all users can see all posts for the show page they can't edit them but they can see them and number two so that we can see the comments for the post so let's go over to our show page here for the post show so this is the the html erb for the route that we were viewing here okay so right after that description let's add another paragraph tag where we're gonna have our comments and this is gonna have the same sort of pattern as uh when we were rendering out the posts description now what we could do is we could write all the html and everything for the comments directly right here but what we want to do instead is we're going to make use of something called a partial a partial is just kind of like another sub component of the view that we're going to write in a separate html.erb file so here we're going to say render at post.comments and by saying render this is actually like a one of the sort of magical conventional things about rails is we can call render and pass it a single instance of a active record model or we can pass it an active record collection and under the hood render knows how to find the view that is related to the types of those of those objects that are being passed in so in this case because we're passing in comments it's going to look for views for rendering comments and at this time we don't actually have any comments views there's no there's no comments controller and there's no comments views so let's go create a comments controller so we say rails g controller comments by creating the controller this way we will get a controller class we'll also get this views directory and this is really where we want to uh what we want to edit here so we'll open this up actually let's just open everything up here and go to app views comments and we're going to add underscore comment.html.erb now the name of this file is actually pretty important so it has to start with an underscore that's the convention when we're working with partials unless we want to like override it and pass in the name of the partial and then we it has to be in this comments directory and it has to you know have the html.erb sort of extension here so if we go back to our posts show and here at the bottom when we call render posts for comments it's going to go through each of the comments that are returned from this active record collection which actually we haven't defined the association yet either and that will call this comment or that will like render this comment view so for now let's just say like uh comment show or comment partial okay so now if we come back over here and we click refresh it should fail because this comments method doesn't exist on the post so we'll go into our um post model we need to add another relationship to the post model so we created a new comment model that's here comment and this comment model belongs to a post and belongs to a user and when something when we have a belongs to relationship we can go to the other side of the association so we can go up here to post and we can say has many comments similarly we can go to the user model and say has many comments because a comment belongs to a user so now if we were to refresh the page here we see this new comments thing and there's no there's no actual comments here yet so let's actually jump into um rails console and we'll create a comment so from by running rails c we can open up the rails console that allows us to sort of mess around with our data so here we can say comment.create let's see post is going to be our post id is going to be 11 user id or user maybe is just like user.last and then content is going to be our test comment okay so this should create a comment and it's stored in the database with id one the post that it's related to is post number 11. we know that because in the url here this says number 11. and then down here we see user id 9. i think user id 9 is this uh this new user that i'm logged in as we'll see so if we refresh the page here we should see okay so now we see that we're rendering out our comment partial if we were to create another one here so test comment number two and refresh the page now we see comment partial is being rendered twice that's because we're going to see that partial rendered for every single instance of the comments that are returned for this blog post so again if we said if we were to say like post.find 11 and this is our our current post right so we're back in the rails console here we have an instance of a post so because we wrote that association we can call post doc comments and that gives us back this active record collection with all the comments in it and the first one says test comment the second one says test comment number two okay so we've got our association set up we've got a couple comments here what we want to do now is sort of build out maybe an article um i tried to look up like i would try to look up the semantic html for the different things that you're building out and inside this article we're going to have a p tag that has the content for the comment comment.content and if we refresh the page now now we actually see the real content for those comments that were being created so we see the test comment and test comment number two that's kind of cool the other thing we might do is just put like a horizontal reference line in between each of them so we've got some sort of line uh not pretty but it's common to see this sort of uh border situation maybe we want to do it with css or something different but i think that's fine for now now another thing that we might want to do is include the author so if we look back at these posts right we see carlos p matthew m coding horror etc and then even on hacker news right we see sort of the username and then when they commented right next to the comment so let's add the at least the author down here at the bottom so i'm going to use a small tag here and i'm going to say this was by comment.user so that's going to be the user model because a comment recall that a comment belongs to a user and right now i think we just have an email address for a user and so we'll just tack that at the end there so if we jumped i think yeah if we jumped back into user we would see that they have an email so if we refresh the page now now we see not only the comment but also who the comment was left by so let's actually log out and let's log back in i think so i've got another i'm logged back in as another user here and if i wanted to go to posts 11 here and view the show page this is failing because in a previous episode we scoped all of our posts controller actions to the current user but we do want to allow other users to see posts so let's go up update our posts controller so that in the show action we're just allowing access to anyone's we're just going to say post.all and this set post thing will not need to happen on the show action it only needs to happen on it only needs to happen here so we'll refresh the page and now we have access to posts undefined method title for active record relation ah this should not be posted all should be fine params id okay all right so all right great so now we're able to see posts that were made by other users so we're logged in as cgive dev but the the original post was written by cjville gmail.com and so that's cool that we can see those other comments we can see the post but if we wanted to make our own post our own comment we don't have access to the rails console so how do we go about doing that i think it makes sense to add a form right maybe at the bottom of our comments section where we can add an input that will allow users to enter their own comments so back in the post show again we technically could render like we could write all of the html right here for our form to accept a comment uh directly right here but instead what we can do is we can again use a partial that is the form for a comment so here we can say render i think it's render partial and then we're going to say comments form and we'll come back here in just a second so then in our comments we're going to add a underscore form.html.erb and this is going to be our form now for before i run away too fast like these look different right so the first one we're able to use render and then just pass in this active record collection with the underlying objects when we say render partial we need to specify like the actual sort of view slug or i don't know what this is actually called just kind of like what is the controller's name slash and then the partial name um and that's because if we just said render like this if we just said render form it's going to try to look at the posts form not the comments form and because we're in the posts show page we need to tell it how to go like sort of up and over to a sibling directory a view for a sibling so we're going to say render partial we'll go back into our form here for comments and we're going to add a new form and the controller action here we want to send a post request to a specific route now let's talk about this route for a second so like technically we could do slash comments right we could create our comments controller to receive a post request at the top level and we would expect that we are being passed the id of the post and perhaps the content and then we can look up the current user by who's logged in an alternative is to nest the route underneath posts so let's talk about that for a second so if we go to routes here right now we have resources posts and um okay so resources posts gives us all of these top these top seven routes for all these posts things but what we can do is we can actually nest it so that instead of having a top level slash comments we can have slash post slash id slash comments so we're gonna nest that resource inside and how the way that we do that with routes is we say um we pass a block to the resources method so resources here is just a method this is called a domain specific language the routing stuff that we have in rails but this resources is a method that could be defined to accept several different arguments one of which again is a block and inside of that block we can sort of recursively continue using this domain specific language to define the routes so here we can say resources comments and in this case we really only want create at least to start and by doing this let's take a look at what happens with our routes so if we refresh the page here you'll notice that we have this new post comments path that has post slash post id slash comments and this is going to give us well this is only accepts post requests so it will receive a post request to this route and it will create we expect by convention it will create a comment that is related to this specific post so that's kind of the convention when we're using these nested routes all right so let's head back over here actually we'll copy this comments path because this is what we want to use as our action is our post comment path now when we're using this post comments path if it has a dynamic segment or something here that starts with a colon as part of the path that's not optional so this one's optional because it has parens because this is not optional we have to pass something as an argument into this method and the argument should be a post and the method will know to pull off its id so here we're going to say post now we have to go through one more concept and that is when we're rendering a partial in order for the partial to know which post we're talking about when we're passing it in there we generally will pass another argument here called loca locals and this is going to be the local variables that we want to be defined inside of that partial when it's running so we can say post is at post you could also technically use the instance variable at post here that's not a it's not a good convention you would it's better to like explicitly pass in um these objects as as local variables to your partials so that you can render these partials in a bunch of different places so we've got a post that's coming in this is going to be able to create a new post or new comment that's related to this post we also need to have some sort of like text area to accept the content and the name for this text area is going to be comment content so at the top level params that we're passing in we're going to have a hash called comment and inside of that hash we will have a value or a key content that points at a value and that is like the content that was typed in and we're going to make this maybe 6 rows and i don't know 20 columns comment comment text boxes are usually pretty small so that people don't get mouthy uh posts slash 11 here all right so undefined method render partial did i spell that wrong is it not render is it render partial like this let's see render partial there we go sorry so this is render again and then it's not underscore partial it's it's a it is a method that accepts a hash and if you pass it partial with the key or the yeah like the the path to the partial it should work all right so we see the form here if we right click and we inspect we'll see that our path or our action for our form is indeed post slash 11 slash comments so we're going to send a post request we're passing along our authenticity token to prevent csrf attacks we're going to pass on any content that we type in here this is my content and we are going to send this as a post request to this route now this route is going to map to um the uh controller the comments controller i thought we would see it here oh here it is so if we scroll over to the right we can see that this is going to the comments controllers create action which we haven't defined yet so let's go do that also so comments controller create action and inside of here what we want to do is we want to like uh you know create a new comment for the post from the current user so how can we do this well in previous episodes we talked about creating a comment or creating an object by saying at comment equals current user.comments.new this is called again building through association so we're building through this comments association here and again we're going to have our private comment params and our we're going to pull we're going to require the comment to be at the top level because again we have this top level hash here comment and then we want to permit the content okay so then we're going to permit this content to be passed when we're initializing this new instance of a comment through this comments association now at this point the comment will know its content that was passed in from the front end and it will know the id of the current user so it will be able to set the user id and it'll be able to set the content however we have not actually set the post anywhere and so it turns out that the post id again if we call back if we go back to this this view here recall that like this thing that starts with a colon will be available in params at params colon post id so the value that's being passed in here again is going to be the id of the post that's number 11. we want to merge the params that we're passing in here so that it contains the post id params post id okay so this is this is a long chain here but this is what we're doing so the first these first three lines here are just pulling out the content and permitting it so that it's allowed the last line is explicitly merging that post id from post params at the top level so that when we create our new comment we're passing in the content and the post id and because we're building through association we're going to have the user id and that's all that we need for creating our comment so now here at the top we can say if we don't save so then we want to say flash notice is going to be the comment.errors.full messages.2 sentence i believe and if we are able to to successfully save the comment then we want to redirect to the post path for params post id we could also just say redirect to i think at comment.post but this one is a little safer yeah if for some reason we don't have a post this is this is fine uh okay so let's go back so let's go back over here we're gonna refresh the page and we're gonna test this out so we're gonna say you know this is another test comment and hit submit all right look at that it's already showing up here that's fantastic so if we close that testing testing testing we've got a bunch of comments here and they're all showing up now a couple things they're showing up at the bottom so chronologically we're seeing the oldest comment at the top so let's go back to our comment or our post show here and what we could do is say we want these to be ordered in reverse and we can say order id descending we'll come back over here refresh our page and now the newest comments should be at the top is this the newest and that is at the top so now we're seeing them in reverse chronological order this is super cool but we have a small problem we have a performance issue so if there were hundreds and hundreds of comments we've got we've got a couple of performance issues here one of them is called an n plus one query the other one is that we're not actually caching the current user so every time we see current user being called inside of our controller action there's a couple of them here we're firing off requests to to query the current user and so there's a couple different performance issues here so we're going to clean those up in the next episode thanks so much for watching and we'll see in the next one [Music] you
Info
Channel: CJ Avilla
Views: 612
Rating: undefined out of 5
Keywords: cjav_dev, web development tutorials, web development for beginners, vim, ruby, rails, javascript, ruby on rails, ruby on rails tutorial, comments, blog comments, flat comments, comments without threads, comments without replies
Id: TcltF0KVyrE
Channel Id: undefined
Length: 25min 33sec (1533 seconds)
Published: Wed Aug 25 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.