Skip to content

Instantly share code, notes, and snippets.

@leahgarrett
Last active April 17, 2019 05:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leahgarrett/9100cfdbc2a2b0765c1de5c810d1f785 to your computer and use it in GitHub Desktop.
Save leahgarrett/9100cfdbc2a2b0765c1de5c810d1f785 to your computer and use it in GitHub Desktop.
Model and ORM

More Models and Restaurants app

Resources


Restaurant app

Objective

  • fix list of food types for filtering
  • add ability to add reviews to restaurants

Getting Started

Create a git repo for the rails-01-restaurants app on github and clone it locally

Create the rails app

rails new rails-01-restaurants --database=postgresql
cd rails-01-restaurants
rails db:create
rails server

RestaurantController

Creating the controller
rails generate controller restaurants

Ok now we know the basic functionality we are going to need lets setup our routes file.

config/routes.rb

Rails.application.routes.draw do
    get "/restaurants", to: "restaurants#index", as: "restaurants"
    post "/restaurants", to: "restaurants#create"
    get "/restaurants/new", to: "restaurants#new", as: "new_restaurant"
    get "/restaurants/:id", to: "restaurants#show", as: "restaurant"

    root 'restaurants#index'
end

Ok the routes file is setup so lets create and fill out our controller.

app/controllers/restaurant_controller.rb

class RestaurantsController < ApplicationController
    def index
    end

    def create
    end

    def new
    end

    def show
    end
end

Restaurant Model

Generate the model
rails generate model Restaurant title:string address:string description:string food_type:string

Run the created migration
rake db:migrate


Restaurant Views

And now lets just setup some empty views for index, new and show. Create will not have a view since its responsible for creating a new movie and then redirecting to one of the other routes.

Create these files

  • app/views/restaurants/index.html.erb
  • app/views/restaurants/new.html.erb
  • app/views/restaurants/show.html.erb

Alright so if we go to "localhost:3000/restaurants/new" we don’t get an error but there isn’t anything on our screen yet. Let’s fix that by adding in some HTML.

Use of for in labels
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label

app/views/restaurants/new.html.erb

<h1>Create A New Restaurant</h1>

<form action="<%= restaurants_path %>" method="POST" >
    <input type="hidden" value="<%= form_authenticity_token %>" name="authenticity_token" />

    <label for="title">Title</label>
    <input type="text" name="title" id="title"/>

    <label for="address">Address</label>
    <input type="text" name="address" id="address"/>

    <label>Description</label>
    <input type="textarea" name="description" id="description" />

    <label for="food_type">Food type</label>
    <input type="text" name="food_type" id="food_type" />

    <input type="submit" value="Create" />
</form>

Lets first print out our params variable and make sure we are receiving data first.

def create
    puts params
end

RestaurantsController

Look at the server output to see the data is coming through. Let’s fill out our controller methods.

class RestaurantsController < ApplicationController
    def index
        @restaurants = Restaurant.all
    end

    def create
        @restaurant = Restaurant.new(restaurant_params)

        if @restaurant.save
            redirect_to @restaurant
          else
            render 'new'
          end
    end

    def new
        @restaurant = Restaurant.new
    end

    def show
        @restaurant = Restaurant.find(params[:id])
    end

    private
      def restaurant_params
        params.permit(:title, :address, :description, :food_type)
      end
      
end

And now our other views.

app/views/restaurants/index.html.erb

<h1>Restaurants</h1>

<ul>
    <% @restaurants.each_with_index do |restaurant, index| %>
        <li>
            <%= restaurant.title %> - <%= restaurant.food_type %> <%= link_to "show", restaurant_path(restaurant.id) %>
        </li>
    <% end %>
</ul>

app/views/restaurants/show.html.erb

<h2><%= @restaurant.title %></h2>

<h3>Food Type</h3>
<p><%= @restaurant.food_type %></p>

<h3>Address</h3>
<p><%= @restaurant.address %></p>

<h3>Description</h3>
<p><%= @restaurant.description %></p>

<%= link_to "Back", :back %>

Awesome now all that’s left is to add in a way to update.


Edit

Ok first we need to setup our routes, controller methods and a view with a form to update our movie.

config/routes.rb

put "/restaurants/:id", to: "restaurants#update"
patch "/restaurants/:id", to: "restaurants#update"
get "/restaurants/:id/edit", to: "restaurants#edit", as: "edit_restaurant"

app/controllers/restaurants_controller.rb

def edit
    @restaurant = Restaurant.find(params[:id])
end

def update
      @restaurant = Restaurant.find(params[:id])

      if @restaurant.update(restaurant_params)
        redirect_to @restaurant
      else
        render 'edit'
      end
end

app/views/restaurants/edit.html.erb

<h1>Edit Restaurant</h1>

<form action="<%= restaurant_path(params[:id]) %>" method="POST" >
    <input type="hidden" value="<%= form_authenticity_token %>" name="authenticity_token" />

    <input type="hidden" name="_method" value="patch" />

    <label for="title">Title</label>
    <input type="text" name="title" id="title" value="<%= @restaurant.title %>"/>

    <label for="address">Address</label>
    <input type="text" name="address" name="address" id="address" value="<%= @restaurant.address %>"/>

    <label>Description</label>
    <input type="textarea" name="description" id="description" value="<%= @restaurant.description %>" />

    <label for="food_type">Food type</label>
    <input type="text" name="food_type" id="food_type"  value="<%= @restaurant.food_type %>"/>

    <input type="submit" value="Update" />
</form>

Add Validation

/app/models/restaurant.rb

class Restaurant < ApplicationRecord
    validates :title, presence: true
    validates :address, presence: true
    validates :description, presence: true
    validates :food_type, presence: true
end

Add the error display code to the create and edit forms

/app/views/restaurants/new.html.erb

<h1>Create A New Restaurant</h1>

<form action="<%= restaurants_path %>" method="POST" >
    <input type="hidden" value="<%= form_authenticity_token %>" name="authenticity_token" />

    <% if @restaurant.errors.any? %>
    <div id="error_explanation">
        <h2>
        <%= pluralize(@restaurant.errors.count, "error") %> prohibited
        this article from being saved:
        </h2>
        <ul>
        <% @restaurant.errors.full_messages.each do |msg| %>
            <li><%= msg %></li>
        <% end %>
        </ul>
    </div>
    <% end %>
    
    <label for="title">Title</label>
    <input type="text" name="title" id="title"/>

    <label for="address">Address</label>
    <input type="text" name="address" name="address" id="address"/>

    <label>Description</label>
    <input type="textarea" name="description" id="description" />

    <label for="food_type">Food type</label>
    <input type="text" name="food_type" id="food_type" />

    <input type="submit" value="Create" />
</form>

Reviews

Reviews will

  • belong to restaurants
  • restaurants will have many reviews (We will add user sign-in later)

Review Model

Generate the model
rails generate model Review title:string content:string rating:integer

Update the migration to create the association by adding the belongs_to /db/migrate/20190417044201_create_reviews.rb

  • your file name will have a different time stamp
class CreateReviews < ActiveRecord::Migration[5.2]
  def change
    create_table :reviews do |t|
      t.string :title
      t.string :content
      t.integer :rating
      t.belongs_to :restaurant, index: true

      t.timestamps
    end
  end
end

Seed some data

Add fake gem by adding the following line to the end of the gemfile
gem "faker", "~> 1.9"

Then at the command line run
bundle install

Add some seed data

/db/seeds.rb

puts "Start of Seeding..."
Restaurant.destroy_all
10.times do
  params = {
    title: Faker::Restaurant.unique.name,
    address: Faker::Address.unique.full_address,
    food_type: Faker::Restaurant.type,
    description: Faker::Lorem.paragraph  
  }
  puts "Creating Restaurant: #{params[:title]}"
  restaurant = Restaurant.new(params)
  restaurant.save

  reviews = rand(6)
  reviews.times do
    params = {
      title: Faker::Restaurant.unique.name,
      rating: rand(5),
      content: Faker::Lorem.paragraph,
      restaurant_id: restaurant.id
    }
    puts "Creating review: #{params[:title]}"
    review = Review.new(params)
    review.save
  end
end

puts "Seeding Over"

Add Relationship to the model

/app/models/restaurant.rb

class Restaurant < ApplicationRecord
    validates :title, presence: true
    validates :address, presence: true
    validates :description, presence: true
    validates :food_type, presence: true

    has_many :reviews
end

The dependent: :destroy argument says, when a Restaurant gets deleted, all reviews belonging to that restaurant will be deleted too.

Display Reviews with Restaurants

app/views/restaurants/index.html.erb

<h1>Restaurants</h1>

<ul>
    <% @restaurants.each_with_index do |restaurant, index| %>
        <li>
            <p><%= restaurant.title %> - <%= restaurant.food_type %> <%= link_to "show", restaurant_path(restaurant.id) %></p>
            <p>Reviews: <%= restaurant.reviews.count %></p>
        </li>
    <% end %>
</ul>

app/views/restaurants/show.html.erb

<h2><%= @restaurant.title %></h2>

<h3>Food Type</h3>
<p><%= @restaurant.food_type %></p>

<h3>Address</h3>
<p><%= @restaurant.address %></p>

<h3>Description</h3>
<p><%= @restaurant.description %></p>

<h3>Reviews</h3>
<ul>
    <% @restaurant.reviews.each do |review| %>
        <li>
            <p><%= review.title %> - <%= review.rating %> </p>
            <p><%= review.content %></p>
        </li>
    <% end %>
</ul>

<%= link_to "Back", :back %>

Add Create for Reviews

Add review edit Add review show Add review destroy Add restaurant reference for review

config/routes.rb

  post "/reviews", to: "reviews#create", as: "review"

Creating the controller
rails generate controller reviews

Add create to the review controller

/app/controllers/reviews_controller.rb

class ReviewsController < ApplicationController
    def create
      @restaurant = Restaurant.find(params[:restaurant_id])
      @review = @restaurant.reviews.create(review_params)
      redirect_to restaurant_path(@restaurant.id)
    end
   
    private
      def review_params
        params.permit(:title, :content, :rating)
      end
  end

Edit the show details page to include a add review form below the description

app/views/restaurant/show.html.erb

<<h3>Add a Review</h3>

<form action="<%= review_path %>" method="POST" >
    <input type="hidden" value="<%= form_authenticity_token %>" name="authenticity_token" />
    <input type="hidden" value="<%= @restaurant.id %>" name="restaurant_id" />

    <label for="title">Title</label>
    <input type="text" name="title" id="title"/>

    <label for="content">Content</label>
    <input type="text" name="content" id="content"/>

    <label for="rating">Rating</label>
    <input type="text" name="rating" id="rating" />

    <input type="submit" value="Create" />
</form>

Next Steps

  • add navigation with a link to new restaurant and list restaurants

Restaurant Show erb

  • Add code to only display the Reviews heading if there are any reviews
  • Change the review layout from list to use headings and make the rating more obvious
  • Add an overall rating for the restaurant

Restaurant Index erb

  • Display an overall rating for the restaurant

  • add an edit link

  • Add a destroy method for restaurants

  • Add a destroy method for reviews

    • Add a link to Delete review on the restaurant show page
  • Add flash messages to CRUD operations

  • Add next to the show page to show the next restaurant

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment