Controllers and Routing


In this guide you will learn about controllers and how they fit into the Rails framework. We will cover very basic concepts, and further explanations will be linked in specific guides linked below, or can be found in Rails's official Action Controller guide here.

What is a Controller?

A controller mediates the communication between views and models in order to serve the right resources for your application to display. The model should not know how to represent itself in the view, and the view should not know how resources should be acquired from the models. The two parties both communicate with the controller, which knows which models to extract from or write to, and then renders the correct data into the correct views. An application without controllers would have a very limited amount of flexibility of managing its resources.

CRUD Actions

CRUD stands for Create, Read, Update, and Destroy. These are the four basic functions that interface with persistent storage, such as a database. Rails models are essentially wrappers to tables in a database, and it's the controller's job to select the correct action to use for a particular model after receiving a request.

Rails defines several default actions for a particular resource, and they are as follows:

class UsersController < ApplicationController

    def index
        # Gets a list of all users
    end

    def new
        # Shows how to create a new user (think of something like a sign up form)
    end

    def create
        # Creates a new user and adds it to the database
    end

    def show
        # Shows a particular user's information
    end

    def edit
        # Shows how to edit a particular user (think of an edit form)
    end

    def update
        # Updates a users attributes in the database
    end

    def destroy
        # Removes a user from the database
    end

end

These are obviously not implemented, but when you declare something like a User to be a resource in your application, Rails will expect the UsersController to have these methods.

A Note on Naming

Rails follows a central paradigm called "Convention over Configuration", which basically means that Rails takes care of a lot of decision making for you without restricting your flexibility. A key example is how files are named. If you create a model called User, it must be in a file called user.rb. The controller that manages the User resource must then be called UsersController (note how it is preferred to be pluralized), which is contained in a file named users_controller.rb. Rails will automatically choose the correct controller based on the resource name.

Routing

We will take a brief pause from controllers to discuss routing, since this is where it comes in play. The Router in a Rails application controls which HTTP requests (URLs) get processed by which controller actions. For example, this is a route:

get '/patients/:id', to: 'patients#show'

A request sent to your app's /patients/:id endpoint will be processed by the PatientsController's show action.

Note that we've been throwing around the term resource a lot. Rails allows you to define RESTful resources, which come with a prepackaged set of routes. For example, the single line

resources :photos

in your routes.rb will create 7 default routes, shown below:

HTTP Verb Path Controller#Action Named Helper
GET /photos photos#index images_path
GET /photos/new photos#new new_image_path
POST /photos photos#create images_path
GET /photos/:id photos#show image_path(:id)
GET /photos/:id/edit photos#edit edit_image_path(:id)
PATCH/PUT /photos/:id photos#update image_path(:id)
DELETE /photos/:id photos#destroy image_path(:id)

This is another example of "Convention over Configuration." These are the standard routes that you should expect for managing a resource.

There is much more to learn about routing, such as nested resources and member/collection routes and shallow nesting. These will come up in your project as you need them, so use this reference to help you out when you find yourself not knowing how to continue!

Strong Parameters

Strong parameters are a security measure enforced by Rails to prevent users from sending malicious data to your application. If you are building an online learning platform, you will not want users to be able to update their progress or grades, as this will conflict with the integrity of your data.

The controllers receive all request parameters in a reserved variable called params. You can access this variable from anywhere in the controller. Consider the following JSON request body for updating a user:

{
    "user": {
        "name": "Morgana", 
        "grade": 105.0, 
        "age": 17
    }
}

In your UsersController, params[:user] will contain all of the fields name, grade, and age. However, if you use the following strong parameters:

params.require(:user).permit(:name, :age)

Then, only name and age will be available (grade will be filtered out and thus not modified). Usually, strong parameters are defined in a private method and utilized in the following manner:

class UsersController < ApplicationController

    def update
        @user = User.find(params[:id])
        @user.update(user_params)
    end

    private

    def user_params
        params.require(:user).permit(
            :name, 
            :age
        )
    end
end

Rendering

By default, if you don't return anything in a controller method, Rails will look for and return a view template matching the controller action. For example, consider the following index method:

class UsersController < ApplicationController

    def index
        @users = User.all
    end

end

Without an explicit render statement, Rails will look for a template called index.html.erb in your user views directory and render it to the browser. Note that it is not limited to html (you can return JSON or XML too), and erb is one of many templating engines.

However, if you are using Rails mainly as just an API, then you will likely want data to be returned in JSON format (which your front end can use to render however it wants). You can use explicit renders to achieve this:

class UsersController < ApplicationController

    def index
        @users = User.all
        render json: @users
    end

end

results matching ""

    No results matching ""