Rails 総復習1ヶ月チャレンジ 6日目(Railsチュートリアル編)

チャレンジ6日目です!Railsチュートリアルが思った以上に内容が濃くて当初の予定より押してしまっているのですが、しっかり覚える必要があるので手を抜かずにやっていきたいと思います。  

また今回から全ての手順をやるとブログで1日が終わってしまうので基本的に学んだポイントや重要だと思ったところに絞って書いていこうと思います。(今更ながら)

6日目(Ruby on Rails チュートリアル 13章ユーザーのマイクロポスト)   

マイグレーションの複合キーインデックス

チュートリアルだとこのような形で実装がありました。

class CreateMicroposts < ActiveRecord::Migration[6.0]
  def change
    create_table :microposts do |t|
      t.text :content
      t.references :user, foreign_key: true

      t.timestamps
    end
    add_index :microposts, [:user_id, :created_at]
  end
end

この部分

add_index :microposts, [:user_id, :created_at]

今まで何度もこのような記述を見て何となく付けると良いのかな?と思っていましたがちゃんと調べることにしました。

(参考)stackoverflow.com

上記参考のAnswerより

複数列のインデックスは、クエリが複数の列に対する条件を含む場合に役立ちます。

中略

インデックスの2番目の列がなければ、RDBMSはuser_id 123のすべての行をメモリにロードしてから、その行が基準を満たすかどうかを判断しなければなりません。

なるほど要するに無駄にクエリを走らせず、高速化させるために付けているというわけですね。

newではなく何故build?

これも今まで見かけてたけどnewではなくbuild使っているのは何故?というのがありました。

#app/controllers/microposts_controller.rb

@micropost = current_user.microposts.build(micropost_params)  

これも調べました。ただnewとbuildの明確な違いは分からず、チュートリアルの動画教材を視聴して見ると、動画内で安川さんが

”関連付けをベースにしてインスタンス生成しているのが分かりやすくしている”

という内容を発言されており、そういうものなのかと納得しました。

default_scope

データベースから要素を取得したときの、デフォルトの順序を指定するメソッド。scopeの一種。

class Micropost < ApplicationRecord
  :
  default_scope -> { order(created_at: :desc) }
  :
end

上記の例ではMicropostクラスのオブジェクトデータを取得した時にデフォルトで新しい順(DESC)になるようにしています。Railsガイドによると、デフォルトスコープを適用する上記の->部分は省略出来そうです。
ちなみにこの中ではrubyの機能のProcが使われております。これは次で解説します。

Procオブジェクト

Rubyの機能。ブロックと似ているようですがProcはコードの塊を変数に入れて、後からcallを使い呼び出すことが出来る。 参考:プロを目指す人の為のRuby入門P374

#例  
p = -> { print 'foo' }
=> #<Proc:0x00007fedcccfbb30 (irb):1 (lambda)>
> p.call
foo=> nil

パーシャルのエラーメッセージを汎用性高く使いたい

<%= form_with model: @user, local: true  do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
:
<% end %>

objectを使うと、フォームを作る時に参照したデータを引っ張ってこれる。

object: f.object

することで呼び出したパーシャルの先で渡したデータをローカル変数(今回はobject)として扱うことが出来る。
パーシャルではこのように記述します。

#app/views/shared/_error_messages.html.erb
<% if object.errors.any? %>
  <div id="error_explanation">
    <div class="alert alert-danger">
      The form contains <%= pluralize(object.errors.count, "error") %>.
    </div>
    <ul>
    <% object.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
<% end %>

SQLインジェクションを避ける

SQLインジェクション

SQLインジェクション(英: SQL Injection)とは、アプリケーションのセキュリティ上の不備を意図的に利用し、アプリケーションが想定しないSQL文を実行させることにより、データベースシステムを不正に操作する攻撃方法のこと。また、その攻撃を可能とする脆弱性のこと

#app/models/user.rb

class User < ApplicationRecord
  .
  .
  .
  # 試作feedの定義
  # 完全な実装は次章の「ユーザーをフォローする」を参照
  def feed
    Micropost.where("user_id = ?", id)
  end

    private
    .
    .
    .
end

この部分

Micropost.where("user_id = ?", id)

プレースホルダー(疑問符)があることによってSQLクエリに代入する前にidがエスケープされてSQLインジェクションを防ぐことができます。
ちなみにこれは下記と同じ書き方です。

Micropost..where("user_id = id")
Micropost..where(user_id: id)  

参考:【Rails】 whereメソッドを使って欲しいデータの取得をしよう!

request.refereer

マイクロポストを削除するした後のリダイレクト先指定に使われています。

#app/controllers/microposts_controller.rb

class MicropostsController < ApplicationController
  .
  .
  def destroy
    @micropost.destroy
    flash[:success] = "Micropost deleted"
    redirect_to request.referrer || root_url
  end
  .
  .
end

この部分

redirect_to request.referrer || root_url

request.referrerというメソッドは、一つ前のURLを返します。
マイクロポストがHomeページ、もしくはプロフィールページどちらから削除されたとしてもDELETEメソッドが発行されたページに戻すことができます。万が一元に戻すURLがなかった場合に備えて|| root_url でデフォルトを設定しています。 requestについてはAction Controller の概要 - Railsガイド
referrerについては
Referer - HTTP | MDN

今回は以上です!