学生向けプログラミング入門 | 無料

学生向けにプログラミングを無料で解説。Java、C++、Ruby、PHP、データベース、Ruby on Rails, Python, Django

【民泊5.1】【Windows】電話番号認証

まずTwilioでアカウント登録をしてください。
www.twilio.com


アカウントの登録は以下の手順でお願いします。
mrradiology.hatenablog.jp


記述追加 GemFile
81行目に「gem 'twilio-ruby', '~> 4.11.1'」の記述追加

source 'https://rubygems.org'

git_source(:github) do |repo_name|
  repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
  "https://github.com/#{repo_name}.git"
end


# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.0.7', '>= 5.0.7.1'
# Use postgresql as the database for Active Record
gem 'pg', '>= 0.18', '< 2.0'
# Use Puma as the app server
gem 'puma', '~> 3.0'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# Use CoffeeScript for .coffee assets and views
gem 'coffee-rails', '~> 4.2'
# See https://github.com/rails/execjs#readme for more supported runtimes
# gem 'therubyracer', platforms: :ruby

# Use jquery as the JavaScript library
gem 'jquery-rails'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 3.0'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platform: :mri
end

group :development do
  # Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
  gem 'web-console', '>= 3.3.0'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

#bootstrap
gem 'bootstrap-sass', '~> 3.4.1'

#デバイス
gem 'devise', '~>4.2'

#Gravatar
gem 'gravtastic'

#フラッシュメッセージ
gem 'toastr-rails', '~> 1.0'

#日本語化
gem 'rails-i18n'

#画像アップロード
gem 'paperclip', '~> 5.1.0'

#googleマップ
gem 'geocoder', '~> 1.4'

#アマゾンS3
gem 'aws-sdk', '~> 2.8'

#日付ピッカー
gem 'jquery-ui-rails', '~> 5.0'

# 検索
gem 'ransack', '~> 1.7'

# 電話番号認証
gem 'twilio-ruby', '~> 4.11.1'



コマンド
bundle


「config\initializers」フォルダに「twilio.rb」ファイルを新規作成してください。


config\initializers\twilio.rb(新規作成したファイル)
ご自分のアカウントSIDとAUTHTOKENを入れて下さい。

Twilio.configure do |config|
    config.account_sid = 'AC6c3786554525790e777ea3bca592a70e'
    config.auth_token = 'ae5debedfafff4ddd84634f6d505edd1'
end



コマンド
rails g migration AddPinAndPhoneVerifiedToUser pin phone_verified:boolean


コマンド
rails db:migrate


記述追加 app\models\user.rb(22行目)
10行目「from」にはTwilioで無料取得したご自分のアメリカの電話番号を入力して下さい。

  def generate_pin
    self.pin = SecureRandom.hex(2)
    self.phone_verified = false
    save
  end

  def send_pin
    @client = Twilio::REST::Client.new
    @client.messages.create(
      from: '+12056565281',
      to: self.phone_number,
      body: "テストサイトMinpakuです。あなたのPinコードは #{self.pin}"
    )
  end

  def verify_pin(entered_pin)
    update(phone_verified: true) if self.pin == entered_pin
  end  



app\models\user.rb
31行目「from」にはTwilioで無料取得したご自分のアメリカの電話番号を入力して下さい。

class User < ApplicationRecord

  # アバター画像表示用
  include Gravtastic
  gravtastic

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

  #長さ50文字以下 入力必須
  validates :fullname, presence: true, length: {maximum: 50}
  
  has_many :rooms
  has_many :reservations

  has_many :guest_reviews, class_name: "GuestReview", foreign_key: "guest_id"
  has_many :host_reviews, class_name: "HostReview", foreign_key: "host_id"

  def generate_pin
    self.pin = SecureRandom.hex(2)
    self.phone_verified = false
    save
  end

  def send_pin
    @client = Twilio::REST::Client.new
    @client.messages.create(
      from: '+12056565281',
      to: self.phone_number,
      body: "テストサイトMinpakuです。あなたのPinコードは #{self.pin}"
    )
  end

  def verify_pin(entered_pin)
    update(phone_verified: true) if self.pin == entered_pin
  end  

end



記述追加 app\controllers\users_controller.rb(14行目から)

    def update_phone_number
      current_user.update_attributes(user_params)

      # 日本の電話国番号+81を先頭につける
      japan_number = user_params[:phone_number].sub(/\A./,'+81')
      current_user.update_attributes(phone_number: japan_number)

      current_user.generate_pin
      current_user.send_pin
  
      redirect_to edit_user_registration_path, notice: "保存しました。"
    rescue Exception => e
      redirect_to edit_user_registration_path, alert: "#{e.message}"
    end
  
    def verify_phone_number
      current_user.verify_pin(params[:user][:pin])
  
      if current_user.phone_verified
        flash[:notice] = "電話番号が確認されました。"
      else
        flash[:alert] = "電話番号を確認できません。"
      end
  
      redirect_to edit_user_registration_path
  
    rescue Exception => e
      redirect_to edit_user_registration_path, alert: "#{e.message}"
    end
  
  
    private
  
    def user_params
      params.require(:user).permit(:phone_number, :pin)
    end



app\controllers\users_controller.rb

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

      # ユーザーがホストの場合、ホストに対するすべてのゲストレビューを表示
      @guest_reviews = Review.where(type: "GuestReview", host_id: @user.id)

      # ユーザーがゲストの場合、ユーザに対するすべてのホストレビューを表示
      @host_reviews = Review.where(type: "HostReview", guest_id: @user.id)
     
    end

    def update_phone_number
      current_user.update_attributes(user_params)

      # 日本の電話国番号+81を先頭につける
      japan_number = user_params[:phone_number].sub(/\A./,'+81')
      current_user.update_attributes(phone_number: japan_number)

      current_user.generate_pin
      current_user.send_pin
  
      redirect_to edit_user_registration_path, notice: "保存しました。"
    rescue Exception => e
      redirect_to edit_user_registration_path, alert: "#{e.message}"
    end
  
    def verify_phone_number
      current_user.verify_pin(params[:user][:pin])
  
      if current_user.phone_verified
        flash[:notice] = "電話番号が確認されました。"
      else
        flash[:alert] = "電話番号を確認できません。"
      end
  
      redirect_to edit_user_registration_path
  
    rescue Exception => e
      redirect_to edit_user_registration_path, alert: "#{e.message}"
    end
  
  
    private
  
    def user_params
      params.require(:user).permit(:phone_number, :pin)
    end
 
  end



記述更新 config\routes.rb(12行目)

  resources :users, only: [:show] do
    member do
      post '/verify_phone_number' => 'users#verify_phone_number'
      patch '/update_phone_number' => 'users#update_phone_number'
    end
  end



config\routes.rb

Rails.application.routes.draw do

  #ルートをpages#homeに設定
  root 'pages#home'

  get 'pages/home'
  get '/your_trips' => 'reservations#your_trips'
  get '/your_reservations' => 'reservations#your_reservations'
  get 'search' => 'pages#search'
  get 'dashboard' => 'dashboards#index'

  resources :users, only: [:show] do
    member do
      post '/verify_phone_number' => 'users#verify_phone_number'
      patch '/update_phone_number' => 'users#update_phone_number'
    end
  end

  resources :rooms, except: [:edit] do
    member do
      get 'listing'
      get 'pricing'
      get 'description'
      get 'photo_upload'
      get 'amenities'
      get 'location'
      get 'preload'
      get 'preview'
    end
    resources :photos, only: [:create, :destroy]
    resources :reservations, only: [:create]
  end

  resources :guest_reviews, only: [:create, :destroy]
  resources :host_reviews, only: [:create, :destroy]

  devise_for :users,
  path: '',
  path_names: {sign_in: 'login', sign_out: 'logout', edit: 'profile', sign_up: 'registration'},
  controllers: {registrations: 'registrations'}
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end



記述追加 app\views\devise\registrations\edit.html.erb
51行目から電話番号入力フィールドの記述を追加しています。
コードをコピーしてファイルを置き換えてください。

<div class="row">
  <div class="col-md-3">
    <ul class="sidebar-list">
      <li class="sidebar-item"><%= link_to "ユーザ登録情報の編集", edit_user_registration_path, class: "sidebar-link active" %></li>
    </ul>
    <br/>
    <%= link_to "登録情報を確認する", user_path(current_user.id), class: "btn btn-default" %>
  </div>
  <div class="col-md-9 text-center">
    <div class="panel panel-default">
      <div class="panel-heading">登録情報 追加・修正</div>
      <div class="panel-body">
        <div class="container">

          <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
            <%= render 'shared/devisemes' %>

            <div class="form-group">
              <%= f.text_field :fullname, autofocus: true, placeholder: "氏名", class: "form-control" %>
            </div>

            <div class="form-group">
              <%= f.text_area :description, rows: 5, cols: 25, autofocus: true, placeholder: "説明", class: "form-control" %>
            </div>

            <div class="form-group">
              <%= f.email_field :email, autofocus: true, placeholder: "メールアドレス", class: "form-control" %>
            </div>

            <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
              <div>現在、次の確認を待っています: <%= resource.unconfirmed_email %></div>
            <% end %>

            <div class="form-group">
              <%= f.password_field :password, autocomplete: "off", placeholder: "パスワード (変更しない場合は空白のまま)", class: "form-control" %>

            <div class="form-group">
              <%= f.password_field :password_confirmation, autocomplete: "off", placeholder: "確認(変更しない場合は空白のまま)", class: "form-control" %>
            </div>


            <div class="actions">
              <%= f.submit "保存する", class: "btn btn-normal btn-block" %>
            </div>
          <% end %>

        </div>
      </div>
    </div>

    <br/>
    <div class="panel panel-default">
      <div class="panel-heading">あなたの電話番号</div>
      <div class="panel-body">
        <div class="container">
          <% if !current_user.phone_number.blank? %>
              <h4><%= current_user.phone_number %></h4>
              <% if current_user.phone_verified == false %>
                  <button class="btn btn-default" data-toggle="modal" data-target="#verify-phone">PINコード入力</button>
              <% else %>             
              <button class="btn btn-default" data-toggle="modal" data-target="#update-phone">電話番号を更新</button>
              <% end %>
          <% else %>
              <button class="btn btn-default" data-toggle="modal" data-target="#update-phone">電話番号を追加</button>
          <% end %>
        </div>
      </div>
    </div>

  </div>
</div>

<!-- 電話番号のMODAL -->
<div id="update-phone" class="modal fade" role="dialog">
  <div class="modal-dialog">
    <div class="modal-content">

      <div class="modal-header">
        <button class="close" data-dismiss="modal">&times;</button>
        <h4 class="modal-title">電話番号</h4>
      </div>

      <div class="modal-body">
        <%= form_for current_user, url: update_phone_number_user_path(current_user) do |f| %>
            <div class="form-group">
              <%= f.phone_field :phone_number, class: "form-control", placeholder: "電話番号を入力してください。" %>
            </div>
            <div class="text-center">
              <%= f.submit "保存する", class: "btn btn-normal" %>
            </div>
        <% end %>
      </div>

    </div>
  </div>
</div>


<!-- PINコード入力のMODAL -->
<div id="verify-phone" class="modal fade" role="dialog">
  <div class="modal-dialog">
    <div class="modal-content">

      <div class="modal-header">
        <button class="close" data-dismiss="modal">&times;</button>
        <h4 class="modal-title">PINコード</h4>
      </div>

      <div class="modal-body">
        <%= form_for current_user, url: verify_phone_number_user_path(current_user), method: :post do |f| %>
            <div class="form-group">
              <%= f.text_field :pin, class: "form-control", placeholder: "SMS送信されたPINコードを入力", value: "" %>
            </div>
            <div class="text-center">
              <%= f.submit "確認する", class: "btn btn-normal" %>
            </div>
        <% end %>
      </div>

    </div>
  </div>
</div>



ブラウザ確認
http://localhost:3000/profile


「電話番号追加」をクリックします。

電話番号追加
電話番号追加


携帯電話番号を入力して下さい。
携帯電話番号入力
携帯電話番号入力


電話番号は保存されますが「phone_verified」はまだ「false」です。
false
false


ショートメールでPinコードが届きますので入力します。
PIN入力
PIN入力


「phone_verified」が「ture」になりました。
true
true


記述追加 app\views\dashboards\index.html.erb(13行目)

<% if current_user.phone_verified %>
      <li>電話番号<span class="pull-right icon-babu"><i class="fa fa-check-circle-o"></i></span></li>
<% end %>



app\views\dashboards\index.html.erb

<div class="row">
  <div class="col-md-3">
    <div class="center">
       <%= image_tag current_user.gravatar_url, class: "avatar-full" %>
    </div>
    <div class="panel panel-default">
      <div class="panel-heading"><%= current_user.fullname %></div>
      <div class="panel-body">
        <ul class="sidebar-list">
          <% if current_user.confirmation_token %>
            <li>メールアドレス<span class="pull-right icon-babu"><i class="fa fa-check-circle-o"></i></span></li>
          <% end %>
          <% if current_user.phone_verified %>
            <li>電話番号<span class="pull-right icon-babu"><i class="fa fa-check-circle-o"></i></span></li>
          <% end %>
        </ul>
      </div>
    </div>
  </div>

  <div class="col-md-9">

    <!-- お知らせ -->
    <div class="panel panel-default">
      <div class="panel-heading">お知らせ</div>
      <div class="panel-body">



      </div>
    </div>

    <!-- 登録している部屋 -->
    <div class="panel panel-default">
      <div class="panel-heading">登録しているお部屋(<%= @rooms.length %>)</div>
      <div class="panel-body">
        <%= render partial: "rooms/rooms_list", locals: {rooms: @rooms} %>
      </div>
    </div>


  </div>
</div>



ブラウザ確認
http://localhost:3000/dashboard


電話番号が登録されている場合チェックマークがつくようになりました。

電話番号登録
電話番号登録


記述追加 app\views\users\show.html.erb(18行目)

<% if @user.phone_verified %>
      <li>電話番号<span class="pull-right icon-babu"><i class="fa fa-check-circle-o"></i></span></li>
<% end %>



app\views\users\show.html.erb

<div class="row">
  <div class="col-md-3">
    <div class="center">
      <%= image_tag @user.gravatar_url, class: "avatar-full" %>
    </div>
    <div class="panel panel-default">
      <div class="panel-heading" style="text-align: center;"><%= @user.fullname %></div>
      <br/>
      <div class="center">
        <%= @user.description %>
       </div>
       <hr/>
      <div class="panel-body">
        <ul class="sidebar-list">
          <% if @user.confirmation_token %>
            <li>Emailアドレス<span class="pull-right icon-babu"><i class="fa fa-check-circle-o"></i></span></li>
          <% end %>
          <% if @user.phone_verified %>
            <li>電話番号<span class="pull-right icon-babu"><i class="fa fa-check-circle-o"></i></span></li>
          <% end %>
        </ul>
      </div>
    </div>
  </div>

  <div class="col-md-9">
 
    <br/>
    <h4><%= @user.fullname %>さんが登録しているお部屋(<%= @rooms.length %>)</h4><br/>

    <div class="row">
      <%= render partial: "rooms/rooms_list", locals: {rooms: @rooms} %>
    </div>

    <br/>
    <h4>ゲストからのレビュー (<%= @guest_reviews.count %>)</h4>
    <div class="row">
      <%= render "reviews/guest_list" %>
    </div>
    <br/>

    <h4>ホストからのレビュー (<%= @host_reviews.count %>)</h4>
    <div class="row">
      <%= render "reviews/host_list" %>
    </div>

    <br/>

  </div>
</div>



ブラウザ確認
同じく電話番号の登録欄をつけました。
http://localhost:3000/users/3