# Controllers
> Controllers intermediate between models and views by handling users requests and determining the appropriate response to them.

In Rails, controllers play a fundamental role as a component of the Model-View-Controller pattern.

They act as intermediaries between [models](https://avohq.io/glossary/models) and views by handling user requests and determining the appropriate response to those requests.

Let's start by learning about their roles more in depth:
## The role of a controller in Rails
Controllers are responsible for handling the flow of data between the UI, the [views](https://avohq.io/glossary/views), and the data layer (models).

When a request is made to our application, the request is usually routed to a controller action that is responsible for processing the request and returning a response.

In a typical request flow, the controller is responsible for:

- **Receiving requests:** they receive HTTP requests that can come from users or even other computers, interpret the parameters and determine the appropriate action to execute.
- **Interacting with models or other objects:** because we would rather not have too much logic lying around in our controllers, they interact with other objects, generally models, to perform the desired operations, especially if we are doing CRUD operations.
- **Rendering views:** after processing the request, controllers typically render views that are used to present the data to users. By default, views are ERB templates that produce HTML, but they can actually produce JSON, XML and other formats.
- **Authorization:** controllers are often responsible for checking if a request is permitted in any given scenario and responding in consequence. Even if authorization can happen at a lower level, using a Rack middleware, developers tend to prefer handling it at the controller level.
- **Redirections:** we can handle these in the router itself or in a middleware but, whenever the redirect is part of the logic, it's preferable that we have them in the controller to avoid causing confusion.
- **Managing sessions and cookies:** authentication logic and cookie-bound user state is handled by controllers.
## What is a controller in Rails?
In Rails, a controller is just a class that inherits from `ActionController::Base` which follows some conventions:

- It's defined inside the `controllers` folder.
- Its name is usually in plural and ends in “Controller” like: `UsersController` or `SessionsController`.
- It defines actions, which are simply Ruby methods, that correspond to a route defined in the `routes.rb` file. However, every action in the router corresponds to a controller action, not every method in a controller has to correspond to a route action because we can define or invoke [helper methods](https://avohq.io/glossary/helpers) to accomplish what we want.
- Ideally, a controller only defines standalone actions that map to the REST methods: `index` to produce a collection of records, `show` to produce the detailed version of a record, `new` and `edit` to render the forms where records are created or edited, `create` and `update` to actually persist or update records and `destroy` to delete them. Sticking to this convention is not mandatory, but it usually helps us better design our application because it forces us to better extract concepts and entities.
- By default, a standalone controller action matches to a view in the `views` folder that has the same name as the action. For example, the `show` action in the `UsersController` would match to the `users/show.html.erb` in the `views` folder.
- It provides us with access to a special `render`method which can only be called once for every controller action because it returns a view of some kind.
- It provides us with access to a `respond_to` method that receives a block, where we can define which request formats we wish to handle and how to handle them.

So, even if a controller is a somewhat special Ruby object, at the end of the day, it's still a Ruby class so we can treat it as such.
## Controller structure
A typical controller in Rails might look like this:

```ruby
class ProductsController < ApplicationController
  before_action :set_product, only: [:show, :edit, :update, :destroy] # Filters

  ## GET /products => Returns a collection
  def index
    @products = Product.recent.page(params[:page]).per(params[:per_page])
  end

  ## GET /products/:id => Returns a single product
  def show
  end

  ## GET /products/new => Initializes a new product
  def new
    @product = Product.new
  end

  ## POST /products => Creates a new product
  def create
    @product = Product.new(product_params)

    if @product.save
      redirect_to @product, notice: 'Product was successfully created.'
    else
      render :new, status: :unprocessable_entity
    end
  end

  ## GET /products/:id/edit => Edits an existing product
  def edit
  end

  ## PATCH/PUT /products/:id => Updates an existing product
  def update
    if @product.update(product_params)
      redirect_to @product, notice: 'Product was successfully updated.'
    else
      render :edit, status: :unprocessable_entity
    end
  end

  ## DELETE /products/:id => Deletes a product
  def destroy
    @product.destroy
    redirect_to products_url, notice: 'Product was successfully destroyed.'
  end

  private

  # Strong parameters
  def product_params
    params.expect(product: [:name, :description, :price, :current_count, :active])
  end

  # Set product for actions
  def set_product
    @product = Product.find(params[:id])
  end
end
```
## The `update` action, step by step

`update` is the action behind `PATCH /products/:id`. The form that `edit` renders submits to it, and `form_with model: @product` picks the `PATCH` verb for you when the record is already saved. Four things happen in order:

1. The `set_product` filter loads the record from `params[:id]`.
2. `product_params` runs the submitted attributes through `params.expect`, so a client can only change the columns you listed.
3. `@product.update(product_params)` assigns those attributes, runs validations and callbacks, and saves. It returns `false` instead of raising when a validation fails.
4. On success you redirect; on failure you render the form again with the errors.

```ruby
# PATCH /products/:id
def update
  if @product.update(product_params)
    redirect_to @product, notice: "Product was successfully updated.", status: :see_other
  else
    render :edit, status: :unprocessable_entity
  end
end
```

The two status codes matter with Turbo, which Rails ships by default. A `303 See Other` after a successful submission tells the browser to fetch the next page with `GET`, so a refresh never repeats the `PATCH`. A `422 Unprocessable Entity` on failure tells Turbo to replace the form with the one you re-rendered, errors included; a plain `200` would leave the page unchanged.

A few variations you'll reach for:

- **Change one attribute from a button.** Add a member route (`patch :archive, on: :member`) with its own small action that calls `@product.update(archived: true)`, rather than overloading `update` with flags.
- **Fail loudly.** `update!` raises `ActiveRecord::RecordInvalid` instead of returning `false`. Use it in jobs and console scripts, where there is no form to re-render.
- **Skip validations and callbacks.** `update_column` and `update_columns` write straight to the database. They are for counters and timestamps, not for data a user typed.
- **`PUT` versus `PATCH`.** Rails routes both to `update`. Rails generates `PATCH` for forms since Rails 4 because an edit form sends a partial representation, which is what `PATCH` means.

## Filters or callbacks
Filters, or controller callbacks, provide a way to hook into the controller's lifecycle to extract duplicated logic and maximize code reutilization.

Rails provides us with three different filters:

- **`before_action`**: runs before the action executes. They're usually used to extract common code from multiple actions.
- **`after_action`**: runs code after the action executes, and it has access to the data that the client will receive.
- **`around_action`**: is executed before and after a controller action. They are useful for things like measuring performance.

Filters are ideal for authentication, authorization, setting common variables, logging, and performance tracking. You can limit filters to specific actions using the `only` or `except` options.

```ruby
class ProductsController < ApplicationController
  before_action :require_login, except: [:index, :show]
  before_action :set_product, only: [:show, :edit, :update, :destroy]
  after_action :log_activity, only: [:create, :update, :destroy]
  
  private
  
  def require_login
    redirect_to login_path, alert: "Please log in first" unless current_user
  end
  
  def set_product
    @product = Product.find(params[:id])
  end
  
  def log_activity
    ActivityLogger.log(current_user, "#{action_name} product ##{@product.id}")
  end
end
```

As useful as they are, don't overuse them because they can easily obscure the flow of execution and make the code confusing and harder to debug in general.
## Controller best practices
The main thing to consider when it comes to defining a controller in Rails is that their role is to operate as a coordinator layer and not so much to handle everything needed to produce the response.

So, considering this, some standard best practices are to write maintainable code are:

- **Keep controllers skinny:** have controllers be as lightweight as possible and delegate most of the logic to other, more appropriate, objects.
- **Use strong parameters:** avoid security issues by using strong parameters to only allow a list of predefined attributes to be passed to models or the objects that handle the logic or the persistence.
- **Avoid duplication:** keep your code more maintainable by defining private methods when necessary or extracting common logic to concerns. However, don't use a concern unless you've already identified clear duplication, and beware of relying on private methods to handle logic that should be located elsewhere.
- **Inherit as little as possible:** to avoid future confusion, don't inherit too much behavior from other controllers, like `ApplicationController`. Having too much logic in there can be problematic in the long term because abstracted behavior is harder to follow.
- **Handle errors:** don't trust the happy path, and think as hard as you can about possible errors that can arise from executing the code in your controllers. Errors are not necessarily handled entirely in the controller itself but think about them when writing logic in models or other objects.
- **Write tests when appropriate:** test your endpoints when they're handling complex logic or if they are responsible for a critical part of your application. Tests for endpoints that only declare an instance variable are less valuable.
## Summary
Rails controllers are the intermediaries between models and views in the MVC pattern. 

They receive HTTP requests, determine appropriate actions, and generate responses. 

A controller is a Ruby class inheriting from `ActionController::Base` that lives in the controllers folder and follows naming conventions.

Controllers define action methods (index, show, new, create, edit, update, destroy) that correspond to routes and typically map to similarly named views which produce HTML by default but can produce JSON, XML or other formats if needed.

They can use filters like `before_action` to share logic across actions, strong parameters to avoid mass assignment attacks and also include reusable code by using concerns.

Apart from coding styles and design criteria, controllers should generally be skinny by delegating business logic elsewhere and making as little work as possible.

It's also advisable to handle errors gracefully to provide adequate feedback to users and avoid degrading the user experience by raising an error, like a 500 error, that might confuse users.
