質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails 6

Ruby on Rails 6は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

解決済

2回答

1031閲覧

【Ruby on Rails】フォロー機能の実装をしたいのだがいいねボタンがビューに表示されない

kema

総合スコア6

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails 6

Ruby on Rails 6は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

0クリップ

投稿2020/10/30 11:04

編集2020/10/31 08:17

前提・実現したいこと

ポートフォリオの作成でActionTextを使用したブログ投稿アプリを作成しています。
フォロー機能を付けたくてRailsでフォロー機能を作る方法という記事を参考に実装を進めています。

発生している問題・エラーメッセージ

ブログの詳細ページでフォローができるようにしたいのですが、フォローをするボタンが表示されません。
テラテイルの中でも過去に同じ記事を参考にしていた方の質問も参考にしてエラーの解決を試みましたが、うまくいきませんでした。

該当のソースコード

relationshipsテーブル

class CreateRelationships < ActiveRecord::Migration[6.0] def change create_table :relationships do |t| t.references :user, foreign_key: true t.references :follow, foreign_key: { to_table: :users } t.timestamps t.index [:user_id, :follow_id], unique: true end end end

relationshipsモデルのアソシエーション

class Relationship < ApplicationRecord belongs_to :user belongs_to :follow, class_name: 'User' validates :user_id, presence: true validates :follow_id, presence: true end

userモデルのアソシエーション

class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable,:recoverable, :rememberable has_one_attached :image has_many :posts, dependent: :destroy has_many :comments, dependent: :destroy has_many :likes, dependent: :destroy has_many :like_posts, through: :likes, source: :post has_many :relationships has_many :followings, through: :relationships, source: :follow has_many :reverse_of_relationships, class_name: 'Relationship', foreign_key: 'follow_id' has_many :followers, through: :reverse_of_relationships, source: :user validates :profile, length: { maximum: 200 } with_options presence: true do validates :nickname, presence: true, length: { maximum: 10 } EMAIL_REGEX = /@+/.freeze validates :email, uniqueness: { case_sensitive: true }, format: { with: EMAIL_REGEX, message: '@を使用してください' } PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?[\d])[a-z\d]+\z/i.freeze validates :password, confirmation: true, length: { minimum: 6 }, format: { with: PASSWORD_REGEX, message: 'には英字と数字の両方を含めて設定してください' } end def follow(other_user) unless self == other_user self.relationships.find_or_create_by(follow_id: other_user.id) end end def unfollow(other_user) relationship = self.relationships.find_by(follow_id: other_user.id) if relationship relationship.destroy end end def following?(other_user) self.followings.include?(other_user) end end

relationshipsコントローラ

class RelationshipsController < ApplicationController before_action :set_user def create follwing = current_user.follow(@user) if following.seve flash[:success] = 'ユーザーをフォローしました' redirect_to @user else flash.now[:alert] = 'ユーザーのフォローに失敗しました' redirect_to @user end end def destroy following = current_user.unfollow(@user) if following.destroy flash[:success] = 'ユーザーのフォローを解除しました' redirect_to @user else flash.now[:alert] = 'ユーザーのフォロー解除に失敗しました' redirect_to @user end end private def set_user @user = User.find(params[:follow_id]) end end

postsコントローラー

class PostsController < ApplicationController before_action :set_post, only: [:show, :edit] before_action :move_to_index, except: [:index, :show, :search] def index @posts = Post.includes(:user).order("created_at DESC") @post = Post.includes(:user) end def show @comment = Comment.new @comments = @post.comments.includes(:user).order("created_at DESC") @user = User.find(params[:id]) end def new @post = Post.new end def edit end def create @post = Post.new(post_params) if @post.valid? @post.save redirect_to root_path else render :new end end def update @post = Post.find(params[:id]) if @post.update(post_params) redirect_to root_path else render :edit end end def destroy post = Post.find(params[:id]) if post.destroy redirect_to root_path else render :show end end def search @posts = Post.searchtext(params[:keyword]) end private def set_post @post = Post.find(params[:id]) end def post_params params.require(:post).permit(:title, :content, :image, :likes_count).merge(user_id: current_user.id) end def move_to_index unless user_signed_in? redirect_to action: :index end end end

フォローのボタン
app/views/relationships/_follow_button.html.erb

<% if user_signed_in? %> <% unless current_user == user %> <% if current_user.following?(user) %> <%= form_for(current_user.relationships.find_by(follow_id: user.id), html: { method: :delete }) do |f| %> <%= hidden_field_tag :follow_id, user.id %> <%= f.submit 'Unfollow', class: 'btn btn-danger btn-block' %> <% end %> <% else %> <%= form_for(current_user.relationships.build) do |f| %> <%= hidden_field_tag :follow_id, user.id %> <%= f.submit 'Follow', class: 'btn btn-primary btn-block' %> <% end %> <% end %> <% end %> <% end %>

ルーティング

Rails.application.routes.draw do devise_for :users, only: [:show] devise_for :users, controllers: { registrations: 'users/registrations' } root to: "posts#index" resources :posts do resources :comments, only: [:create, :destroy] collection do get 'search' end end resources :users, only: [:show, :index, :search] resources :relationships, only: [:create, :destroy] end

app/views/posts/show.html.erb

<div class="container"> <div class="row"> <div class="col-md-8 offset-md-2"> <%= image_tag @post.image.variant(resize:'800x800').processed %> <br> <br> <br> <h1 class="text-center"> <strong><%= @post.title %></strong> </h1> <div class="row"> <div class="col-2"> <% if @post.user.image.attached? %> <%= image_tag @post.user.image, class:"avatar m-4"%> <% else %> <%= image_tag ("defo.jpg"), class:"avatar m-4"%> <% end %> </div> <div class="col-4 align-self-center"> <a href="/users/<%= @post.user.id %>", class="h5", style="text-decoration: none"> <p class="h3 text-bold "><%= @post.user.nickname %></p> </a> <%= render partial: 'posts/posts', collection: @posts, as: :post %> </div> <%= render 'relationships/follow_button', user: @user %> </div> <%= @post.content %> </div> </div> </div> <hr> <div class="container "> <div class="col text-center text-muted mb-3"> <h4>コメント</h4> <%= @comments_count %> </div> <div class="col-8 mx-auto"> <% if @comments %> <% @comments.each do |comment| %> <% if user_signed_in? %> <p> <strong><%= link_to comment.user.nickname, "/users/#{comment.user_id}" %>:</strong> <%= comment.text %> </p> <% else %> <p> <strong> <%= comment.user.nickname %>:</strong> <%= comment.text %> </p> <% end %> <% end %> </div> <div class="row "> <div class="col-8 mx-auto"> <% if current_user %> <hr> <%= form_with(model: [@post, @comment], local: true) do |form| %> <div class="form-group mt-3"> <%= form.text_area :text, placeholder: "コメント", rows: "2", class: "form-control" %> <div class="text-right"> <%= form.submit "SEND", class: "btn btn-outline-secondary "%> <% end %> </div> </div> <% else %> <strong><p class="text-center">※※※ コメントするにはログインしてください ※※※</p></strong> <% end %> </div> </div> <% end %> </div> </div> <div class="container" style="margin-bottom:200px;">

試したこと

元の記事のコメント欄やこちらの質問でもあったようにrelationshipsコントローラの記述を
params[:relationship][:follow_id]の部分をparams[:follow_id]に訂正してみました。
それだと下記のエラーが出ます。

undefined method `id' for nil:NilClass

その後にhidden_field_tag :follow_id, user.idの部分をf.hidden_field :follow_id, user.id に修正して見ましたがエラー内容は変わりませんでした。

エラーメッセージがidが無いなっているのビューを表示させているposts_controller.rbのshowメソッドの中で@userを定義すると、エラーはなくなるのですがフォローボタンが表示されませんでした。

他にも自分なりに考えて色々とやってみたのですが解決できずにいます。

どうかよろしくおねがいします。

補足情報(FW/ツールのバージョンなど)

ruby_version 2.6.5

Rails 6.0.3.4

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

コントローラのうちposts#showに関係する部分だけ抽出しますと

ruby

1class PostsController < ApplicationController 2 before_action :set_post, only: [:show, :edit] 3 def show 4 @posts = Post.includes(:user).order("created_at DESC") 5 @comment = Comment.new 6 @comments = @post.comments.includes(:user).order("created_at DESC") 7 user = User.find(params[:id]) 8 @user = User.find(params[:id]) 9 end 10 private 11 def set_post 12 @post = Post.find(params[:id]) 13 end 14end

不思議な事に気づきませんか?
わかりやすくparams[:id]についてのみ抽出します

ruby

1 user = User.find(params[:id]) 2 @user = User.find(params[:id]) 3 @post = Post.find(params[:id])

params[:id]が何なのかを考え直すとよいでしょう。


質問とは関係ない部分ですがposts#showにてpost全件を表示するのは規模が大きくなると負荷がひどいだろうなという感想を抱きます。

投稿2020/10/31 04:31

asm

総合スコア15149

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kema

2020/10/31 07:16

asm様、回答ありがとうございます。頂いた回答を元に修正いたしました。 初心者ゆえ理解が浅いままにアプリを制作していて、params[:id]の意味を途中から見失っておりました。 今回改めて考え直すことができました。詳細ページに表示したい記事の情報を何度も取得してしまっていたのですね、、。 あと参考までにお聞きしたいのですが、もしも規模が大きくなってきた場合にはどのようにしたら負荷を抑えることができるのでしょうか?
asm

2020/10/31 10:27 編集

> 詳細ページに表示したい記事の情報を何度も取得してしまっていたのですね、、。 いえ、postのidが渡されているのにそのidでuserを検索している事が大問題です。 (複数回検索も無駄っちゃ無駄ですが) > もしも規模が大きくなってきた場合 まずは、「渡されたidのpostを表示するアクション(posts#show)にPost全件の表示がそもそも必要なのか」という設計部分ですね。 ツイッターのリプライのように関連するpostを表示する可能性はありますが、全件は過剰かと思います。 万が一、必要な場合はlimitなどで表示させる件数を絞ることを考えますし それでも負荷的にきつい場合はなんらかの方法でキャッシュさせる事になります。
kema

2020/10/31 10:54

なるほど、、質問以上の情報まで教えて頂き本当に参考になります。 一覧表示にしてしまっている部分も件数を絞って表示するように変更していきたいと思います。 ありがとうございました! 引き続きこれからもよろしくお願いします。
guest

0

ベストアンサー

先頭と最後の
<% unless current_user == user %>
<% end %>
を削除すると表示されるのでは?
もしそうですと、そこに解決のヒントがあります。

追記
ということは、復数ユーザーを登録して試すというのをやっていないですね。
<% unless current_user == user %>
これは、いま開いている人が、user でなかったらボタンを表示する」ということです。
ですから二人以上のuserを登録し、それぞれでPostしてみてください。

ああ、
before_action :set_user があるのにその set_userが定義されていないからです
private直下のdef にmethos名が落ちてます

投稿2020/10/30 23:53

編集2020/10/31 07:23
winterboum

総合スコア23567

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kema

2020/10/31 01:59

回答ありがとうございます! ご指摘のとおりに<% unless current_user == user %><% end %>の部分を削除すると、ボタンが表示されました。それで表示されるのはpostsコントローラーのshowの中でuserの情報をすべて取得してしまっているから、サインインしたユーザーでは表示されないということなのでしょうか? その後に試しにログアウトした状態で記事の詳細ページにアクセスしようとすると ``` undefined method `following?' for nil:NilClass ``` と別のエラーが表示されます。 following?メソッドが無いということはやはりuserの情報がちゃんと取れていないということだとは思うのですが、まだまだ勉強不足で理解が浅く具体的な解決方法がわかりません。 もう少し具体的に説明していただくと非常にありがたいです。 お手間を取らせますがよろしくおねがい致します。
kema

2020/10/31 04:56

本当にありがとうございます! ユーザーを二人にして別々の投稿をしてみるとうまく表示の切り替えができました。 ログインしていないユーザーに対しては<% if user_signed_in? %>を追記して記事の詳細ページの閲覧だけは可能にしました。 ただ表示がうまくいったと思ったら次はフォローするとエラーが発生します、、、 エラーの部分は【@user = User.find(params[:follow_id])】です。 この部分のエラーに関しては過去の質問なども参考にしてコードを修正してみたのですが、次に実行するとまた違う種類のエラーが発生してしまいました。 【undefined method `set_user' for #<RelationshipsController:0x00007fb25ec77938> Did you mean? set_request!】と表示されてやはりset_userが無いと言われてしまいます。 もっと別なところに問題があるのでしょうか? 何度も質問してすいません。
winterboum

2020/10/31 06:02

断片的に書かれても状況がわかりません。 どこでどういう操作をしたらどこのどこでこういうエラーになった と判るようにしてください。 エラーメッセージは省略しないで全文
kema

2020/10/31 06:33

winterboum様。 すいません、質問することにあまり慣れておらず分かりにくい説明をしてしまいました。 まずログインした状態で、posts/show.html.erbにあるフォローボタンを押すとエラーが出ます。 エラー文は 【NoMethodError in RelationshipsController#create】 undefined method `set_user' for #<RelationshipsController:0x00007fb25ec77938> Did you mean? -------------------------------- set_request! lambda do |target, value, &block| target, block, method, *arguments = expand(target, value, block) target.send(method, *arguments, &block) end end ーーーーーーー Parameters: {"authenticity_token"=>"7XgeZEMRhzfQ2BzmTCMuRJFVCwpOMK/U+ZwJWfU/E1WM9cZRQU/+j6WjmV4nnpzaGIHgEbJKZ9xQrJyEgEW1bA==", "follow_id"=>"1", "commit"=>"Follow"} と表示されていて、その中の「target.send(method, *arguments, &block)」の部分が強調されている状態です。
winterboum

2020/10/31 07:21

target.send(method, *arguments, &block) はどのfileにあります?
kema

2020/10/31 07:54

行き違いになっていたらすいませんが念の為に送ります。 fileの場所が通常のエラー文の中では判別がつかなかったのでbetter_errorsを導入してみたのですが、 activesupport (6.0.3.4) lib/active_support/callbacks.rbの428行目が先程のコメントに書いたエラーの部分です。 念の為にターミナルのログの中身も載せておきます。 => Booting Puma => Rails 6.0.3.4 application starting in development => Run `rails server --help` for more startup options Puma starting in single mode... * Version 3.12.6 (ruby 2.6.5-p114), codename: Llamas in Pajamas * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://localhost:3000 Use Ctrl-C to stop Started POST "/relationships" for ::1 at 2020-10-31 16:49:16 +0900 (0.3ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483 (0.4ms) SELECT `schema_migrations`.`version` FROM `schema_migrations` ORDER BY `schema_migrations`.`version` ASC NameError - undefined local variable or method `params' for RelationshipsController:Class: app/controllers/relationships_controller.rb:27:in `<class:RelationshipsController>' app/controllers/relationships_controller.rb:1:in `<main>' Started POST "/__better_errors/b6e6137b4217991e/variables" for ::1 at 2020-10-31 16:49:16 +0900 Started POST "/relationships" for ::1 at 2020-10-31 16:49:41 +0900 Processing by RelationshipsController#create as HTML Parameters: {"authenticity_token"=>"VB9yhigO9SHlMnGpOJ9oVaDN/eE61CmfDGHYpJBciYw1kqqzKlCMmZBJ9BFTItrLKRkW+sau4ZelUU155SYvtQ==", "follow_id"=>"1", "commit"=>"Follow"} Completed 500 Internal Server Error in 241ms (ActiveRecord: 0.0ms | Allocations: 81819) NoMethodError - undefined method `set_user' for #<RelationshipsController:0x00007f930edbdd20> Did you mean? set_request!: Started POST "/__better_errors/160c18b082f62335/variables" for ::1 at 2020-10-31 16:49:41 +0900
winterboum

2020/10/31 08:15

回答の追記試して
kema

2020/10/31 08:26 編集

解決致しました。質問の方も編集して修正致しました。 長い時間お付きあいして頂き本当にありがとうございました。最後は本当にシンプルな記述ミスだったのでお恥ずかしい限りです。 でも一人で考えていたらもっと堂々巡りしていたと思うので本当に感謝しています! ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問