見出し画像

Rails まとめ④

・ライブラリ
複雑なプログラムを一つのセットにしたもの。
Rubyのライブラリは、RubyGemsと呼ぶ。

・RubyGems
Rubyのライブラリの総称。

・gem
RubyGemsにはたくさんのライブラリが含まれる、それぞれをgemと言う。
RailsもRubyのgemの一つ。Railsの中でもgemを使用できる。
Railsアプリケーション内で使用するgemはGemfileに記載して管理。
それらのgemはbundlerによって管理されている。

・Gemfile ファイル
アプリケーション内で使用したいgemを記載。

・Gemfile.lock ファイル
bundle install済みのバージョンを記録しておく場所。

・bundler
複雑なgem管理を行ってくれるのが、bundlerというgem。

・bundle install コマンド
Gemfileに記載されているgemを、アプリに適用するためのコマンド。
インストールするバージョンはGemfile.lockに従う。

・bundle update コマンド
最新のバージョンであるgemをインストールするコマンド。
他のgemとの互換性のために、最新のバージョンを適用したくない
gemがある場合は、Gemfileでバージョンを指定する。
【例】 Gemfile
# mysql2は、0.4.4以上、0.6.0未満のバージョンを適用するという意味。
gem 'mysql2', '>= 0.4.4', '< 0.6.0'

ユーザー管理機能gem

①devise
ユーザー管理機能を簡単に実装するためのgem。

Gemfile を編集。

# 最終行に追記。
gem 'devise'  

②gemをインストール

ターミナル

# ディレクトリにいることを確認。
$ pwd  

# Gemfileに追記したdeviseをインストール
$ bundle install  

③deviseの適用

ターミナル

# deviseの設定ファイルを作成
$ rails g devise:install
新規作成されるファイル
・config/initializers/devise.rb
・config/locales/devise.en.yml

deviseの機能を持ったUserモデルを作成

通常のモデルの作成方法ではなく、deviseのモデルの作成用コマンドで
Userモデルを作成する。

①userモデルを作成

ターミナル

# deviseコマンドでモデルを作成
$ rails g devise user
新規作成されるファイル
・app/models/user.rb
・db/migrate/20XXXXXXXXXXXX_devise_create_users.rb
・test/fixtures/users.yml
・test/models/user_test.rb

②rails db:migrateを実行

ターミナル
	
# 作成されたマイグレーションファイルを実行
$ rails db:migrate

SequelProを開き、usersテーブルが作成されているか確認。

新規登録・ログインの実装

未ログイン時は、画像のようなログインボタンと新規登録ボタンを表示する。新規投稿はログインしたユーザーにする。新規投稿ボタンは削除。

app/views/layouts/application.html.erb を編集。

     <div class="header__right">
       <%= link_to "ログイン", new_user_session_path, class: "header__right--btn" %>
       <%= link_to "新規登録", new_user_registration_path, class: "header__right--btn" %>
     </div>
・user_signed_in? メソッド
deviseを実装すると、user_signed_in?メソッドを使用できる。ユーザーが
サインインしているかどうか検証するメソッド。サインイン時にはtrueを
返しサインインしていない場合にはfalseを返す。
【例】
<% if user_signed_in? %>
 # ユーザーがサインインしている場合に実行する処理
<% end %>
【例】deviseによって設定されるPrefixの一部
リクエスト                           Prefix                                  パス
devise/sessions#new          new_user_session             /users/sign_in
devise/sessions#create      user_session                     /users/sign_in
devise/sessions#destroy    destroy_user_session      /users/sign_out

①新規投稿ボタンとログアウトボタンを設置

app/views/layouts/application.html.erb を編集。

     <div class="header__right">
       <% if user_signed_in? %>
         <%= link_to "新規投稿", new_post_path, class: "header__right--btn" %>
         <%= link_to "ログアウト", destroy_user_session_path, method: :delete, class: "header__right--btn" %>
       <% else %>
         <%= link_to "ログイン", new_user_session_path, class: "header__right--btn" %>
         <%= link_to "新規登録", new_user_registration_path, class: "header__right--btn" %>
       <% end %>
     </div>

②devise用のビューを作成

ターミナル

$ rails g devise:views

ブログ情報とユーザー情報の紐付け

・アソシエーション
アソシエーションの定義で、モデル間を紐付けることができる。

・rails g migration コマンド
データベースのテーブルの情報を追加・削除・変更するために、
マイグレーションファイルを生成するコマンド。
【例】ターミナル

# rails g migration Addカラム名To追加先テーブル名 追加するカラム名:型
# usersテーブルにintroductionカラムをtext型で追加するマイグレーションファイルの作成。
$ rails g migration AddIntroductionToUsers introduction:text
〜プログラムが単語の区切りを認識する記述方法〜
・スネークケース
「_」で単語を区切る方法
・キャメルケース
単語の頭文字を大文字にすることで単語を区切る方法
ターミナル

# postsテーブルにuser_idカラムをinteger型で追加。
$ rails g migration AddUserIdToPosts user_id:integer

# マイグレーションファイルの実行
$ rails db:migrate

モデル間の紐付け
① モデルクラスにhas_manyやbelongs_toなどのメソッドで関係が定義
② 所属する側のテーブルに所属するモデル名_idというカラムがある
②の実装に関しては先ほどuser_idというカラムをpostsテーブルに追加し完了した

・has_many メソッド
Userモデルの視点。ユーザーは複数の投稿が可能。一人のユーザーは複数の投稿を所有している。この状態をhas manyの関係といい、「User has many Posts」の状態と言えます。「1対多」のつながりを示す。
app/models/user.rb を編集。

class User < ApplicationRecord
 # Include default devise modules. Others available are:
 # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
 devise :database_authenticatable, :registerable,
        :recoverable, :rememberable, :validatable

 has_many :posts
end
・belongs_to メソッド
Postモデルの視点。1つの投稿を複数人が投稿できない。投稿は必ず1人のユーザーに所属する。この状態をbelongs toの関係といい、「Posts belongs to User」の状態と言えます。「1対1」のつながりがあることを示す。
app/models/post.rb を編集。

class Post < ApplicationRecord
 validates :title, :content, presence: true
 belongs_to :user
end

記事保存時にuser_idも登録できるように設定

・current_user メソッド
ログイン中のユーザー情報を取得するdeviseのヘルパーメソッド。

・merge メソッド
2つのハッシュを結合できる。
app/controllers/posts_controller.rb を編集。


private
def post_params
  params.require(:post).permit(:title, :content).merge(user_id: current_user.id)
end

ブログの投稿者がわかるようにする

# usersテーブルにnicknameカラムをstring型で追加するマイグレーションファイルを作成
$ rails g migration AddNicknameToUsers nickname:string

# マイグレーションファイルの実行
$ rails db:migrate
app/views/devise/registrations/new.html.erb を編集。

<h2>Sign up</h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
 <%= render "devise/shared/error_messages", resource: resource %>

 <div class="field">
   <%= f.label :nickname %><br />
   <%= f.text_field :nickname, autofocus: true %>
 </div>
・configure_permitted_parameters メソッド
deviseでは初期状態でサインアップ時にメールアドレスとパスワードのみを受け取るようにストロングパラメーターが設定してあるので、追加したキー(nickname)のパラメーターは許可されていません。
app/controllers/application_controller.rb を編集。

 before_action :configure_permitted_parameters, if: :devise_controller?

 protected
 def configure_permitted_parameters
   devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname])
 end
end

①Sequel Proのnicknameカラムに直接ニックネームを入力。

②ビューファイル編集

app/views/posts/index.html.erb 

     <div class="content__right__bottom">
       <div class="content__right__bottom--userName">
         <%= post.user.nickname %>
       </div>
       <div class="content__right__bottom--date">
         <%= post.created_at %>
       </div>
     </div>
・N+1問題
処理の中で都度SQLが発行され、大量のSQLが発行されてパフォーマンスが低下してしまう問題。SQLは簡単に説明するとモデルを利用してデータベースの情報にアクセスする際に発行される命令文です。SQL発行毎にデータベースに通信され、SQLが大量に発行されれば処理が重くなる。
現状だと「全投稿を取得する1回」+「投稿に紐付くユーザーの数」だけSQLが発行される。解決するためにユーザー情報を先読みするように実装する。

・includes メソッド

【例】
# アソシエーションを組んだ際はincludesメソッドを利用して対策する。
モデル名.includes(:紐付くモデル名)
app/controllers/posts_controller.rb

 def index
   @posts = Post.includes(:user)
 end

マイページ実装

①ルーティングを記述

config/routes.rb を編集。

Rails.application.routes.draw do
 devise_for :users
 root to: 'posts#index'
 resources :posts, except: :index
 resources :users, only: :show
end

②コントローラーとアクションを作成

ターミナル

# users_controllerと関連するファイルを作成
$ rails g controller users
・新規作成されるファイル
app/controllers/users_controller.rb
test/controllers/users_controller_test.rb
app/helpers/users_helper.rb
app/assets/javascripts/users.js.coffee
app/assets/stylesheets/users.css.scss

③users_controller.rbを編集

app/controllers/users_controller.rb を編集。

class UsersController < ApplicationController
 def show
   @user = User.find(params[:id])
   @posts = @user.posts
 end
end

④マイページ用のビューファイルを作成

app/views/users/show.html.erb を作成、編集。

<div class="postTitle">
 <%= @user.nickname %>さんの投稿一覧
</div>
<% @posts.each do |post| %>
 <div class="content">
   <div class="content__left">
     <div class="content__left--image"></div>
   </div>
   <div class="content__right">
     <div class="content__right__top">
       <%= link_to post.title, post_path(post.id), class: "content__right__top--title" %>
     </div>
     <div class="content__right__bottom">
       <div class="content__right__bottom--userName">
         <%= link_to post.user.nickname, user_path(post.user), class: "content__right__bottom--userName--btn" %>
       </div>
       <div class="content__right__bottom--date">
         <%= post.created_at %>
       </div>
     </div>
   </div>
 </div>
<% end %>

⑤マイページへのリンクを作成

app/views/layouts/application.html.erb を編集。

   <div class="header__right">
   <% if user_signed_in? %>
       <%= link_to current_user.nickname, user_path(current_user), class: "header__right--btn" %>
       <%= link_to "新規投稿", new_post_path, class: "header__right--btn" %>
       <%= link_to "ログアウト", destroy_user_session_path, method: :delete, class: "header__right--btn" %>
   <% else %>

⑥一覧表示の投稿者名からマイページにアクセスする


app/views/posts/index.html.erb を編集。

       <div class="content__right__bottom--userName">
         <%= link_to post.user.nickname, user_path(post.user), class: "content__right__bottom--userName--btn" %>
       </div>

編集・削除機能へのユーザー制限

条件分岐を使って制限を実装
ログイン状態か判断する実装で<% if user_signed_in? %>を利用したが、
これはログイン状態のユーザーが他人の投稿も編集・削除が可能。
「ユーザーがログインしており、ログイン中のユーザーと投稿したユーザーが一致している」条件でのみ機能が使用できるよう実装。

【例】
# 「ユーザーがログインしている」かつ「ログインしているユーザーと投稿したユーザーが一致している」
if user_signed_in? && current_user.id == @post.user_id
app/views/posts/show.html.erb 

<div class="postAuthor">
</div>
<% if user_signed_in? && current_user.id == @post.user_id %>
 <div class="postManage">
   <%= link_to "編集", edit_post_path(@post.id), class: "postManage__edit" %>
   <%= link_to "削除", post_path(@post.id), method: :delete, class: "postManage__delete" %>
 </div>
<% end %>
<div class="postText">
 <%= simple_format @post.content %>
</div>

部分テンプレートの実装

①データを表示するまとまりをテンプレート化
同じHTML構造の部分を共通化し、無駄なくビューファイルを作成する。
部分テンプレートのファイル名は必ずアンダーバー「_」から始める。

app/views/posts/index.html.erb を編集。

<% @posts.each do |post| %>
 <div class="content">
   <div class="content__left">
     <div class="content__left--image"></div>
   </div>
   <div class="content__right">
     <div class="content__right__top">
       <%= link_to post.title, post_path(post.id), class: "content__right__top--title" %>
     </div>
     <div class="content__right__bottom">
       <div class="content__right__bottom--userName">
         <%= link_to post.user.nickname, user_path(post.user), class: "content__right__bottom--userName--btn" %>
       </div>
       <div class="content__right__bottom--date">
         <%= post.created_at %>
       </div>
     </div>
   </div>
 </div>
<% end %>
app/views/posts/_post.html.erb を作成、編集

<div class="content">
 <div class="content__left">
   <div class="content__left--image"></div>
 </div>
 <div class="content__right">
   <div class="content__right__top">
     <%= link_to post.title, post_path(post.id), class: "content__right__top--title" %>
   </div>
   <div class="content__right__bottom">
     <div class="content__right__bottom--userName">
       <%= link_to post.user.nickname, user_path(post.user), class: "content__right__bottom--userName--btn" %>
     </div>
     <div class="content__right__bottom--date">
       <%= post.created_at %>
     </div>
   </div>
 </div>
</div>​
・render メソッド
部分テンプレートを呼び出す際に利用するメソッド

・partial オプション
renderメソッドで使用できるオプション。明示的に部分テンプレート名を指定し、部分テンプレートを表示できる。

・locals オプション
renderメソッドで使用できるオプション。部分テンプレート内でその変数を使える。
【例】
_sample.html.erbという部分テンプレートを呼ぶ。
<% render partial: "sample" %>

【例】
hello!という文字列の代入されたpostという変数が使える
<% render partial: "sample", locals: { post: "hello!" } %>
app/views/posts/index.html.erb を編集。

<% @posts.each do |post| %>
 <%= render partial: "post", locals: { post: post } %>
<% end %>

このrenderメソッドのlocalsオプションに注目。{ post: post }の右側のpostはeachメソッド内の変数としてのpostでpostのインスタンスを示す。
左側のpostは部分テンプレート内での変数の名前を表しています。

②users/show.html.erbを編集

app/views/users/show.html.erb を編集。

<div class="postTitle">
 <%= @user.nickname %>さんの投稿一覧
</div>
<% @posts.each do |post| %>
 <%= render partial: "posts/post", locals: { post: post } %>
<% end %>

デバッグをするためのツール

・pry-rails
Rails向けに開発されたデバッグツールを提供するgem。作業の際にバグの有無を確認したり、処理を止めてコードが正しいかを確認するツール。
binding.pry  をコード内に記述すると、binding.pryの部分で処理が止まる。
Gemfile に追加。

# 省略
gem 'pry-rails'
ターミナル

# gemをインストール
$ bundle install
app/controllers/posts_controller.rb を編集。

 def create
   binding.pry 
   Post.create(post_params)
   redirect_to root_path
 end

①新規投稿し処理を止める。

ターミナル

# 10の矢印の部分で処理が止まっている。
    9: def create
=> 10:   binding.pry
   11:   Post.create(post_params)
   12:   redirect_to root_path
   13: end

# post_paramsを>の後に入力すると中に入っているデータを確認できる。exitで終了する。
[1] pry(#<PostsController>)> 


この記事が気に入ったらサポートをしてみませんか?