3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Rails】初学者向け_基礎のみを活用しチャット機能を作る方法

Last updated at Posted at 2022-07-16

1.バージョンを確認しましょう!

Rails 5.1.6
ruby 2.7.2p112

2.完成イメージ

ログイン済みのユーザーで一つのチャットルームを共有し、交流できる機能です。
同じ名前のグループは作成できないようにしてあります。
ぜひclosedな環境でグループを作成し、お話してみてください(^_^)v

3.実装の大きな流れ

1.グループチャット機能に必要なModel、Controller、Viewの実装
2.グループ系モデルとUserモデルの紐付け
3.ちょっとしたフロントエンドデザイン

ちなみにUserモデルは既に作られている前提で記事を書いています。
筆者はdeviseというgemを使ってUser周りを整えております。

ちなみに本記事はこのような目次で進んで行きます!
一緒に楽しんでいきましょう ᕙ( ˙꒳​˙ )ᕗ

4. 必要ファイルを作成しましょう

$ rails g controller groups  #groups_controller.rbを作成
$ rails g controller chats   #chats_controller.rbを作成
$ rails g model group        #Group.rbを作成
$ rails g model group_user   #Group_user.rbを作成

Groupsテーブルがチャット周りの情報を格納するテーブルになってます✊
このGroupには幾つかのUser情報が含まれるので、
Group_userという中間テーブルを作ってあげましょう。

上記3つのファイルの作成が終わったら、
少しマイグレーションファイルに記述を書き加えていきましょう✊

~~_create_groups.rb
class CreateGroups < ActiveRecord::Migration[6.1]
  def change
    create_table :groups do |t|

      # 追記ここから
      t.string :name, null: false
      t.index :name, unique: true
      # 追記ここまで

      t.timestamps
    end
  end
end

ちょっとコードの確認をしましょうか。
t.string :name, null: falseはよく見ますよね。groupsテーブルにnameカラムを追加しています。
null: falseは「データがない状態は許さへんで!」ってことです!

t.index :name, unique: trueというこの行。これはユニーク制約と呼ばれるものです。
簡単に述べると「同じデータ」を保存しないということです。

同じグループ名を作ることはできないということですね。

~~_create_group_users.rb
class CreateGroupUsers < ActiveRecord::Migration[6.1]
  def change
    create_table :group_users do |t|

      # 追記ここから
      t.references :group, foreign_key: true
      t.references :user, foreign_key: true
      # 追記ここまで

      t.timestamps
    end
  end
end

モデルを作る際にreferencesを追記することで、指定したモデルとのアソシエーション関係を自動で作ることができます。これはとても便利なのです。

ただ実はこんな感じでターミナルでまとめて記述することもできます。
Groupだけでなく、その中でお話するメッセージもテーブル設計したいので、下記を記述しましょう。

$ rails g model Chat content:string user:references group:references 

後はGroupとUserが紐付いているいる必要があるため、下記のようにGroupsテーブルにuser_idを格納できるようにしましょう。

$ rails generate migration AddUserIdToGroups user_id:integer

最後に忘れないうちにマイグレーションをしておきましょう〜!^^


$ rails db:migrate

4.モデルファイルに追記

下記のモデルをこんな感じで編集していきましょう ᕙ( ˙꒳​˙ )ᕗ

こんな感じでカラムは入っていませんか??
下記のように直していきましょう!!

app/models/user.rb
  has_many :group_users, dependent: :destroy #追加
  has_many :groups, through: :group_users, dependent: :destroy #追加
  has_many :chats, dependent: :destroy #追加

throughは中間テーブルを通して(through)その先を参照するという事です。
つまり中間テーブルを介してgroupモデル、ないしはuserモデルにアクセスするという意味ですね。

app/models/group.rb
class Group < ApplicationRecord
    belongs_to :user
    has_many :chats, dependent: :destroy #追加
    has_many :group_users, dependent: :destroy #追加
    has_many :users, through: :group_users, dependent: :destroy #追加
    validates :name, presence: true, uniqueness: true
end

validates :name, presence: true, uniqueness: trueは先程の同じ名前のグループを生成しないようにしています。uniqueness独自性という意味ですからね。

app/models/group_user.rb
class GroupUser < ApplicationRecord
    belongs_to :group
    belongs_to :user
end

ちなみに、ここまでの作業で自然とUserモデルGroupモデルのアソシエーションが完了しています。
ぜひ自分の目を駆使しながら、どこでアソシエーションを使っていたか見抜いてみてください👀

5.ルーティングについて

ルーティングは下記を記載しましょう✊

app/config/routes.rb
    resources :groups, only: [:index, :new, :create, :show, :edit, :update, :destroy] do
      resources :chats, only: [:create]
    end

ルーティングについて少しおさらいしましょう。
アプリケーション開発していくとRoutingの記述がかなり増えてきて、とても見辛くなってしまいます。
そこでresourcesが登場するのでした。

routingにて特定のコントローラーに対してresoucesを記述することによりindex、create、new、edit、show、update、destroyの標準的な機能を想定したルーティングを設定してくれます。

しかしいま実装したいのは、group_idが1のURL内で複数のチャット投稿をしていく場合です。
つまりhttps://×××××××/group/1/chat/2のようなURLになるのでした。

このようなときに上記のようにdo~endで付帯するresourcesを囲んであげるのですが、このような書き方をRoutingのネストと言います。

6.Controllerの記述

長く書きすぎたので、Controllerはあっさり書こうと思います。
まずchats_controllerから!

app/controllers/chat_controller.rb
class ChatsController < ApplicationController
    before_action :authenticate_user!

    def create
      group = Group.find(params[:group_id])
      chat = group.chats.build(chat_params) #buildを使い、contentとtweet_idの二つを同時に代入
      chat.user_id = current_user.id
      if chat.save
        flash[:success] = "コメントしました"
        redirect_back(fallback_location: root_path)
      else
        flash[:success] = "コメントできませんでした"
        redirect_back(fallback_location: root_path)
      end
    end
  
    private
  
      def chat_params
        params.require(:chat).permit(:content)
      end
end

app/controllers/group_controller.rb
class GroupsController < ApplicationController
    before_action :set_group, only: [:edit, :update]

    def index
        @groups = Group.all
        @group_none = "グループに参加していません。"
    end

    def new
        @group = Group.new
    end

    def create
        @group = Group.new(group_params)
        @group.user_id = current_user.id
        if @group.save
            redirect_to groups_url, notice: 'グループを作成しました。'
        else
            render :new
        end
    end

    def show
        @group = Group.find(params[:id])
        @chats = @group.chats
        @chat = Chat.new
    end

    def edit
        @group = Group.find(params[:id])
    end

    def update
        @group = Group.find(params[:id])
        if @group.update(group_params)
            redirect_to groups_path, notice: 'グループを更新しました。'
        else
            render :edit
        end
    end

    def destroy
        delete_group = Group.find(params[:id])
        if delete_group.destroy
            redirect_to groups_path, notice: 'グループを削除しました。'
        end
    end

    private
        def group_params
            params.require(:group).permit(:name, user_ids: [])
        end

end

@group_none = "グループに参加していません。"というコードにより、グループがない際はメッセージを出すように変数化しています。

7.Viewの記述

app/views/group/index.html.erb
<%= stylesheet_link_tag 'groups', :media => "all" %>

 <li><%= link_to "グループを作成する", new_group_path %></li>

<div class="groups-container"> 
    <h3>OpenしているGroup一覧</h3>

    <% if @groups == [] %>
        <%= @group_none %>
    <% else %>
        <% @groups.each do |t| %>
          <div class="group">
            <div class="main-box">
                <div class="left-container">
                 <span>グループ名:<a href="/groups/<%= t.id %>"><%= t.name %></a></span>
                 <br>
                 <span>作成時刻: <%= t.created_at %></span>
                 </div>
                 <br>            
                 <br>
            <% if user_signed_in? && current_user.id == t.user_id %>  
              <%= button_to "グループを閉じる", group_path(t.id), method: :delete %>
            <% end %>
          </div>
       <% end %>
    <% end %>
< /div>

こちらはindex.html.erbでした。if文を用いて、グループが無い際は文章が表示されるように仕込んであります。

app/views/group/new.html.erb
<%= stylesheet_link_tag 'group', :media => "all" %>


<div class="post-container">
    <p class="title">チャット作成ツール</p>
    <%= form_for(@group, :url => { controller:'groups', action:'create'})do |f| %>
    <%= f.label :チャット名 %>
    <br>
    <%= f.text_field :name,size: 140%>
    <br>
    <br>
    <%= f.submit "チャット作成"%>
    <br>
<% end %>
</div>

これはグループ作成ページですね。

app/views/group/show.html.erb
<%= stylesheet_link_tag 'group', :media => "all" %>

<h3>グループでチャットしよう!</h3>
<div class="groupinfo">
  <p>グループ名:<%= @group.name %></p>
  <p>グループ作成日:<%= @group.created_at %></p>
</div>

<div class="comment-wrapper">
  <p>チャットルーム</p>
  <% @chats.each do |c| %>
    <div>
     <span>ユーザー名:<%= c.user.name unless c.user.blank? %></span>
      <br>
      <%= c.content %>
    </div>
      <br>
  <% end %>

  <% if user_signed_in? %>
    <%= form_with(model: [@group, @chat], local: true) do |f| %>
      <%= f.text_area :content %>
      <%= button_tag type: "submit" do %>
        <i class="far fa-comments"></i> チャットする
      <% end %>
    <% end %>
  <% end %>
</div>

とりあえずなんとか最低限は完成や、、、

8.デザイン

coming soon

9.まとめ

そこまで複雑な機能設計はないので、もっと複雑なものを実装次第Qiitaに取り入れようと思っております。
デザインも記載しますので、ぜひお楽しみに!

3
0
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
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?