はじめに
Railsの学習記録です。
※前回の記録
※Rubyに関しては以下で記録
✅参考
Progate:Rails学習コース
書籍:現場Rails
投稿とユーザーの紐づけ
※ProgateⅨの内容
①postsテーブルにuser_idカラムを追加して紐づけ
rails g migration add_user_id_to_posts
マイグレーションファイルの編集
class AddUserIdPosts < ActiveRecord::Migration[5.2] def change add_column :posts, :user_id, :integer end end
マイグレートでカラム追加
rails db:migrate
Postモデルへバリデーションを追加
validates :user_id, {presence:true}
②投稿をログインユーザーへ紐づける
postコントローラのcreateアクションのnewメソッド引数にプロパティ追加。
def create @post = Post.new( content: params[:content], user_id: @current_user.id )
🙄current_userはapplicationコントローラで定義しているログインユーザーの情報が諸々入ったオブジェクトですね!
③誰の投稿かわかるようにビューを変更
postコントローラのshowアクション内で変数を定義
def show @post = Post.find_by(id: params[:id]) @user = User.find_by(id: @post.user_id) end
🙄post変数のuser_idカラムを参照してUserモデルから取り出し定義!※この後さらにリファクタリングします。
post/show.html.slimの編集
.ly img src="/user_images/#{@user.image_name}" = link_to(@user.name, "/users/#{@user.id}")
表示されることを確認

④モデルにインスタンスメソッドを定義
※投稿に紐づくユーザーの情報を簡単に使えるようにメソッドを定義する。
postモデル内にインスタンスメソッドを定義
→postインスタンスで使うことができる。
def user return User.find_by(id: self.user_id) end
※selfはインスタンス自身を指す
確認
post = Post.find_by(id: 9) post.user User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] => #<User id: 1, name: "sato", email: "sato@example.com", created_at: "2020-07-02 08:39:39", updated_at: "2020-07-04 12:44:58", image_name: "1.jpg", password: "testtest">
postコントローラを書き換える
# @user = User.find_by(id: @post.user_id) @user = @post.user
🙄Postモデルのユーザーidでusersテーブルのidカラムを探してますね。
投稿一覧にもユーザー名を表示するため、ビューの変更
index.html.slim
.ly - @post.each do |post| .display-block img src="/user_images/#{post.user.image_name}" span = link_to(post.user.name, "/users/#{post.user.id}") span = link_to(post.content, "/post/#{post.id}")
確認

※備忘録:ここでuser_idがnilだったりするとエラーになったのでデータの整理をしました。
⑤ユーザー詳細ページに投稿を表示
find_byメソッドは条件に合致するデータ1件のみの取得なので、複数のデータを取得するためにwhereメソッドを用いる。
post = Post.where(user_id: 1)
UserモデルにPostインスタンスを戻り値として返すpostsメソッドを定義
def posts return Post.where(user_id: self.id) end
※これでuserインスタンスで作られたインスタンス変数でpostsメソッドが利用できる。
users/show.html.slimに以下追記
.ly h2 投稿一覧 - @user.posts.each do |post| .display-block img src="/user_images/#{post.user.image_name}" p = link_to(post.user.name, "/users/#{post.user.id}") p = link_to(post.content, "/post/#{post.id}")
※erbの場合
<% @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 %>
🙄コードの量がだいぶ変わってしまうのでerbとslimは頭の切り替えが必要な印象。。。
確認

⑥投稿者のみ編集できるようにする
リンクの非表示
posts/show.html.erbの編集
<% 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にensure_corrent_userメソッドを定義
before_action :ensure_correct_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
※ここでローカル環境ではエラー出るようになってしまったので作業ステイ(備忘録)。
モデルにメソッド定義して、self活用しだすあたりの理解があいまい。
いいね機能の実装
※ProgateⅩの内容 非同期ではなくリロードで実装している。また詳細ページでのみの実装。
※以下は実際にローカルで作ったわけではありません。
いいね機能のためのlikesテーブルを作成
rails g model Like user_id:integer post_id:integer // user_id,post_idカラム rails db:migrate
バリデーション追加
validates :user_id, {presence: true} validates :post_id, {presence: true} # validates :user_id ,:post_id ,{presence: true} # 1行で書くことも可能
テーブルにデータ挿入
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 %> <%= link_to("いいね!済み","/likes/#{@post.id}/destroy",{method: "post"}) %> <% else %> <%= link_to("いいね!","/likes/#{@post.id}/create",{method: "post"}) %> <% end %>
いいねボタンの作成※ビューは不要のためrails gは用いない!!
touch app/controllers/likes_controller.rb
Likeデータを作成するためのcreateアクション、削除のためのdestroyアクションを作成
class LikesController < ApplicationController 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 end
routes.rbの編集
post "likes/:post_id/create" => "likes#create" post "likes/:post_id/destroy" => "likes#destroy"
Font Awesomeの利用
<head>タグで読み込み
HTML要素をリンク化する場合。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 %>
いいねの数取得
rails c > Like.all.count => 3 > Like.where(post_id: 1).count => 2
posts_controller.rbのshowアクションに@likes_count変数を定義
@likes_count = Like.where(post_id: @post.id).count
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) # @userに紐付くLikeデータを取得 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 %>
パスワードの取り扱い
※ProgateⅪの内容
パスワードを普通の文字列でDB上に保管してしまうと、ハッキングでデータを盗まれた場合などのリスクが大きいので、安全性を高める必要がある。
パスワードの暗号化にbcryptというパッケージをgemを活用してインストールする。※以下Gemfileを編集
gem 'bcrypt'
インストール
bundle install
Userモデルを編集
has_secure_password ※パスワードをpassword_digestカラムに保存してくれる。
※bcryptをインストールすることで上記メソッドを使えるようになる。
マイグレーションファイルの作成
rails g migration change_users_columns
複数カラムの変更
def change add_column :users, :password_digest, :string remove_column :users, :password, :string end
※remove_columnでパスワードのカラムを取り消し
データベースの変更を反映
rails db:migrate
password_digestカラムに暗号化されたパスワードを保存するためには、今まで通りpasswordカラムに値を代入して保存。自動でpassword_digestカラムに保存される。
暗号化されたパスワードを用いたログイン
has_secure_passwordメソッドを有効にするとauthenticateメソッドが使えるようになる。
authenticateメソッドは引数を暗号化しpassword_digestの値と一致するか判定する。
users_controller.rb
def login @user = User.find_by(email: params[:email]) if @user && @user.authenticate(params[:password]) ~~~~ end end
おわりに
Progateの実装も、後半になるにつれてだいぶ繰り返しの実装になるように感じた。
テスト含め基本的な部分をもうちょっと固めたいと感じたので明日は書籍もしくはRailsチュートリアルを用いて学習していきたいと思います。
コメントを残す