始めに
忘れるといけないので、deviseと合わせたいいね機能の実装を簡単にまとめました。
前提
環境
Rails 5.2系
Ruby 2.6系
使用ライブラリ
devise
Slim
実装
1.Railsアプリの作成
$ cd
$ rails new favorite_function
$ cd favorite_function
2.gem deviseとSlim導入
gem 'slim-rails' #Slimのジェネレータを提供
gem 'html2slim' #ERB形式のファイルをslim形式に変換してくれる
gem 'devise'
$ bundle
3.deviseをインストール
$ rails g devise:install
create config/initializers/devise.rb
create config/locales/devise.en.yml
===============================================================================
Some setup you must do manually if you haven't yet:
1. Ensure you have defined default url options in your environments files. Here
is an example of default_url_options appropriate for a development environment
in config/environments/development.rb:
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
In production, :host should be set to the actual host of your application.
2. Ensure you have defined root_url to *something* in your config/routes.rb.
For example:
root to: "home#index"
3. Ensure you have flash messages in app/views/layouts/application.html.erb.
For example:
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
4. You can copy Devise views (for customization) to your app by running:
rails g devise:views
===============================================================================
4.deviseインストールと同時に指示される4つの指示に従い編集を行う
#### 4-1. 1つ目
Rails.application.configure do
.
.
.
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
.
.
.
end
#### 4-2. 2つ目
$ rails g controller posts new index show
Rails.application.routes.draw do
root to: 'posts#index' #自分がアプリに設定しようと思っているroot_urlを指定
resources :posts
.
.
.
end
#### 4-3. 3つ目
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
#### 4-4. 4つ目
$ rails g devise:views
invoke Devise::Generators::SharedViewsGenerator
create app/views/devise/shared
create app/views/devise/shared/_error_messages.html.erb
create app/views/devise/shared/_links.html.erb
create app/views/devise/confirmations
create app/views/devise/confirmations/new.html.erb
create app/views/devise/passwords
create app/views/devise/passwords/edit.html.erb
create app/views/devise/passwords/new.html.erb
create app/views/devise/registrations
create app/views/devise/registrations/edit.html.erb
create app/views/devise/registrations/new.html.erb
create app/views/devise/sessions
create app/views/devise/sessions/new.html.erb
create app/views/devise/unlocks
create app/views/devise/unlocks/new.html.erb
create app/views/devise/mailer
create app/views/devise/mailer/confirmation_instructions.html.erb
create app/views/devise/mailer/email_changed.html.erb
create app/views/devise/mailer/password_change.html.erb
create app/views/devise/mailer/reset_password_instructions.html.erb
create app/views/devise/mailer/unlock_instructions.html.erb
5.ここで全てのビューをSlimに変換する
$ bundle exec erb2slim app/views/ --delete
_※deviseのディレクトリに関してはSlimに変換するとdevise/shared/error_messages.html.slimでエラーが起きるので修正する。
- if resource.errors.any?
#error_explanation
h2
#以下の3行でエラーが出るので以下のように修正
= I18n.t("errors.messages.not_saved",
count: resource.errors.count,
resource: resource.class.model_name.human.downcase)
ul
- resource.errors.full_messages.each do |message|
li
= message
6.認証機構のビューを作成
html
head
.
.
.
body
- if user_signed_in?
=link_to 'ログアウト', destroy_user_session_path, method: :delete
- else
=link_to '新規登録', new_user_registration_path
=link_to 'ログイン', new_user_session_path
.
.
.
・deviseの、どのパスがどのアクションへ繋がっているかわからなくなったらrails routesコマンドで確認
7.モデルの作成
$ rails g devise User
$ rails g model Post content:string user:references
$ rails g model Favorite user:references post:references
$ rails g migration add_columns_to_users username:string
$ rails db:migrate
・モデル名 : references
とすることで、自動でマイグレーションファイル内でforeign_keyを張ってくれ、モデルファイル内で関連付けも行ってくれる。
・UserとPostは1対多
の関係なので、Postに対してUserの所有であることを示すreferenceを張る。
・UserはPostに対する複数のいいねを持ち
、PostもUserからの複数のいいねを持つ``````多対多
の関係なので、Favoriteに対してPostとUserの所有であることを示すreferenceを張る。
・deviseで作成されるデフォルトのUserモデルにはカラム追加できない
ので、カラム追加コマンドを実行しnameなどのUserに関するデータを作成。
8.モデルの関連付け
#### 8-1. Favoriteモデル
class Favorite < ApplicationRecord
belongs_to :user
belongs_to :post
end
#### 8-2. Postモデル
class Post < ApplicationRecord
belongs_to :user
has_many :favorites
end
・投稿に付いたいいねを取得する際にpost.favoritesという呼び出しが可能になる。post.favorites.countとすればいいね数を取得できる。
#### 8-3. Userモデル
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :favorites
has_many :favorite_posts, through: :favorites, source: :post
has_many :posts
end
・ユーザーの付けたいいねを取得
する際にuser.favoritesという呼び出しが可能になる。
・ユーザーがいいねを付けた投稿を自分のプロフィール画面に表示させたい場合
がある。その際にuser.favorite_postsという呼び出しができるようになる。これはthroughオプション
を使い、中間テーブルを通してPostモデルへと関連付けている。
・throughで使うモデルは、必ず先に関連付けを行っていなければならない
。この場合だとfavoritesを先に関連付けていなければならない。
・sourceには、命名したモデル名の参照元となるモデル名を記述する。
has_many :好きなモデル名, through: :中間テーブル, source: :命名したモデルの参照元となるモデル名
9.コントローラの作成
$ rails g controller posts new index show #deviseの設定の際に作成してる場合は省略
$ rails g controller users index show
$ rails g controller favorites create destroy
・(posts、)users、 favoritesコントローラ
10.ユーザー系機能の実装
#### 10-1. ルーティングの編集
Rails.application.routes.draw do
resources :users, only: [:index, :show] # ユーザー一覧やプロフィール画面用
devise_for :users # ログイン機構用
end
#### 10-2. usersコントローラの編集
class UsersController < ApplicationController
before_action :authenticate_user! # deviseが用意してくれてるuser認証
def index
@users = User.all
end
def show
@user = User.find(params[:id])
end
end
#### 10-3. usersのビューの編集
##### ● indexビュー
h1 ユーザー一覧
- @users.each do |user|
p
= link_to user.email, user
##### ● show
h1 ユーザープロフィールページ
p
= @user.username
= @user.email
#### 10-4. applicationビューにユーザー系ページへのリンクを張る
body
- if user_signed_in?
.
.
.
+ = link_to 'マイページ', user_path(current_user)
+ = link_to 'ユーザー一覧', users_path
- else
.
.
11.投稿系機能の実装
#### 11-1. ルーティングの編集
Rails.application.routes.draw do
resources :users
devise_for :users
root to: 'posts#index' # 投稿一覧をルートに指定
resources :posts
end
#### 11-2. postsコントローラの編集
class PostsController < ApplicationController
def index
@posts = Post.all
end
def new
@post = Post.new
end
def create
@post = current_user.posts.new(post_params) # current_userはdeviseが用意してくれる、ログイン最中のユーザーを表す
if @post.save
redirect_to @post
end
end
def show
@post = Post.find(params[:id])
end
private
def post_params
params.require(:post).permit(:content)
end
end
・ 新規投稿される際、作成した投稿の所有者を投稿したユーザーにしたいのでcurrent_user.posts
とする。これで、postsテーブルのuser_idにはcurrent_userのidが格納される。(UserモデルとPostモデルの関連付けを行っていないと、この方法は使えないので注意
)
#### 11-3. postsのビューの編集
##### ● index
h1 投稿一覧
- @posts.each do |post|
p= link_to post.user.email, user_path(post.user.id)
p= link_to post.content
##### ● new
h2 新規投稿
= form_with model: @post do |f|
p
= f.label :content
= f.text_area :content
= f.submit '投稿'
##### ● show
h1 投稿詳細ページ
p= @post.content
#### 11-4. applicationビューに投稿系ページへのリンクを張る
body
- if user_signed_in?
.
.
.
= link_to '投稿一覧', posts_path
- else
.
.
12.いいね機能の実装
#### 12-1. 投稿にいいねボタンを作成
h1 投稿一覧
- @posts.each do |post|
p= link_to post.user.email, user_path(post.user.id)
p= link_to post.content
- if user_signed_in? # deviseが用意してくれてる、ユーザーがログインしてるかどうかを確かめるメソッド
- if post.favorited_by?(current_user)
p= link_to post.favorites.count, post_favorites_path(post.id), method: :delete
- else
p= link_to post.favorites.count, post_favorites_path(post.id), method: :post
- else
= post.favorites.count
・ログインしているユーザーだけが見れるようにする
ために、user_signed_in?
を用いる。
・createアクションにていいねが付き
、destroyアクションにていいねが消える
仕組みなので、link_toヘルパーのmethodにそれぞれ明示的に指定してあげる。
・いいねをした場合はいいねの削除リンクを表示
させ、いいねをまだしていない場合はいいねの作成リンクを表示させたい
ので、favorited_by(current_user)メソッド
でそれを確認する。
・favorited_by(current_user)メソッドはこの後作成する。
・post.favorites.countでは、その投稿のいいね数を表示させている
。
#### 12-2. いいねの有無を確認するメソッドを定義
class Post < ApplicationRecord
belongs_to :user
has_many :favorites
+ def favorited_by?(current_user)
+ favorites.where(user_id: current_user.id).exists?
+ end
end
・Postモデルに対してだけ呼び出すメソッドのため、Postのインスタンスメソッドとして定義する。
・favoritesテーブルのuser_idにログイン中のユーザーのidが存在するか
を確かめている。
・post.favorited_by(current_user)みたいな感じで使うことになる。少し簡単にして見てみる
h1 投稿一覧
- @posts.each do |post|
.
.
.
- if post.favorited_by?(current_user)
# いいねの削除リンクを表示
- else
# いいねの作成リンクを表示
・これは以下のコードを示している。
- if post.favorites.where(user_id: current_user.id).exists?
# 削除リンク表示
- else
# いいねの作成リンクを表示
・取得したpostのfavoritesテーブルから
、user_idがログイン中のユーザーのidであるものを取得
させて、それが存在するかどうかを確認
させている。
・存在すればtrueを返す。いいねしてあるという意味
なので、いいね削除リンクを表示させる。
・存在しなければfalseを返す。いいねしてないという意味
なので、いいね作成リンクを表示させる。
#### 12-3. ルーティングの編集
Rails.application.routes.draw do
resources :users
devise_for :users
root to: 'posts#index'
resources :posts do
+ resource :favorites, only: [:create, :destroy]
end
end
・favoritesはposts内にネストさせる。これによりpost_favorites_path
などとして、投稿に対していいねを付けるためのパスの指定が簡単になる。
#### 12-4. favoritesコントローラの編集
class FavoritesController < ApplicationController
def create
favorite = current_user.favorites.build(post_id: params[:post_id])
favorite.save
redirect_to posts_path
end
def destroy
favorite = Favorite.find_by(post_id: params[:post_id], user_id: current_user.id)
favorite.destroy
redirect_to posts_path
end
end
・ログイン中のユーザーのfavoritesテーブルに
、いいねを押した投稿のidを格納する
。そして保存することでいいねの付与と見なさせている。
・いいね解除を押した投稿のid
と、ログイン中のユーザーのidが一致
するfavoritesテーブルのデータを取得して、消す。そうすることでいいね解除と見なさせている。
#### 12-5. ユーザーのページで、そのユーザーがいいねした投稿を表示する。ついでに、そのユーザーが投稿した投稿たちも表示させる。
##### ● usersコントローラのshowアクションを編集
class UsersController < ApplicationController
before_action :authenticate_user!
def index
@users = User.all
end
def show
@user = User.find(params[:id])
@posts = @user.posts
@favorite_posts = @user.favorite_posts
end
end
・@posts
では、普通の投稿を表示するために通常の投稿を取得してる。
・@favorite_posts
では、ユーザーがいいねした投稿を取得してる。app/models/user.rbで、favoritesテーブルを通してfavorite_postsという名前でPostモデルと関連付けを行わせたのはこのため。
##### ● show.html.slimを編集する
h1 ユーザープロフィールページ
p
= @user.username
h3 いいね
- @favorite_posts.each do |f_post|
p
= f_post.content
h3 投稿
- @posts.each do |post|
p
= post.content
・上はユーザーがいいねした投稿を表示する。
・下はユーザーが投稿した投稿たちを表示する。
・クリックイベントによる切り替えを行うとTwitterみたいな感じにできますが、ここではやりません。
以上で、いいね機能の実装は完了です。
大いに参考とした記事
https://qiita.com/kazukimatsumoto/items/14bdff681ec5ddac26d1