先日から作成しているサンプルプロジェクトの続き。
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
それっぽいのができた。