先日から作成しているサンプルプロジェクトの続き。
deviseというgemを使って、ログイン認証機能を付けたアプリに対して、ツイート機能を実装してみる。
データベースの基本構成は、下記の通り。
user
- id
- encrypted_password
tweet
- id
- body
- user_id
ターミナルから下記コマンドを打ち、usersというコントローラを作成し、indexとshowアクションを生成しておく。
deviseでusers用のモデルは生成済のため、usersに関しては、ここではコントローラだけ作成すればOK。
rails g controller users index show
ルーティングも自動でindexとshowが追加されているが、あとで編集するのでそのままにしておく。
tweet用のコントローラとモデルを作成する。(マイグレーションも忘れずに実施する)
rails g model Tweet body:text user_id:integer rails g controller tweets new index show create rails db:migrate
いろいろとわさわさできた。
ここから手動でコードをカサカサ変えていく
config/routes.rb
ルーティングは、もともと作ってあったサンプル画面のhello_catsをコメントアウトして、先ほど作成したtweetsに変更。
Rails.application.routes.draw do root 'tweets#index' devise_for :users resources :tweets resources :users # get 'hello_cats/index', to: 'hello_cats#index' # root 'hello_cats#index' end
app/controllers/users_controller.erb
usersコントローラでは、indexアクションでUserデータ全取得、showアクションで該当IDのデータを取得。
before_actionの使い方については、こちらの記事を参照。(ログイン前のユーザーに対してはログインページにリダイレクトさせる。)
class UsersController < ApplicationController
  before_action :authenticate_user!
  def index
    @users = User.all
  end
  def show
    @users = User.find(params[:id])
  end
end
app/controllers/tweets_controller.erb
index画面以外は、ログイン前のユーザーはログイン画面にリダイレクト。
class TweetsController < ApplicationController
  before_action :authenticate_user!, exept: [:index]
  def new
    @tweets = Tweet.new
  end
  def index
    @tweets = Tweet.all
  end
  def show
    @tweets = Tweet.find(params[:id])
  end
  def create
  end
end
app/views/layouts/application.html.erb
ツイート機能に関するページへのリンクを追加
<body>
  <header>
    <nav>
      <% if user_signed_in? %>
        <%= link_to '新規投稿', new_tweet_path %>    #追加
        <%= link_to 'マイページ', user_path(current_user.id) %>    #追加
        <%= link_to '登録内容変更', edit_user_registration_path %>
        <%= link_to 'ログアウト', destroy_user_session_path, method: :delete %>
      <% else %>
        <%= link_to '新規登録', new_user_registration_path %>
        <%= link_to 'ログイン', new_user_session_path %>
      <% end %>
        <%= link_to 'ツイート一覧', tweets_path %>    #追加
        <%= link_to 'ユーザー一覧', users_path %>     #追加
    </nav>
  </header>
  <p class="notice"><%= notice %></p>
  <p class="alert"><%= alert %></p>
  <%= yield %>
</body>
app/views/tweets/index.html.erb
<h1>Tweets#index</h1> <p>Find me in app/views/tweets/index.html.erb</p> <% @tweets.each do |tweet| %> <hr size="5"> <p><span>ツイート内容:</span><%= link_to tweet.body, tweet_path(tweet.id) %></p> <% end %>
app/views/tweets/new.html.erb
<h1>Tweets#new</h1>
<p>Find me in app/views/tweets/new.html.erb</p>
<%= form_for @tweets do |f| %>
  <p>
    <%= f.label :body, "ツイート" %>
    <%= f.text_field :body %>
  </p>
  <%= f.submit %>
<% end %>
app/views/tweets/show.html.erb
<h1>Tweets#show</h1> <p>Find me in app/views/tweets/show.html.erb</p> <hr size="5"> <p><span>ツイート内容:</span><%= @tweet.body %></p>
app/views/users/index.html.erb
<h1>Users#index</h1> <p>Find me in app/views/users/index.html.erb</p> <% @users.each do |user| %> <hr size="5"> <p><span>email: </span><%= link_to user.email, user_path(user.id) %></p> <% end %>
app/views/users/show.html.erb
<h1>Users#show</h1> <p>Find me in app/views/users/show.html.erb</p> <hr size="5"> <p><span>email: <.span><%= @users.email %></p>
app/models/user.erb
1ユーザーにつき、たくさんのtweetが紐づくので、userモデルに「has_many :tweets」を追加する。
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  has_many :tweets      #追加
end
app/models/tweet.erb
今度は逆に、tweetは必ずユーザーが一意に決まるので、tweetモデルに「belongs_to :user」を追加する。
class Tweet < ApplicationRecord belongs_to :user #追加 end
app/controllers/tweets_controller.erb
再度tweets_controller.erbに戻り、createアクションを追加する。
  def create
    @tweets = Tweet.new(tweet_params)
    @tweets.user_id = current_user.id
    @tweets.save
    redirect_to tweets_path
  end
  private
    def tweet_params
      params.require(:tweet).permit(:body)
    end
それっぽいのができた。
 
