- Read Tutorial
- Watch Guide Video
In this guide, we are going to walk through how to create a custom database query scope in our Rails application.
In the last guide, we changed the index
method of posts_controller.rb
to have the following code:
# app/controllers/posts_controller.rb @posts = current_user.posts
Though this code is working, it's a poor practice to customize database queries in the controller. A better way would be to move this functionality to the model which will allow us to manage our scopes from our model file.
Let's start by opening the model file post.rb
and creating a custom scope, like this:
# app/models/post.rb class Post < ActiveRecord::Base enum status: { submitted: 0, approved: 1, rejected: 2 } belongs_to :user validates_presence_of :date, :rationale scope :posts_by, ->(user) { where(user_id: user.id) } end
This scope is more explicit than our code that was in the controller. Our scope is called posts_by
and it runs a database query where it queries all of the posts that have the user id of the user we pass to it.
Now let's go back to the controller file and update the method to read:
# app/controllers/posts_controller.rb def index @posts = Post.posts_by current_user end
I like this implementation because it almost reads like plain language. You can read it as: "@posts store the posts by the current user". This wasn't a huge refactor, however it's important to maintain a pattern of encapsulating any database query scopes inside of the model (since that's where they really belong since they're data related). This implementation will also allow us to have better control of the query in the future. For example, what if we had to pass in a conditional, such as checking if the posts are approved or not? By keeping the logic for this query in the model we will know exactly where we need to go in order to make the code change.
If you test this in the browser, everything should still work properly. And if you run rspec
, the tests should all green too!