ここでは、ユーザに紐付く投稿を取得し、いいね機能を実装し、パスワードを暗号化していきます。
Ruby on Rails5 学習コースⅨ
・postsテーブルにuser_idカラムを追加する
$ rails g migration add_user_id_to_posts # (YYYYMMDDHHMMHH_add_user_id_to_posts.rb) def change add_column :posts, :user_id, :integer end $ rails db:migrate
・user_idのバリデーション
# (models/post.rb)
validates :user_id, {presence: true}
・投稿したユーザのidを保存する
# (posts_controller.rb) def create @post = Post.new( content: params[:content], user_id: @current_user.id # 投稿したユーザidを保存 ) : end
・user_idからユーザ情報を取得する
# (posts_controller.rb) def show @post = Post.find_by(id: params[:id]) @user = User.find_by(id: @post.user_id) # @post.user_idの値からユーザ情報を取得 end
・ユーザ画像とユーザ名を表示する
# (posts/show.html.erb)
<img src="<%= "/user_images/#{@user.image_name}" %>"> # ユーザ画像
<%= link_to(@user.name, "/users/#{@user.id}") %> # ユーザ名(リンク)
・モデルにインスタンスメソッドを定義する
# (models/post.rb) def user return User.find_by(id: self.user_id) # インスタンスメソッド内で、selfはそのインスタンス自身を指す end $ rails c > post = Post.find_by(id: 1) # Postモデルのインスタンスを取得する > post.user # 変数postに対してuserメソッドを呼び出す id: 1, # 投稿に紐付いているユーザ情報が表示される name: "にんじゃわんこ", : password: "wanko"
・ユーザに紐付く複数の投稿を取得する
$ rails c
> posts = Post.where(user_id: 1) # whereメソッドで複数のデータを取得
# (models/user.rb)
def posts
return Post.where(user_id: self.id)
end
$ rails c
> user = User.find_by(id: 1)
> user.posts # 変数userに対してpostsメソッドを呼び出す
# ユーザに紐付いている複数の投稿情報が表示される
# (users/show.html.erb)
<% @user.posts.each do |post| %> # ユーザに紐付いている複数の投稿を1つづつpost変数に格納
<div class="posts-index-item">
<div class="post-left">
<img src="<%= "/user_images/#{post.user.image_name}" %>"> # 投稿に紐付いているユーザの画像を表示
</div>
<div class="post-right">
<div class="post-user-name">
# 投稿に紐付いているユーザ名を、投稿に紐付いているユーザの詳細ページへリンクする
<%= link_to(post.user.name, "/users/#{post.user.id}") %>
</div>
<%= link_to(post.content, "/posts/#{post.id}") %>
</div>
</div>
<% end %>
・投稿者だけが編集できるようにする
# (posts/show.html.erb)
# 投稿を作成したユーザidとログインしているユーザidが等しいことを確認
<% if @post.user_id == @current_user.id %>
<%= link_to("編集", "/posts/#{@post.id}/edit")
<%= link_to("削除", "/posts/#{@post.id}/destroy", {method: "post"}) %>
<% end %>
# (posts_controller.rb)
before_action :ensure_corrent_user, {only: [:edit, :update, :destroy]}
:
def ensure_correct_user
@post = Post.find_by(id: params[:id])
if @post.user_id != @current_user.id
flash[:notice] = "権限がありません"
redirect_to("/posts/index")
end
end
・完成版
# (YYYYMMDDHHMMHH_add_user_id_to_posts.rb)
def change
add_column :posts, :user_id, :integer
end
# (models/post.rb)
validates :content, {presence: true, length: {maximum: 140}}
validates :user_id, {presence: true} # 追加
def user
return User.find_by(id: self.user_id) # 追加
end
# (models/user.rb)
validates :name, {presence: true}
validates :email, {presence: true, uniqueness: true}
validates :password, {presence: true}
def posts
return Post.where(user_id: self.id) # 追加
end
# (posts_controller.rb)
before_action :authenticate_user
before_action :ensure_correct_user, {only: [:edit, :update, :destroy]} # 追加
def index
@posts = Post.all.order(created_at: :desc)
end
def show
@post = Post.find_by(id: params[:id])
@user = @post.user # 追加
end
def new
@post = Post.new
end
def create
@post = Post.new(
content: params[:content],
user_id: @current_user.id # 追加
)
if @post.save
flash[:notice] = "投稿を作成しました"
redirect_to("/posts/index")
else
render("posts/new")
end
end
def edit
@post = Post.find_by(id: params[:id])
end
def update
@post = Post.find_by(id: params[:id])
@post.content = params[:content]
if @post.save
flash[:notice] = "投稿を編集しました"
redirect_to("/posts/index")
else
render("posts/edit")
end
end
def destroy
@post = Post.find_by(id: params[:id])
@post.destroy
flash[:notice] = "投稿を削除しました"
redirect_to("/posts/index")
end
def ensure_correct_user # 追加
@post = Post.find_by(id: params[:id])
if @post.user_id != @current_user.id
flash[:notice] = "権限がありません"
redirect_to("/posts/index")
end
end
# (posts/show.html.erb)
<div class="post-user-name">
<img src="<%= "/user_images/#{@user.image_name}" %>"> # 追加
<%= link_to(@user.name, "/users/#{@user.id}") %> # 追加
</div>
<p>
<%= @post.content %>
</p>
<div class="post-time">
<%= @post.created_at %>
</div>
<% if @post.user_id == @current_user.id %> # 追加
<div class="post-menus">
<%= link_to("編集", "/posts/#{@post.id}/edit") %>
<%= link_to("削除", "/posts/#{@post.id}/destroy", {method: "post"}) %>
</div>
<% end %>
# (posts/index.html.erb)
<% @posts.each do |post| %>
<div class="posts-index-item">
<div class="post-left">
<img src="<%= "/user_images/#{post.user.image_name}" %>"> # 追加
</div>
<div class="post-right">
<div class="post-user-name">
<%= link_to(post.user.name, "/users/#{post.user.id}") %> # 追加
</div>
<%= link_to(post.content, "/posts/#{post.id}") %>
</div>
</div>
<% end %>
# (users/show.html.erb)
<div class="user">
<img src="<%= "/user_images/#{@user.image_name}" %>">
<h2><%= @user.name %></h2>
<p><%= @user.email %></p>
<% if @user.id == @current_user.id %>
<%= link_to("編集", "/users/#{@user.id}/edit") %>
<% end %>
</div>
<% @user.posts.each do |post| %> # 追加
<div class="posts-index-item">
<div class="post-left">
<img src="<%= "/user_images/#{post.user.image_name}" %>">
</div>
<div class="post-right">
<div class="post-user-name">
<%= link_to(post.user.name, "/users/#{post.user.id}") %>
</div>
<%= link_to(post.content, "/posts/#{post.id}") %>
</div>
</div>
<% end %>
・確認すること
- 投稿詳細ページで、ユーザ名のリンクが表示されていて、ユーザ詳細ページへ画面遷移する
- ユーザ詳細ページにそのユーザが出品したアイテムのみ一覧表示されている
- 投稿者のみ編集/削除ボタンが表示されていて、実行できる
- 投稿者以外は編集/削除ができない
Ruby on Rails5 学習コースⅩ
・いいね機能の実装
# likesテーブルを作成するマイグレーションファイルを作成
$ rails g model Like user_id:integer post_id:integer
$ rails db:migrate # DBに変更を反映
# (models/like.rb)
validates :user_id, {presence: true}
validates :post_id, {presence: true}
$ rails c
> like = Like.new(user_id: 1, post_id: 2) # データを追加してみる
> like.save
# (posts/show.html.erb)
<% if Like.find_by(user_id: @current_user.id, post_id: @post.id) %>
いいね!済み
<% else %>
いいね!していません
<% end %>
・いいねボタンの設置
# (routes.rb)
post "likes/:post_id/create" => "likes#create"
# (likes_controller.rb)
before_action :authenticate_user
def create
@like = Like.new(user_id: @current_user.id, post_id: params[:post_id])
@like.save
redirect_to("/posts/#{params[:post_id]}")
end
# (posts/show.html.erb)
<% if Like.find_by(user_id: @current_user.id, post_id: @post.id) %>
いいね!済み
<% else %>
<%= link_to("いいね!", "/likes/#{@post.id}/create", {method: "post"}) %>
<% end %>
・いいね取り消しボタンの設置
# (routes.rb)
post "likes/:post_id/destroy" => "likes#destroy"
# (likes_controller.rb)
def destroy
@like = Like.find_by(user_id: @current_user.id, post_id: params[:post_id])
@like.destroy
redirect_to("/posts/#{params[:post_id]}")
end
# (posts/show.html.erb)
<% if Like.find_by(user_id: @current_user.id, post_id: @post.id) %>
<%= link_to("いいね!済み", "/likes/#{@post.id}/destroy", {method: "post"}) %>
<% else %>
<%= link_to("いいね!", "/likes/#{@post.id}/create", {method: "post"}) %>
<% end %>
・ボタンをアイコンに変更
# (layouts/application.html.erb)
<head>
:
# Font Awesomeの読み込み
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
</head>
# (posts/show.html.erb)
<%= link_to("/likes/#{@post.id}/create", {method: "post"}) do %>
# 文字列として認識されるのを避ける為、HTML要素は以下に記述する
<span class="fa fa-heart like-btn"></span>
<% end %>
・いいねの数を取得する
$ rails c
> Like.all.count # likesテーブルの全データの数
> Like.where(post_id: 1).count # likesテーブルのpost_idが1の数
# (posts_controller.rb)
def show
@post = Post.find_by(id: params[:id])
@user = @post.user
@likes_count = Like.where(post_id: @post.id).count # 変数@likes_countを定義
end
# (posts/show.html.erb)
<%= @likes_count %>
・いいねした投稿の一覧を表示する
# (routes.rb)
get "users/:id/likes" => "users#likes"
# (users_controller.rb)
def likes
@user = User.find_by(id: params[:id])
@likes = Like.where(user_id: @user.id)
end
# (users/show.html.erb)
<ul class="user-tabs">
<li class="active"><%= link_to("投稿", "/users/#{@user.id}") %></li>
<li><%= link_to("いいね!", "/users/#{@user.id}/likes") %></li>
</ul>
# (users/likes.html.erb)
<% @likes.each do |like| %>
<% post = Post.find_by(id: like.post_id) %>
:
<% end %>
・完成版
# (models/like.rb)
validates :user_id, {presence: true}
validates :post_id, {presence: true}
# (routes.rb)
post "likes/:post_id/create" => "likes#create" # 追加
post "likes/:post_id/destroy" => "likes#destroy" # 追加
post "users/:id/update" => "users#update"
get "users/:id/edit" => "users#edit"
post "users/create" => "users#create"
get "signup" => "users#new"
get "users/index" => "users#index"
get "users/:id" => "users#show"
post "login" => "users#login"
post "logout" => "users#logout"
get "login" => "users#login_form"
get "users/:id/likes" => "users#likes" # 追加
get "posts/index" => "posts#index"
get "posts/new" => "posts#new"
get "posts/:id" => "posts#show"
post "posts/create" => "posts#create"
get "posts/:id/edit" => "posts#edit"
post "posts/:id/update" => "posts#update"
post "posts/:id/destroy" => "posts#destroy"
get "/" => "home#top"
get "about" => "home#about"
# (users_controller.rb)
def likes
@user = User.find_by(id: params[:id])
@likes = Like.where(user_id: @user.id)
end
# (posts_controller.rb)
def show
@post = Post.find_by(id: params[:id])
@user = @post.user
@likes_count = Like.where(post_id: @post.id).count # 追加
end
# (likes_controller.rb)
before_action :authenticate_user
def create
@like = Like.new(user_id: @current_user.id, post_id: params[:post_id])
@like.save
redirect_to("/posts/#{params[:post_id]}")
end
def destroy
@like = Like.find_by(user_id: @current_user.id, post_id: params[:post_id])
@like.destroy
redirect_to("/posts/#{params[:post_id]}")
end
# (layouts/application.html.erb)
<head>
<title>TweetApp</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> # 追加
</head>
# (posts/show.html.erb)
<% if Like.find_by(user_id: @current_user.id, post_id: @post.id) %>
<%= link_to("/likes/#{@post.id}/destroy", {method: "post"}) do %>
<span class="fa fa-heart like-btn-unlike"></span>
<% end %>
<% else %>
<%= link_to("/likes/#{@post.id}/create", {method: "post"}) do %>
<span class="fa fa-heart like-btn"></span>
<% end %>
<% end %>
<%= @likes_count %>
<% if @post.user_id == @current_user.id %>
<div class="post-menus">
<%= link_to("編集", "/posts/#{@post.id}/edit") %>
<%= link_to("削除", "/posts/#{@post.id}/destroy", {method: "post"}) %>
</div>
<% end %>
# (users/show.html.erb)
<ul class="user-tabs">
<li class="active"><%= link_to("投稿", "/users/#{@user.id}") %></li>
<li><%= link_to("いいね!", "/users/#{@user.id}/likes") %></li>
</ul>
# (users/likes.html.erb)
<div class="user">
<img src="<%= "/user_images/#{@user.image_name}" %>">
<h2><%= @user.name %></h2>
<p><%= @user.email %></p>
<% if @user.id == @current_user.id %>
<%= link_to("編集", "/users/#{@user.id}/edit") %>
<% end %>
</div>
<ul class="user-tabs">
<li><%= link_to("投稿", "/users/#{@user.id}") %></li>
<li class="active"><%= link_to("いいね!", "/users/#{@user.id}/likes") %></li>
</ul>
<% @likes.each do |like| %>
<% post = Post.find_by(id: like.post_id) %>
<div class="posts-index-item">
<div class="post-left">
<img src="<%= "/user_images/#{post.user.image_name}" %>">
</div>
<div class="post-right">
<div class="post-user-name">
<%= link_to(post.user.name, "/users/#{post.user.id}") %>
</div>
<%= link_to(post.content, "/posts/#{post.id}") %>
</div>
</div>
<% end %>
・確認すること
- 投稿詳細ページでいいねボタンといいねの数が表示されている
- いいねボタンをクリックすると、いいね又はいいね解除ができる(色が変わる)
- いいね一覧タブが表示されていて、画面遷移できる
Ruby on Rails5 学習コースⅪ
・bcryptのインストール
# (Gemfile)
gem 'bcrypt' # 暗号化する為のgem
$ bundle install # 上記で記載したgemをインストール
$ rails s -b $IP -p $PORT # 「bundle install」後は、railsサーバを「ctrl+c」で停止した後、起動する
# (models/user.rb)
has_secure_password # bcryptをインストールすることで暗号化するメソッドを使用できる
validates :name, {presence: true}
validates :email, {presence: true, uniqueness: true}
# passwordカラムのバリデーションは削除しておく(has_secure_passwordメソッドが自動的にチェック)
def posts
return Post.where(user_id: self.id)
end
・password_digestカラムの追加
$ rails g migration change_users_columns # YYYYMMDDHHMMSS_change_users_columns.rb def change add_column :users, :password_digest, :string # password_digestカラムの追加 remove_column :users, :password, :string # passwordカラムの削除 end $ rails db:migrate
・暗号化されたパスワードを用いてログインする
# (users_controller.rb) def login @user = User.find_by(email: params[:email]) # authenticateメソッドを使って、「送信されたメールアドレスと一致するユーザー」のpassword_digestと、送信されたパスワードが一致するかを確認 if @user && @user.authenticate(params[:password]) : end end
・確認すること
- 「rails c」にて、パスワードが暗号化されて保存されることを確認
- 問題なくログインできる
> user = User.find_by(id: 1)
> user.password = “password”
> user.save
[siteorigin_widget class=”AdWidgetItem”][/siteorigin_widget]
[siteorigin_widget class=”WP_Widget_Search”][/siteorigin_widget]
[siteorigin_widget class=”WP_Widget_Pages”][/siteorigin_widget]
[siteorigin_widget class=”AdWidgetItem”][/siteorigin_widget]
