DEV Community

sawincp
sawincp

Posted on

THE MVC

The Model-View-Controller (MVC) pattern is a commonly used design pattern in web development, especially in Ruby on Rails applications. This design pattern plays a crucial role in organizing and separating various aspects of an application, making it easier to maintain and scale. In today's discussion, we will delve into each of these components, exploring their roles and responsibilities within the MVC architecture.

Model (M)

The Model component of the MVC represents the data and business logic of the application, with its main purpose being to interact with a database by retrieving and storing data. In Rails, models are created using Ruby classes that inherit from ActiveRecord::Base, with each model corresponding to a database table. By inheriting from ActiveRecord::Base, we can create Active Record Associations to accurately describe the relationships between our models (we'll discuss this in more detail shortly). Additionally, the model component is responsible for performing validations on our models to maintain data integrity for our application.

Foreign Keys

Before we can delve into how to incorporate Active Record Associations into our models, it's important to briefly discuss Foreign Keys and how they relate within our database. Foreign Keys are columns in our database that reference the primary key of another table (or another model). When using Active Record, we name the column in the model we are referencing after the name of that model, followed by "_id." This establishes a relationship between our models.

Active Record Associations

To define a relationship between one or more models we can use Active Record Associations:

  • One-To-Many
  • One-To-One
  • Many-To-Many

One-To-Many Relationship

This is the most common relationship when working with models. To establish this relationship, Active Record provides us with the has_many and belongs_to macros. These macros allow us to create instance methods that enable us to access data across models in a one-to-many relationship.

class Recipe < ApplicationRecord
    belongs_to :user
end

Enter fullscreen mode Exit fullscreen mode

From the code snippet above, each Recipe belongs_to one User. This gives us access to a user method in our Recipe class so that we can now retrieve the User object attached to a Recipe.

Going in the opposite direction, each User may have none, one, or many Recipes. To incorporate this we add the has_many macro to our User model.

class User < ApplicationRecord
    has_many :recipes
end
Enter fullscreen mode Exit fullscreen mode

One-To-One Relationship

A one-to-one relationship is typically the least common type of relationship you will encounter. In this type of relationship, two models are associated with each other in such a way that they have a unique and exclusive connection. For instance, a user may have only one associated profile.

class User < ApplicationRecord
    has_many :recipes

    has_one :profile
end
Enter fullscreen mode Exit fullscreen mode
class Profile < ApplicationRecord
    belongs_to :user
end
Enter fullscreen mode Exit fullscreen mode

If you're not sure which model should be declared with which macro, it's usually a safe bet to put belongs_to on the model that has the foreign key column in its database table.

Many-To-Many Relationships

A many-to-many relationship represents an association between two models where each record in one model can be associated with multiple records in another model, and vice versa. This connection is established through a join table that links the two models. You might wonder how to create this join table. It's straightforward: you create a new model (database table) specifically designed to facilitate the connection between the two associated models.

For example, how are we able to explain that a taxi can have many passengers and a passenger can have many taxis? In order to tie these two models together, we need to create a third model (call it rides).

If we think about it, a Ride belongs_to a taxi but it also belongs_to a passenger.

class Ride < ApplicationRecord
  belongs_to :taxi
  belongs_to :passenger
end
Enter fullscreen mode Exit fullscreen mode

And a taxi has_many passengers through rides

class Taxi < ApplicationRecord
  has_many :rides
  has_many :passengers, through: :rides
end
Enter fullscreen mode Exit fullscreen mode

And vice versa for passengers. A passenger has_many taxis through rides

class Passenger < ApplicationRecord
  has_many :rides
  has_many :taxis, through: :rides
end
Enter fullscreen mode Exit fullscreen mode

The has_many :through syntax tells Active Record to associate a Taxi to a Passengers through the Rides model. By using kind of association we are able to associate multiple taxis to multiple passengers and vice-versa.

Validations

Validations are special method calls placed at the top of model class definitions, and they are used to prevent invalid data from being saved to the database. These validations ensure that the data stored in the database adheres to specific criteria or constraints before it is saved. They play a crucial role in maintaining data integrity and consistency within the application.

To add validations to a model, you typically define them within the model class itself:

class User < ApplicationRecord
    validates :username, presence: true, uniqueness: true
end
Enter fullscreen mode Exit fullscreen mode

In this example, we are validating that a User record must have a username which must be unique.

These validations are triggered automatically when a record is being saved to the database using methods like "save" or "create". If the data doesn't meet these specific criteria, the record won't be saved and errors will be added to the object that we can use to show to our users.

View (V)

In a Rails application, the view layer should contain the least amount of logic of any layer in the MVC. The role of the view is to simply render whatever it is sent from the controller.

Controller (C)

The Controller serves as an intermediary between the Model and View. Its primary function is to receive requests from the browser, process them, interact with the Model to retrieve or update data, and then choose the appropriate View to render the response. To facilitate this connectivity, every route defined in the routes file of our application should correspond to a method in our controller. When a request is received, Rails uses the routes file to determine which controller method to execute.

Rails.application.routes.draw do
  post "/signup", to: "users#create"
  get "/me", to: "users#show"
  post "/login", to: "sessions#create"
  delete "/logout", to: "sessions#destroy"
  get "/recipes", to: "recipes#index"
  post "/recipes", to: "recipes#create"
end
Enter fullscreen mode Exit fullscreen mode

In our Routes file listed above, you notice we have many different routes defined in which our user will directed to based off the corresponding HTTP request. With a matching request, the associated controller and controller action will fire. For instance when a user's request matches our

post "/signup" route, the User controller will fire along with the create action

class UsersController < ApplicationController

  def create
    user = User.create!(user_params)
    session[:user_id]= user.id
    render json: user, status: :created
  end

  private

  def user_params
    params.permit(:username, :password, :password_confirmation, :image_url, :bio)
  end

end
Enter fullscreen mode Exit fullscreen mode

In this create action in our User controller, we create a new user, create a session and add the user ID to the session hash and returns back to the new User object along with a HTTP status code of created.

With all of this in mind here is the high-level flow of how the MVC pattern works in a Rails application:

  1. A user makes a request by navigating to a URL in their browser.
  2. The Rails router maps the URL to a specific controller action.
  3. The controller action processes the request, interacts with the model to fetch or modify data, and sets up any necessary variables for the view.
  4. The controller then renders the appropriate view, passing along the data.
  5. The view generates HTML or other content based on the data and sends it as a response to the user's browser.

Top comments (0)