ユーザと記事を紐付け
データベースのリレーション
リレーションとは
TWitterのような場合
記事とユーザーが紐づいている関係、ユーザとフォロワーなど
データがつながっている
どうやって繋げるのか、usersテーブル(ユーザー情報)があるとします。
はたまた、articlesテーブル(投稿情報)があります。
articlesテーブルの中に、use_idといいう項目を追加する。
その中に、この記事は、id1の人のも、この記事は、id2の人のものと分ける。
ということで新しいカラムを追加するには??
まずは、マイグレーションファイルを作る必要があります!!
作るには、ターミナルを開き
$rails g migration AddUserIdToArticles
UserIdをArticlesに追加するとなります。
db/migrate/20230226221434_add_user_id_to_articles.rb
ここができています。
db/schema.rb
の中に、create_table "articles"と記載しているので、
articlesテーブルあることを確認できます。
db/migrate/20230226221434_add_user_id_to_articles.rb
こちらに
add_column :articles, :user_id, :integer
と追加、整数(型)でuser_idをarticlesに追加した。
しかしこれはあんまり良くない😭ただただ、use_idというカラムを作れば良いわけではない。
下記の方が望ましい!
add_reference :articles, :user
railsで用意されているものがある。
こっちを使用した方が検索しやすくなったりする。パフォーマンスを上げるためには、こちらの方が良い!!
※userと書くと、user_idという意味になる。
これで、マイグレーションファイルが作成できたので、マイグレーションを実行していきます👍
ターミナルに行き
$rails db:migrate
を実行!
dbが更新されました!
db/schema.rbで確認してみましょう!
create_table "articles"の中に、t.integer "user_id"が追加されているのが確認できます🙆
app/models/article.rbを見ても、annotate(メモ機能)に、user_id :integerが追加されていることがわかります。
まだこのままだと使えない!
SQLを発行するには、アクティブレコードなので、アクティブレコード上、モデルの中にも色々設定を書かないといけない。
現状は、db上では紐づいているが、SQLを発行するアクティブレコードに対して、設定をできていない状況。
app/models/user.rb
ここで、ユーザーと記事が紐づいているということをアクティブレコードに教える。
has_many :articles
こうするとユーザーは記事をたくさん持っている。
ユーザーがいてその下に、記事がいっぱい紐づいているとrailsは理解する。
ポイントは、複数形になっていること。
has_many ときたら、複数形にしなければいいけない!!
articlesは、articleモデルのことを表している。
(ユーザーから見ると、記事は複数あるので複数形)
app/models/article.rb
この記事はユーザーに紐づいている!という環境を作りたい。
belongs_to :user
belongs 意味:紐づいている。所属している。
記事は、ユーザーの下にあるという意味。
belongs_toの場合は、単数形でおっけい🙆
(記事から見るとユーザーは、一人しか紐づいていないので単数形)
app/models/user.rb
先ほどの、続きに追加
dependent: :destroy
ユーザーが削除されたときに、articlesも全て削除されるよ!という意味。
これで、ユーザーとarticleが紐付きました👏👏
ターミナルでユーザに紐づく記事を作成したり取得
$rails c
railsコンソール立ち上げ
$user = User.first
適当にユーザーを取ってくる。
このユーザーに紐づいた記事を作成していきます!
$user.articles.create!(title: 'useruseruser', content: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
100文字以上に設定してあるのでたくさん!!これで、記事作成👍
今までは、article.createだったのが、user.articles.createになっただけ!!
基本的な考え方は同じ!
ユーザーに紐づいている記事一覧をひろうには?
$user.articles
これを実行👍
$Article.last
先ほどのやつを見てみると、ちゃんとユーザーidが取れているのを確認できます!
user_id: 2
$Article.last.user
とやると記事から、ユーザー情報を取得できる。
このように、アクティブレコードにhas_manyとbelongs_to色々なメソッドができる!
これが、基本的な考え方。
フォームからユーザが記事を作成
app/controllers/articles_controller.rb newとcreateの修正 Article.newを消し、current_user.articles.build
current_user=今ログインしているユーザーの値をとれる。
.buildは.newと役割は基本同じ
ターミナルで確認
$User.first.articles.build
idは2と入っている。他は、nil。という空箱ができている。
create部分も変更
Article.newを消し、current_user.articles.buildを変更。
☆補足
scssにて、下記を読み込まないと
@import './variables';
$primary-colorなどは、読み込むことができない!!!
この状態で、記事を投稿!
コンソールで確認
$Article.last
最後の記事を確認
$Article.last.user
でid2とあるので、ユーザーと記事が紐づいていることが、確認取れました👍
また、
$User.last
とし
$User.last.articles
見れば、今までの記事を確認することができる。
記事の編集と更新
editとupdateをやっていきます !app/controllers/articles_controller.rb
まず、現在set_articleは、いらなくなるので消す。
上部のeditとupdate削除!
次にedit部分に
@article = current_user.articles.find(params[:id])
current_userから、アーティクルを探すにしないといけない!!!
そうしないとセキュリティに問題があります!!
@article = Articles.find(params[:id])
としてたとすると、他人の記事も編集できてしまう!!
idがわかれば編集できてしまう💦
current_user.articlesとしておくことで、自分の記事しか取得できない編集できない!
updateも同じ!
@article = current_user.articles.find(params[:id])
とやると、編集することができます👍
記事の削除
destroyArticle.find(params[:id])
現在は、こうなっているので、全ての記事を削除することができてしまう!!自分の記事じゃなくても消せる(^^;;
current_user.articles.find(params[:id])
今までと同じで、こうすれば良い👍
他の人が、記事の削除だったりをできないようにすることを、常に気をつけましょう😆
この状態で、削除試すとちゃんと実行できますね😂
自分が書いた記事かどうか判定
自分が書いた記事であれば、編集ボタンや削除ボタンを表示するように変更
app/views/articles/show.html.haml
ユーザーがサインインしている且つ自分の記事ですよという条件にする。
current_userは、ここでも使用可🙆
current_user.articles.exists?(id: @article.id)
自分の書いた記事の中に@articleが含まれていれば、自分が書いたことになる。
exists?は、
@articleのidに合致するやつが、articlesの中に存在するかしないか?の条件を判断してくれる!
- if user_signed_in? && current_user.articles.exists?(id: @article.id)
こうすることで、ユーザーがサインインしている且つ自分の記事ですよと判断することができる!!
これで、確認してみると自分の書いた記事のみ、編集・削除が可能ということがわかる!!
current_user.articles.exists?(id: @article.id)
現在この状態だと、ぱっと見で何をしているのかわかりづらい!!
app/models/user.rb
def has_written?(article)
articles.exists?(id: article.id)
end
has_written?
あなた書きました?という意味
has_written?(article)
この記事書きました??
articles.exists?(id: article.id)
自分の書いた記事の中に、引数のアーティクルがあるかないかを、判断する。
app/views/articles/show.html.haml
ここに戻り変更!
current_user.has_written?(@article)
こうすることで、英語さえ読めれば何をしているかは判別することができる👍👍
複雑なものは、モデルの中に隠してしまう!
メソッドを定義して、あとはそれを使うだけ!!
ユーザー名を表示
app/models/user.rb 現在Eメールの情報しかない。アカウントのidのようなものがないので、これを代用して作っていきます!!仮に、Eメールアドレスの頭の部分を取ってきて、アカウントidにしてみます!
def display_name
self.email.split('@').first
end
display_nameと定義し、self.emailとすれば、Eメールを取得できる。
split('@')は、@で、分割される!
〇〇@gmail.com
だとすると、 => ('〇〇', 'gmail.com')このように分割される。
文字列が、あったときにスプリットの後ろ指定した文字と一致していれば、そこを基準に前半と後半で分かれる。
このように、わかれた配列を取得することができる。
first一つ目の文字列を取得。
app/views/articles/show.html.haml
適当に書いてある名前部分を変更。
%p= @article.user.display_name
に変更
これで、基本的に良いが、初めに作った、ユーザーがない投稿
ユーザー機能を作る前のもの
このままだと、ユーザーがないので、おかしくなってしまう!!
- if @article.user.present?
これを先ほどの前に記入。
存在する場合、表示する!
app/views/articles/index.html.haml
インデックスも変更
現在は、 = render 'article', article: articleでidを取得している。
app/views/articles/_article.html.haml
まずこちらから、
%p article.user.display_name
に変更。
このままだとエラーが発生。
undefined method `display_name' for nil:NilClass
displaynameがありませんよとなっています。
display_nameは、userのインスタンスでないと使えない。
現在は、
article.user
=> nil
中身が何もない状態。
なので、
article.user.display_nameにすると
nil.display_nameと同じである。
なので、ノーメソッドエラーとなる。
`display_name' for nil:NilClass>
nilクラスに対して、display_name はありませんよとなる。
よって、エラーの原因はnilとなる。
- if %article.user.present?
存在していれば、表示する。
article_userがいてくれれば、nilになることはない。