LoginSignup
20
20

More than 3 years have passed since last update.

Ruby on Railsで落とし物報告サイト(掲示板風)をつくってみた

Last updated at Posted at 2019-03-28

まえがき

筆者はRuby、RailsをProgateで学習したばかりでその学習した力を試す為に今回落とし物報告サイトを作ってみることにしました。

環境

Mac OS Mojave 10.14.3
Docker 18.09
Ruby 2.6.1
Rails 5.0.0.1

1.ワークディレクトリ作成

開発に使用するワークディレクトリを作成する。
$ mkdir rails-app

2.Dockerfile作成

Docker は、Docker イメージファイルからアプリケーションの実行環境を詰め込んだコンテナを作成できる。

rails-app/Dockerfile
#  rubyの2.6.1 をベースイメージに指定
FROM ruby:2.6.1

# ruby コンテナの中で実行するコマンドを以降に記載

# rails 実行に必要な build-essentialとnodejsをインストール
RUN apt-get update -qq && apt-get install -y build-essential nodejs

# ruby コンテナのルートディレクトリにappディレクトリを作成
RUN mkdir /app

# appディレクトリを作業ディレクトリに指定
WORKDIR /app

# Dockerfileと同じディレクトリに存在するファイルをrubyコンテナのappディレクトリ配下にコピーする
COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock

# Gemのインストールコマンド
RUN bundle install

# Dockerfileと同じディレクトリに存在するファイルをrubyコンテナ内にコピーする
COPY . /app 

3.Gemfile作成

Gemfileは、インストールするGemを管理するためのファイルで、様々な機能を簡単に追加できる。

rails-app/Gemfile
# ソース取得先
source 'https://rubygems.org'

# Rails5を取得
gem 'rails', '5.0.0.1'

また、Gemfile.lock ファイルを作成する。
$touch Gemfile.lock

4.docker-compose.yml作成

docker-compose.yml は、複数のDockerコンテナを設定にしたがってまとめて起動するためのファイルです。

docker-compose.yml
# YMLファイルのバージョンを指定
version: '3'

# サービスの定義
services:
    # サービス1
    webserver:
        # 同じディレクトリ配下にあるDockerfileをビルド
        build: .
        # rails実行(s->serverの略)
        command: bundle exec rails s -p 3000 -b '0.0.0.0'
        # ローカルPCのディレクトリをrubyコンテナにマウント
        volumes:
            - .:/app
        # アプリ公開ポートを指定<公開ポート番号>:<コンテナ内の転送先ポート>
        ports:
            - 3000:3000
        # データベースを先に起動してからwebserverを起動
        depends_on:
            - db 
        tty: true
        stdin_open: true
    #サービス2
    db:
        image: mysql:5.7
        # rubyコンテナの/var/lib/mysqlにマウント
        volumes:
            - post-volume:/var/lib/mysql
        # mysqlのルートパスワードを設定するための環境変数
        environment:
            MYSQL_ROOT_PASSWORD: password

# マウントするデータ領域を指定
volumes:
    post-volume:

5.ビルドおよび起動

開発環境を構築するためのファイルを作成したら、Dockerコマンドでビルドおよび起動してみましょう。

# ファイルを確認
$ ls
Dockerfile      Gemfile         Gemfile.lock        docker-compose.yml

# railsプロジェクトを新規作成し、起動する
$ docker-compose run webserver rails new . --force --database=mysql

# Dockerfileをビルドして、rubyコンテナ内にファイルをコピーする
$ docker-compose build

# データベースに接続するため、パスワードとホスト名を設定
$ vim config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password: password # docker-compose.ymlに記述したパスワード
  host: db # docker-compose.ymlに設定したdbサービス

# webserverとdbを起動
$ docker-compose up -d 
posts_db_1 is up-to-date
Creating posts_webserver_1 ... done

# コンテナの起動を確認
$ docker-compose ps
      Name                     Command               State           Ports         
-----------------------------------------------------------------------------------
posts_db_1          docker-entrypoint.sh mysqld      Up      3306/tcp              
posts_webserver_1   bundle exec rails s -p 300 ...   Up      0.0.0.0:3000->3000/tcp

# 開発用のデータベースを作成
$ docker-compose run webserver bundle exec rake db:create
Starting posts_db_1 ... done
Created database 'app_development'
Created database 'app_test'

6.開発環境の起動・停止・削除

# 起動
$ docker-compose up -d

# 停止
$ docker-compose stop

# 削除
$ docker-compose down

環境設定は以上です。これからはコードを書いていきます。

●Web画面の作成

ルーティングの設定

ルーティングは、URL と Controller の紐付けを行う設定ファイルで、「config/routes.rb」 に記述する。

config/routes.rb
Rails.application.routes.draw do
    # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html

    # root -> ルートパス(localhost:3000)でアクセスした場合に割り当てられる設定で、Controllerファイルに記述するindexメソッドを実行するように定義
    get '', to: 'posts#index'
end

Controllerの作成

ルーティングの設定を行ったので、次は Controller を作成する。
「app/controllers」 ディレクトリ配下に posts_controller.rb のファイルを作成する。

app/controllers/posts_controller.rb
# Applicationcontrollerクラスを継承することで、クラスがコントローラと認識される
class PostsController < ApplicationController
    def index
    end
end

Viewの作成

View は、「app/views」配下に Controller 名と同じ名前でディレクトリを作成し、その配下に各アクションに対応する View を作成する。

# postsディレクトリ作成
$ mkdir app/views/posts

# indexが呼び出された時に表示するViewを作成
vim app/views/posts/index.html.erb
<h1>Hello World</h1>

Hello World の表示をしてみましょう。

Webブラウザに 「http:localhost:3000」 でアクセス

Hello Worldと表示されたら成功です。

●Bootstrapの導入

CSSのフレームワークであるBootstrapを導入をします。

GemfileにbootstrapのGemを記述する。


$ vim Gemfile

# bootstarp ver4を追記
gem 'bootstrap', '~>4.0.0'

# bootstrapをインストールするために再ビルド
$ docker-compose build

# サービスを立ち上げる
$ docker-compose up -d

# 拡張子を変更する
$ cd app/assets/stylesheets
$ mv application.css application.scss

# bootstrapをインポート
$ vim application.scss
/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
 * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
 * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
 * files in this directory. Styles in this file should be added after the last require_* statement.
 * It is generally better to create a new file per style scope.
 *
 */
@import "bootstrap";

Bootstrapには「jquery3, popper, bootstrap-sprockets」というライブラリが必要なので、
「app/assets/javascripts」配下にある application.js にインポートする設定を行う。

# インポート設定
$ vim app/assets/javascripts 

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery3
//= require popper
//= require bootstrap-sprockets
//= require jquery_ujs
//= require turbolinks
//= require_tree .

これでBootstrapを使うための設定は終わりです。

●投稿一覧画面を作成

Rails で作成する Web ページでは、主に 2 種類のテンプレートを使用して作成する。

共通レイアウト
app/views/layouts/application.html.erb

個別レイアウト
app/views/posts/index.html.erb

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>App</title>
    <%= csrf_meta_tags %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <div class="container">
        <%= yield %>
    </div>
  </body>
</html>
app/views/posts/index.html.erb
<div class="d-flex align-items-center">
    <h1>投稿一覧</h1>
    <div class="ml-auto posts_button">
        <a class="btn btn-outline-info" href="/posts/new" role="button">投稿</a>
    </div>
</div>
<table class="table table-bordered border-primary table-hover table-sm posts_table">
  <thead class="thead-dark">
    <tr>
      <th scope="col">落とし物</th>
      <th scope="col">落とし物を見つけた場所</th>
      <th scope="col">投稿者</th>
      <th scope="col">投稿日</th>
      <th scope="col">更新日時</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>携帯電話</td>
      <td>大阪駅</td>
      <td>山田</td>
      <td>2019/04/01</td>
      <td>2019/04/01</td>
    </tr>
  </tbody>
</table>

また、スタイルを定義するファイルは、「app/assets/stylesheets」配下に作成する。
テンプレートと同様に 2 種類のスタイルシートを作成しよう。

●共通のスタイルシート
app/assets/stylesheets/base.scss

●個別のスタイルシート
app/assets/stylesheets/posts.scss

app/assets/stylesheets/base.scss
h1 {
    margin: 30px 0;
}
app/assets/stylesheets/posts.scss
.posts_button {
    a {
        margin: 0 8px;
    }
}

.posts_table {
    tr:hover {
        cursor: pointer;
    }
}

スタイルシートを新規作成したら 「app/assets/stylesheets/application.scss」に
インポートする設定を行い、作成したスタイルシートを使えるようにする。

app/assets/stylesheets/application.scss
@import "base";
@import "posts";

●データベースのテーブル作成

ユーザが投稿する記事は、MySQLに保存します。
rails には、マイグレーションファイルというデータベースのテーブルの作成や変更を
管理するファイルがある。

以下のコマンドで、データベースのモデルとマイグレーションファイルを作成できる。

# モデルとマイグレーションファイル作成
$ docker-compose run webserver bundle exec rails g model post item:string feature:string location:string deliver:string name:string

テーブルを作成するには、このモデルとマイグレーションファイルをマイグレーションする必要がある。

$ docker-compose run webserver bundle exec rake db:migrate

これでテーブルが作成された。

●投稿画面の作成

投稿記事を格納するテーブルを作成したので、次は投稿画面を作成しよう。

事前準備

まずは、ルーティングに変更を加える。

/config/routes.rb
Rails.application.routes.draw do
    # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html

    # root -> ルートパス(localhost:3000)でアクセスした場合に割り当てられる設定で、Controllerファイルに記述するindexメソッドを実行するように定義
    get 'posts', to: 'posts#index'

    # 投稿ページを表示
    get 'posts/new', to: 'posts#new'
end

次は、Controller の変更

app/controllers/posts_controller.rb
class PostsController < ApplicationController
    def index
    end

    # 追加
    def new
    end
end

ヘルパーメソッド

ヘルパーメソッドは、View の中で使用できるメソッドで、HTML を簡潔に記述することができる。

ここでは、form を生成する form_forヘルパー利用したいと思う。

そのため、app/controllers/posts_controller.rb の new メソッドを修正する。

app/controllers/posts_controller.rb
class PostsController < ApplicationController
    def index
    end

    def new
        # Postモデルのオブジェクトを作成
        # @postはインスタンス変数で、Viewで参照可能
        @post = Post.new
    end
end

次は、投稿画面を作成する。

app/views/posts/new.html.erb
<div class="d-flex align-items-center">
  <h1>投稿</h1>
  <div class="ml-auto posts_button">
      <a class="btn btn-outline-info" href="/posts">投稿一覧</a>
  </div>
</div>
<%= form_for @post do |f| %>
    <div class="form-group">
      <%= f.label :item, '落とし物' %>
      <%= f.text_field :item, class: 'form-control' %>
      <small id="item-tip" class="form-text text-muted">
          落とし物を入力してください。
      </small>
  </div>

  <div class="form-group">
      <%= f.label :feature, '落とし物の特徴' %>
      <%= f.text_field :feature, class: 'form-control' %>
      <small id="feature-tip" class="form-text text-muted">
          落とし物の特徴を入力してください。
      </small>
  </div>

  <div class="form-group">
      <%= f.label :location, '落とし物を見つけた場所' %>
      <%= f.text_field :location, class: 'form-control' %>
      <small id="location-tip" class="form-text text-muted">
          落とし物を見つけた場所を入力してください。
      </small>
  </div>

  <div class="form-group">
      <%= f.label :deliver, 'どこに届けたか' %>
      <%= f.text_field :deliver, class: 'form-control' %>
        <small id="deliver-tip" class="form-text text-muted">
            どこに届けたかを入力してください。
        </small>
  </div>

  <div class="form-group">
      <%= f.label :name, '投稿者名' %>
      <%= f.text_field :name, class: 'form-control' %>
        <small id="name-tip" class="form-text text-muted">
            あなたの名前を入力してください。
        </small>
  </div>

  <%= f.submit '投稿', class: 'btn btn-info btn-block' %>
  <small id="submit-tip" class="form-text text-muted">
      投稿する前に投稿内容を見直してください!
  </small>
<% end %>

●投稿データの保存

投稿ボタンを押下したら投稿データをデータベースに保存したい。

ここでは、投稿データの作成から保存まで行う。

事前準備

ルーティングを変更する。

/config/routes.rb
Rails.application.routes.draw do
    # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html

    # root -> ルートパス(localhost:3000)でアクセスした場合に割り当てられる設定で、Controllerファイルに記述するindexメソッドを実行するように定義
    get 'posts', to: 'posts#index'

    # 投稿ページを表示
    get 'posts/new', to: 'posts#new'

    # 投稿データ作成
    post 'posts', to: 'posts#create'
end
app/controllers/posts_controller.rb
class PostsController < ApplicationController
    def index
    end

    def new
        # Postモデルのオブジェクトを作成
        # @postはインスタンス変数で、Viewで参照可能
        @post = Post.new
    end

    def create
        # データはparamsという変数に渡されてくる
        # create は、Postモデルのクラスメソッド
        Post.create(post_params)
    end

    private

    # paramsから欲しいデータのみ抽出
    def post_params
        params.require(:post).permit(:item, :feature, :location, :deliver, :name, :title, :content)
    end
end

続いて、view を編集する。

app/views/posts/index.html.erb
<div class="d-flex align-items-center">
    <h1>投稿一覧</h1>
    <div class="ml-auto posts_button">
        <a class="btn btn-outline-info" href="/posts/new" role="button">投稿</a>
    </div>
</div>
<table class="table table-bordered border-primary table-hover table-sm posts_table">
  <thead class="thead-dark">
    <tr>
      <th scope="col">落とし物</th>
      <th scope="col">落とし物を見つけた場所</th>
      <th scope="col">投稿者</th>
      <th scope="col">投稿日</th>
      <th scope="col">更新日時</th>
    </tr>
  </thead>
  <tbody>
   <!-- 追加 -->
      <% @posts.each do |post| %>
        <tr>
            <td><%= post.item %></td>
            <td><%= post.location %></td>
            <td><%= post.name %></td>
            <td><%= post.created_at %></td>
            <td><%= post.updated_at %></td>
        </tr>
      <% end %>
  </tbody>
</table>

タイムゾーンが英語になっているので、修正する。

config/application.rb
require_relative 'boot'

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module App
  class Application < Rails::Application
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.
    # タイムゾーンを東京にする
    config.time_zone = 'Tokyo'
  end
end

config 配下のファイルを修正したら設定を有効にするために、docker コンテナを立ち上げ直す必要がある。

# コンテナを停止
$ docker-compose stop

# コンテナ起動
# docker-compose start

●フォーマットの管理

フォーマット管理用のファイルは、「config/initializers」 フォルダ配下にファイルを作成して管理する。

config/initializers/time_formats.rb
# 投稿日時のフォーマットを定義
Time::DATE_FORMATS[:datetime_base] = '%Y/%m/%d %H:%M'

設定が完了したら docker を立ち上げ直す。

$ docker-compose stop
$ docker-compose up -d

続いて、index.html.erb ファイルを修正する。

app/views/posts/index.html.erb
<div class="d-flex align-items-center">
    <h1>投稿一覧</h1>
    <div class="ml-auto posts_button">
        <a class="btn btn-outline-info" href="/posts/new" role="button">投稿</a>
    </div>
</div>
<table class="table table-bordered border-primary table-hover table-sm posts_table">
  <thead class="thead-dark">
    <tr>
      <th scope="col">落とし物</th>
      <th scope="col">落とし物を見つけた場所</th>
      <th scope="col">投稿者</th>
      <th scope="col">投稿日</th>
      <th scope="col">更新日時</th>
    </tr>
  </thead>
  <tbody>
      <% @posts.each do |post| %>
        <tr>
            <td><%= post.item %></td>
            <td><%= post.location %></td>
            <td><%= post.name %></td>
            <td><%= post.created_at.to_s(:datetime_base) %></td>
            <td><%= post.updated_at.to_s(:datetime_base) %></td>
        </tr>
      <% end %>
  </tbody>
</table>

●詳細画面作成

投稿一覧、投稿画面の次は、詳細画面を作ろう。

まずは、ルーティングを設定する。

config/routes.rb
Rails.application.routes.draw do
    # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html

    # root -> ルートパス(localhost:3000)でアクセスした場合に割り当てられる設定で、Controllerファイルに記述するindexメソッドを実行するように定義
    get 'posts', to: 'posts#index'

    # 投稿ページを表示
    get 'posts/new', to: 'posts#new'

    # 投稿データ作成
    post 'posts', to: 'posts#create'

    # 投稿ページ表示(追加)
    get 'posts/:id', to: 'posts#open'
end

続いて、Controller を作成する。

app/controllers/posts_controller.rb
# Applicationcontrollerクラスを継承することで、クラスがコントローラと認識される
class PostsController < ApplicationController
    def index
        # 投稿データを全て取得、またインスタンス変数なのでViewで参照可能
        @posts = Post.all
    end

    def new
        # Postモデルのオブジェクトを作成
        # @boardはインスタンス変数で、Viewで参照可能
        @post = Post.new
    end

    def create
        # データはparamsという変数に渡されてくる
        # create は、Postモデルのクラスメソッド
        Post.create(post_params)
    end

    # 追加
    def show
       @post = Post.find(params[:id])
    end

    private

    # paramsから欲しいデータのみ抽出
    def post_params
        params.require(:post).permit(:item, :feature, :location, :deliver, :name, :title, :content)
    end
end

viewを作成する。

app/views/posts/show.html.erb
<div class="d-flex align-items-center mt-4 mb-4">
    <div class="ml-auto posts_button">
        <a class="btn btn-outline-info" href="/posts" role="button">投稿一覧</a>
        <a class="btn btn-outline-info" href="/posts" role="button">編集</a>
    </div>
</div>

<div class="card">
  <div class="card-header bg-info text-white">
    <h4><%= @post.item %></h4>
  </div>
  落とし物の特徴
  <div class="card-body">
    <p class="card-text"><%= simple_format(@post.feature) %></p>
  </div>
  落とし物を見つけた場所
  <div class="card-body">
    <p class="card-text"><%= simple_format(@post.location) %></p>
  </div>
  落とし物を見つけた場所
  <div class="card-body">
    <p class="card-text"><%= simple_format(@post.deliver) %></p>
  </div>
  <div class="card-footer">
    <p class="text-right font-weight-bold mr-10"><%= @post.name %></p>
  </div>
</div>

●リソースベースルーティング

ブラウザから「localhost:3000/rails/info/routes」 のパスにアクセスするとルーティング情報が確認でき、リソースベースルーティングを使えば、一行で簡潔に記載できる。

config/routes.rb(変更前)
Rails.application.routes.draw do
   get 'posts', to: 'posts#index'
   get 'posts/new', to: 'posts#new'
   post 'posts', to: 'posts#create'
   get 'posts/:id', to: 'posts#open'
config/routes.rb(変更後)
Rails.application.routes.draw do
  resources :posts
end

ルーティングテーブルのメソッドのなかで、PATCH と PUT は使用しないので、
only オプションをつけて制限をかけよう。

config/routes.rb
Rails.application.routes.draw do
    resources :posts, only: [:index, :new, :create, :show]
end

投稿一覧ページに投稿内容の詳細を表示させるボタンとリンクを追加する。

app/views/posts/index.html.erb
<div class="d-flex align-items-center">
    <h1>投稿一覧</h1>
    <div class="ml-auto posts_button">
        <!-- 修正-->
        <%= link_to '投稿', new_post_path, class: "btn btn-outline-info" %> 
    </div>
</div>
<table class="table table-bordered border-primary table-hover table-sm posts_table">
  <thead class="thead-dark">
    <tr>
      <th scope="col">落とし物</th>
      <th scope="col">落とし物を見つけた場所</th>
      <th scope="col">投稿者</th>
      <th scope="col">投稿日</th>
      <th scope="col">更新日時</th>
      <!-- 追加 -->
      <th></th>
    </tr>
  </thead>
  <tbody>
      <% @posts.each do |post| %>
        <tr>
            <td><%= post.item %></td>
            <td><%= post.location %></td>
            <td><%= post.name %></td>
            <td><%= post.created_at.to_s(:datetime_base) %></td>
            <td><%= post.updated_at.to_s(:datetime_base) %></td>
            <!-- 追加 post オブジェクトを指定することで、そのページの詳細画面を表示させることができる-->
            <td><%= link_to '詳細', post, id: "detail-" + post.id.to_s, class: 'btn btn-outline-info' %></td>
        </tr>
      <% end %>
  </tbody>
</table>

その他の画面のリンクもヘルパーを使って書き換えておく。

app/views/posts/new.html.erb
<div class="d-flex align-items-center">
  <h1>投稿</h1>
  <div class="ml-auto posts_button">
     <!-- 修正 --> 
     <%= link_to '投稿一覧', posts_path, class: 'btn btn-outline-info' %>
  </div>
</div>
app/views/posts/show.html.erb
<div class="d-flex align-items-center mt-4 mb-4">
    <div class="ml-auto posts_button">
     <!-- 修正 -->
       <%= link_to '投稿一覧', posts_path, class: "btn btn-outline-info" %>
        <a class="btn btn-outline-info" href="/posts" role="button">編集</a>
    </div>
</div>

●編集画面

ルーティングに、編集用の edit / アップデート用の update オプションを追加する。

config/routes.rb
Rails.application.routes.draw do
    resources :posts, only: [:index, :new, :create, :show, :edit, :update]
end

次にコントローラを修正する。

app/controllers/posts_controller.rb
class PostsController < ApplicationController
    def index
        # 投稿データを全て取得、またインスタンス変数なのでViewで参照可能
        @posts = Post.all
    end

    def new
        # Postモデルのオブジェクトを作成
        # @boardはインスタンス変数で、Viewで参照可能
        @post = Post.new
    end

    def create
        # データはparamsという変数に渡されてくる
        # create は、Postモデルのクラスメソッド
        Post.create(post_params)
    end

    def show
        @post = Post.find(params[:id])
    end
    # 追加
    def edit
        @post = Post.find(params[:id])
    end

    private

    # paramsから欲しいデータのみ抽出
    def post_params
        params.require(:post).permit(:item, :feature, :location, :deliver, :name, :title, :content)
    end
end

次は、View を作成する。

app/views/posts/edit.html.erb
<div class="d-flex align-items-center">
  <h1>編集</h1>
  <div class="ml-auto posts_button">
      <%= link_to '投稿一覧', posts_path, class: 'btn btn-outline-info' %>
  </div>
</div>
<%= form_for @post do |f| %>
  <div class="form-group">
  <%= f.label :item, '落とし物' %>
  <%= f.text_field :item, class: 'form-control' %>
  <small id="item-tip" class="form-text text-muted">
      落とし物を入力してください。
  </small>
</div>
<div class="form-group">
  <%= f.label :feature, '落とし物の特徴' %>
  <%= f.text_field :feature, class: 'form-control' %>
  <small id="feature-tip" class="form-text text-muted">
      落とし物の特徴を入力してください。
  </small>
</div>
<div class="form-group">
  <%= f.label :location, '落とし物を見つけた場所' %>
  <%= f.text_field :location, class: 'form-control' %>
  <small id="location-tip" class="form-text text-muted">
      落とし物を見つけた場所を入力してください。
  </small>
</div>
<div class="form-group">
  <%= f.label :deliver, 'どこに届けたか' %>
  <%= f.text_field :deliver, class: 'form-control' %>
  <small id="deliver-tip" class="form-text text-muted">
      どこに届けたかを入力してください。
  </small>
</div>
<div class="form-group">
  <%= f.label :name, '投稿者名' %>
  <%= f.text_field :name, class: 'form-control' %>
  <small id="name-tip" class="form-text text-muted">
      あなたの名前を入力してください。
  </small>
</div>
    <%= f.submit '投稿', class: 'btn btn-info btn-block' %>
    <small id="submit-tip" class="form-text text-muted">
        投稿する前に投稿内容を見直してください!
    </small>
<% end %>

●アップデートアクションの実装

編集画面の次は、編集内容をアップデートできるようにする。

app/controllers/posts_controller.rb
class PostsController < ApplicationController
    def index
        # 投稿データを全て取得、またインスタンス変数なのでViewで参照可能
        @posts = Post.all
    end

    def new
        # Postモデルのオブジェクトを作成
        # @boardはインスタンス変数で、Viewで参照可能
        @post = Post.new
    end

    def create
        # データはparamsという変数に渡されてくる
        # create は、Postモデルのクラスメソッド
        Post.create(post_params)
    end

    def show
        @post = Post.find(params[:id])
    end

    def edit
        @post = Post.find(params[:id])
    end
    #追加
    def update
        post = Post.find(params[:id])
        # モデルの更新は、クラスメソッドのupdateメソッドで行える
        post.update(post_params)

        # リダイレクト処理
        redirect_to post
    end

    private

    # paramsから欲しいデータのみ抽出
    def post_params
        params.require(:post).permit(:item, :feature, :location, :deliver, :name, :title, :content)
    end
end

リダイレクト先の編集ボタンのリンクを書き換えておく。

app/views/posts/show.html.erb
<div class="d-flex align-items-center mt-4 mb-4">
    <div class="ml-auto posts_button">
        <%= link_to '投稿一覧', posts_path, class: "btn btn-outline-info" %>
        <%= link_to '編集', edit_post_path(@post), class: "btn btn-outline-info" %>
    </div>
</div>

<div class="card">
  <div class="card-header bg-info text-white">
    <h4><%= @post.item %></h4>
  </div>
  落とし物の特徴
  <div class="card-body">
    <p class="card-text"><%= simple_format(@post.feature) %></p>
  </div>
  落とし物を見つけた場所
  <div class="card-body">
    <p class="card-text"><%= simple_format(@post.location) %></p>
  </div>
  落とし物を見つけた場所
  <div class="card-body">
    <p class="card-text"><%= simple_format(@post.deliver) %></p>
  </div>
  <div class="card-footer">
    <p class="text-right font-weight-bold mr-10"><%= @post.name %></p>
  </div>
</div>

●削除機能

投稿記事の削除機能を実装しよう。

最初にルートの追加を行う。

config/routes.rb
Rails.application.routes.draw do
    root 'home#index'
    # onlyを削除
    resources :posts
end

ルーティングテーブルを確認すると destory メソッドが出てくるので、これを使おう。

次に Controller を修正する。

app/controllers/posts_controller.rb
class PostsController < ApplicationController
    def index
        @posts = Post.all
    end

    def new
        @post = Post.new
    end

    # 修正
    def create
        post = Post.create(post_params)

        # リダイレクト処理追加
        redirect_to post
    end

    def show
        @post = Post.find(params[:id])
    end

    def edit
        @post = Post.find(params[:id])
    end

    def update
        post = Post.find(params[:id])
        post.update(post_params)
        redirect_to post
    end

    # 削除機能
    def destroy
        post = Post.find(params[:id])
        post.delete

        # 投稿一覧へリダイレクト
        redirect_to posts_path
    end

    private

    def post_params
        params.require(:post).permit(:item, :feature, :location, :deliver, :name, :title, :content)
    end
end

最後に投稿ページ一覧に削除ボタンを追加する。

app/views/posts/index.html.erb
<div class="d-flex align-items-center">
    <h1>投稿一覧</h1>
    <div class="ml-auto posts_button">
      <%= link_to '投稿', new_post_path, class: "btn btn-outline-info" %> 
    </div>
</div>
<table class="table table-bordered border-primary table-hover table-sm posts_table">
  <thead class="thead-dark">
    <tr>
      <th scope="col">落とし物</th>
      <th scope="col">落とし物を見つけた場所</th>
      <th scope="col">投稿者</th>
      <th scope="col">投稿日</th>
      <th scope="col">更新日時</th>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
      <% @posts.each do |post| %>
        <tr>
            <td><%= post.item %></td>
            <td><%= post.location %></td>
            <td><%= post.name %></td>
            <td><%= post.created_at.to_s(:datetime_base) %></td>
            <td><%= post.updated_at.to_s(:datetime_base) %></td>
            <td><%= link_to "詳細" , post, class: 'btn btn-outline-info' %></td>
            <td><%= link_to "削除" , post, class: 'btn btn-outline-info', method: :delete, data: {confirm: "投稿記事を削除しますか?", cancel: "キャンセル", commit: "削除する", title: "削除の確認"} %></td>
        </tr>
      <% end %>
  </tbody>
</table>

link_to に post オブジェクトを渡すと 「/posts/:id」のパスをrails が生成し、「method: :delete」に
渡すことができ、削除ができる。

しかし、確認画面がほしいので作ろう。

$ vim Gemfile

# bootstarpのモーダルを追加
gem 'data-confirm-modal'

# bootstrapをインストールするために再ビルド
$ docker-compose build

# モーダルgemを使えるようにする
$ vim app/assets/javascripts/application.js 
// = require data-confirm-modal

# コンテナの再起動
$ docker-compose stop

# コンテナ起動
$ docker-compose up -d

# 投稿一覧の削除ボタンを修正
<td><%= link_to "削除" , post, class: 'btn btn-outline-info', method: :delete, data: {confirm: "投稿記事を削除しますか?", cancel: "キャンセル", commit: "削除する", title: "削除の確認"} %></td>

削除ボタンを押下すると確認ダイアログが表示されるようになった。

完成画像

投稿一覧画面

スクリーンショット 2019-03-28 13.23.22.png

投稿画面

スクリーンショット 2019-03-28 13.23.12.png

投稿詳細画面

スクリーンショット 2019-03-28 13.26.38.png

編集画面

スクリーンショット 2019-03-28 13.29.52.png

削除確認画面

スクリーンショット 2019-03-28 13.23.47.png

おわり

これで閲覧、投稿、詳細、編集、削除の機能が実装できました。
ぜひ、参考にしてみてください。
最後までお読みいただきありがとうございました。

20
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
20