Rails チュートリアル following followerの関係について
この画面の左側のfollowing followerの数はどのようにして表示されているか?を説明します。
とりあえずアドレスがusers/4なのでUsersControllerのshowが呼ばれているはず
UsersController
def show @user = User.find(params[:id]) @microposts = @user.microposts.paginate(page: params[:page]) end
userのidを取得
Viewの左画面
<aside class="col-md-4"> <section> <%= render 'shared/user_info' %> </section> <section> <%= render 'shared/stats' %> </section> <section> <%= render 'shared/micropost_form' %> </section> </aside> <div class="col-md-8"> <h3>Micropost Feed</h3> <%= render 'shared/feed' %> </div>
真中部分のここが数字を表示している。
<%= render 'shared/stats' %>
'shared/stats'が呼ばれている
Viewの中のパーシャル(部分テンプレート)を見て見ます。
_shared/stats
<% @user ||= current_user %> <div class="stats"> <a href="<%= following_user_path(@user) %>"> <strong id="following" class="stat"> <%= @user.followed_users.count %> </strong> following </a> <a href="<%= followers_user_path(@user) %>"> <strong id="followers" class="stat"> <%= @user.followers.count %> </strong> followers </a> </div>
<%= @user.followed_users.count %>で数字を表示
followed_usersはどこから来たのか?このブログのポイント
Userモデルに関連を宣言
class User < ApplicationRecord has_many :microposts, dependent: :destroy has_many :relationships, foreign_key: "follower_id", dependent: :destroy has_many :followed_users, through: :relationships, source: :followed
最後の行has_many :followed_users, through: :relationships, source: :followed
この行がポイント Userモデルはfollowed_usersという、プロパティを持つようになる。 それはrelationshipsテーブルを用いて関連付けられる。
そしてfollowedモデルを所有できるようになります。
followedモデルって何?
どこにも書いてないよ?
userテーブルのどこにもその様なカラムはない。
ないのになぜ、アクセスできるのか?
create_table "users", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" t.integer "sign_in_count", default: 0, null: false t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" t.string "last_sign_in_ip" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "name" t.boolean "admin", default: false t.index ["email"], name: "index_users_on_email", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
Relationshipのモデル
followedというテーブルと関連づいているように見える。しかし実態はUser
class Relationship < ApplicationRecord belongs_to :follower , class_name: "User" belongs_to :followed , class_name: "User" validates :follower_id, presence: true validates :followed_id, presence: true end
has_many :followed_users, through: :relationships, source: :followed つまりこれは
relationshipsという中間テーブルを通して、followedを所有することになります。
少し難しいかも?
ポイントは、follower followedも同じuserテーブルを参照していることにあります。
自分自身のテーブルを参照しているのですね。
2つのユーザーが同名の外部キーを持つことはできません。
それで、何らかの方法でkeyの名前を変更しないといけない。
followerとしてフォローするのと
followedとしてフォローされているものは違う2つの値をそれぞれ持つのです。
routesファイルを見てみます。
Rails.application.routes.draw do devise_for :users, :controllers => { :registrations => "registrations" } resources :users, only: [:show, :index, :destroy] do member do get :following, :followers end end
これによりルーティングにより下記のrouteが定義されます。
画像参照元 Ruby on Rails チュートリアル:実例を使って Rails を学ぼう
なぜこれで行けるかというと、
Userコントローラーfollowingメソッドにより、followed_idカラムを取得しています。
def following?(other_user) relationships.find_by(followed_id: other_user.id) end def follow!(other_user) relationships.create!(followed_id: other_user.id) end
これにより、取得できるのですね。
Relationshipテーブルにはfollowed_idというカラムがあります。
また、余談ですがindexも設定してあります。
create_table "relationships", force: :cascade do |t| t.integer "follower_id" t.integer "followed_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["followed_id"], name: "index_relationships_on_followed_id" t.index ["follower_id", "followed_id"], name: "index_relationships_on_follower_id_and_followed_id", unique: true t.index ["follower_id"], name: "index_relationships_on_follower_id" end
インデックスを簡単に言うと対象のカラムのデータを取り出し、高速に検索できるように手を加えて保存しておいたものです
何度も頻繁に使うカラムだから、高速で検索できるようにしておいたのですね。
マイグレーションファイル生成時に設定しています。
class CreateRelationships < ActiveRecord::Migration[5.1] def change create_table :relationships do |t| t.integer :follower_id t.integer :followed_id t.timestamps end add_index :relationships, :follower_id add_index :relationships, :followed_id add_index :relationships, [:follower_id, :followed_id], unique: true end end
ここで
add_index :relationships, [:follower_id, :followed_id], unique: true
とあるので、あるユーザーが同じユーザーを複数回フォローすることを防ぐ事ができます。
流れをまとめると
UsersControllerのshowが呼ばれる
Viewには<%= @user.followed_users.count %>とある
followed_usersはどこから来たのかというと
Userモデルに関連付けがある。
has_many :followed_users, through: :relationships, source: :followed
Userモデルはfollowed_usersにアクセスできる。 それはrelationshipsテーブルを用いて関連付けられる。
そしてfollowedモデルを所有できるようになります。
relationshipsモデル。 実態はUserモデルとなっている。
belongs_to :followed , class_name: "User"
Userコントローラーの関数にて取得できる。
def following?(other_user) relationships.find_by(followed_id: other_user.id) end
ソースをきちんと読むのは大切ということがわかった。 間違えている点がありましたら、ご指導よろしくおねがいします。