[Ruby] Creating an EC site with Rails 5 ⑤ ~Customer model edition~

6 minute read

Introduction

It is a continuation of the series of making an EC site where you can shop at a fictitious bakery, Create an EC site with Rails 5 ④. Finally, we will start implementing the function. First of all, in order from the peripheral functions of the parent model of the customer site, so this time we will make CRUD of the Customer model as the center.

Source code

https://github.com/Sn16799/bakeryFUMIZUKI

devise controller

I couldn’t register new with devise, and I wondered why it was because I did not give the parameter permission to the controller side.

app/controllers/customers/registrations_controller.rb


class Customers::RegistrationsController <Devise::RegistrationsController

  before_action :authenticate_customer!
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def after_sign_up_path_for(resource)
    customer_top_path
  end

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:is_active, :first_name, :first_name_kana, :family_name, :family_name_kana, :post_code, :address, :tel])
  end

  protected
  def update_resouce(resource, parans)
    resource.update_without_password(params)
  end

end

I also specified the session_controller login destination and the transition destination after logout.

app/controllers/customers/sessions_controller.rb


class Customers::SessionsController <Devise::SessionsController

  protected

  def after_sign_in_path_for(resource)
    customer_top_path
  end

  def after_sign_out_path_for(resource)
    customer_top_path
  end

end

Enable Bootstrap flash

In Bootstrap, you can use a neat flash message just by specifying the class. It is convenient to create a partial template so that it can be called with render.

app/controllers/application_controller.rb


add_flash_types :success, :danger, :info

app/helpers/application_helper.rb


def flash_class_for flash_type
  case flash_type
    when'success' then'alert-success'
    when'danger' then'alert-danger'
    when'info' then'alert-info'
  end
end

html:app/views/layouts/_flash.html.erb


<% flash.each do |type, msg| %>
  <div class="alert <%= flash_class_for(type) %> row" role="alert">
    <a class="close" data-dismiss="alert"> ×</a>
    <%= msg %>
  </div>
<% end %>

html:app/views/layouts/application.html.erb


<body>
  <%= render'layouts/header' %>
  <div class="container-fluid">
    <%= render'layouts/flash', flash: flash %>
    <div class="row">
      <%= yield %>
    </div>
  </div>
  <%= render'layouts/footer' %>
</body>

Controller

Many features are basic CRUD, but only the withdrawal process is different.

Press the "leave" button on the edit screen (call withdraw action)
↓
Transition to the withdrawal confirmation screen
↓
Press "Unsubscribe" button (alert appears)
↓
When "Yes" is pressed, the withdrawal process is performed (withdraw_done action call)

The flow is as described above. Also, the withdrawal process is not the deletion of Customer data, but the process of changing the is_active column from true to false.

app/controllers/customers_controller.rb


class CustomersController <ApplicationController

  before_action :authenticate_customer!
  before_action :set_customer
  before_action :baria_customer

  def edit
  end

  def show
  end

  def update
    @customer = current_customer
    if @customer.update(customer_params)
      redirect_to customer_path(@customer), success:'Customer information has been updated! '
    else
      flash[:danger] ='Could not update your information. Are there any blank areas? '
      render :edit
    end
  end

  def withdraw
  end

  def withdraw_done
    @customer = current_customer
    @customer.update(is_active: false)
    reset_session
    redirect_to customer_top_path, info:'Thank you. We look forward to seeing you again. '
  end

  private

  def customer_params
    params.require(:customer).permit(:is_active, :first_name, :first_name_kana, :family_name, :family_name_kana, :post_code, :address, :email, :tel, cart_items_attributes: [:_destroy])
  end

  def set_customer
    @customer = current_customer
  end

  #When you try to access another person's page, you return to the previous page
  def baria_customer
    if params[:id].to_i != current_customer.id
      redirect_back(fallback_location: root_path)
    end
  end
end

View

Sign_up

html:app/views/customers/registrations/new.html.erb


<div class='col-lg-6 offset-lg-3 offset-2 space'>
  <div class='row'>
    <h2>New member registration</h2>
  </div>

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


    <div class='form-group row space'>
      <div class='col-lg-4'>
        <h5><%= f.label :name %></h5>
      </div>
      <div class='col-lg-8'>
        <div class="row">
          <div class="col-lg-6">
            (Last name) <%= f.text_field :family_name, autofocus: true, autocomplete:'family_name', class:'form-control' %>
          </div>
          <div class="col-lg-6">
            (First name) <%= f.text_field :first_name, autofocus: true, autocomplete:'first_name', class:'form-control' %>
          </div>
        </div>
      </div>
    </div>

    <div class='form-group row'>
      <div class='col-lg-4'>
        <h5><%= f.label: Phonetic %></h5>
      </div>
      <div class='col-lg-8'>
        <div class="row">
          <div class="col-lg-6">
            (Say) <%= f.text_field :family_name_kana, autofocus: true, autocomplete:'family_name_kana', class:'form-control' %>
          </div>
          <div class="col-lg-6">(メイ)<%= f.text_field :first_name_kana, autofocus: true, autocomplete: 'first_name_kana', class: 'form-control' %>
          </div>
        </div>
      </div>
    </div>

    <div class='form-group row'>
      <div class='col-lg-4'>
        <h5><%= f.label :Eメール %></h5>
      </div>
      <div class='col-lg-8'>
        <%= f.text_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %>
      </div>
    </div>

    <div class='form-group row'>
      <div class='col-lg-4'>
        <h5><%= f.label :郵便番号(ハイフンなし) %></h5>
      </div>
      <div class='col-lg-8'>
        <%= f.text_field :post_code, autofocus: true, autocomplete: 'post_code', class: 'form-control' %>
      </div>
    </div>

    <div class='form-group row'>
      <div class='col-lg-4'>
        <h5><%= f.label :住所 %></h5>
      </div>
      <div class='col-lg-8'>
        <%= f.text_field :address, autofocus: true, autocomplete: 'address', class: 'form-control' %>
      </div>
    </div>

    <div class='form-group row'>
      <div class='col-lg-4'>
        <h5><%= f.label :電話番号(ハイフンなし) %></h5>
      </div>
      <div class='col-lg-8'>
        <%= f.text_field :tel, autofocus: true, autocomplete: 'tel', class: 'form-control' %>
      </div>
    </div>

    <div class='form-group row'>
      <div class='col-lg-4'>
        <h5><%= f.label :パスワード(6文字以上) %></h5>
      </div>
      <div class='col-lg-8'>
        <%= f.password_field :password, autocomplete: 'new-password', class: 'form-control' %>
      </div>
    </div>

    <div class='form-group row'>
      <div class='col-lg-4'>
        <h5><%= f.label :パスワード確認用 %></h5>
      </div>
      <div class='col-lg-8'>
        <%= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form-control' %>
      </div>
    </div>

    <div class='row'>
      <div class='col-lg-2 offset-lg-5 actions'>
        <%= f.submit '新規登録', class:'btn btn-danger' %>
      </div>
    </div>

    <% end %>
  </div>

  <div class="space">
    <h4>すでに登録済みの方</h4>
    <h5><%= link_to 'こちら', new_customer_session_path  %>からログインしてください</h5>
  </div>
</div>

Sign_in

html/app/views/customers/sessions/new.html.erb


<div class='col-lg-6 offset-lg-4 offset-1 space'>
  <div class="row">
    <h3>
      <span style="display: inline-block;">会員の方は</span>
      <span style="display: inline-block;">こちらからログイン</span>
    </h3>
  </div>

  <div class="row">
    <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
    <%= render 'devise/shared/error_messages', resource: resource %>

    <div class="form-group row space">
      <div class="col-lg-4">
        <h5><%= f.label :Eメール %></h5>
      </div>
      <div class="col-lg-8">
        <%= f.text_field :email, autofocus: true, autocomplete: "email", class: 'form-control' %>
      </div>
    </div>

    <div class="form-group row">
      <div class="col-lg-4">
        <h5><%= f.label :パスワード %></h5>
      </div>
      <div class="col-lg-8">
        <%= f.password_field :password, autocomplete: "current-password", class: 'form-control' %>
      </div>
    </div>

    <div>
      <%= link_to "=>パスワードを忘れた方はこちら", new_password_path(resource_name) %>
    </div>

    <div class="form-group row space">
      <div class="col-lg-2 offset-lg-5">
        <%= f.submit "ログイン", class:"btn btn-danger" %>
      </div>
    </div>

    <% end %>
  </div>

  <div class="space">
    <h4>登録がお済みでない方</h4>
    <h5><%= link_to "こちら", new_customer_registration_path  %>から新規登録してください。</h5>
  </div>

</div>

編集

html:app/views/customers/edit.html.erb


<div class='col-lg-10 offset-lg-1 space'>
  <div class='row'>
    <h2>登録情報編集</h2>
  </div>

  <div class='row'>
    <%= form_with(model: @customer, local: true, class: 'container') do |f| %>

    <div class='form-group row'>
      <div class='col-lg-4'>
        <h5><%= f.label :名前 %></h5>
      </div>
      <div class='col-lg-8'>
        <span style='display: inline-block;'>
          (姓)<%= f.text_field :family_name, autofocus: true, autocomplete: 'family_name', class: 'form-control' %>
        </span>
        <span style='display: inline-block;'>
          (名)<%= f.text_field :first_name, autofocus: true, autocomplete: 'first_name', class: 'form-control' %>
        </span>
      </div>
    </div>

    <div class='form-group row'>
      <div class='col-lg-4'>
        <h5><%= f.label :フリガナ %></h5>
      </div>
      <div class='col-lg-8'>
        <span style='display: inline-block;'>
          (セイ)<%= f.text_field :family_name_kana, autofocus: true, autocomplete: 'family_name_kana', class: 'form-control' %>
        </span>
        <span style='display: inline-block;'>
          (メイ)<%= f.text_field :first_name_kana, autofocus: true, autocomplete: 'first_name_kana', class: 'form-control' %>
        </span>
      </div>
    </div>

    <div class='form-group row'>
      <div class='col-lg-4'>
        <h5><%= f.label :Eメール %></h5>
      </div>
      <div class='col-lg-8'><%= f.text_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %>
      </div>
    </div>

    <div class='form-group row'>
      <div class='col-lg-4'>
        <h5><%= f.label :郵便番号(ハイフンなし) %></h5>
      </div>
      <div class='col-lg-8'>
        <%= f.text_field :post_code, autofocus: true, autocomplete: 'post_code', class: 'form-control' %>
      </div>
    </div>

    <div class='form-group row'>
      <div class='col-lg-4'>
        <h5><%= f.label :住所 %></h5>
      </div>
      <div class='col-lg-8'>
        <%= f.text_field :address, autofocus: true, autocomplete: 'address', class: 'form-control' %>
      </div>
    </div>

    <div class='form-group row'>
      <div class='col-lg-4'>
        <h5><%= f.label :電話番号(ハイフンなし) %></h5>
      </div>
      <div class='col-lg-8'>
        <%= f.text_field :tel, autofocus: true, autocomplete: 'tel', class: 'form-control' %>
      </div>
    </div>

    <div class="form-group row">
      <div class="col-lg-4 offset-lg-8">
        <%= f.submit '編集内容を保存する', class:'btn btn-danger' %>
        <%= link_to '退会する', customer_withdraw_path(@customer), class:'btn btn-danger'  %>
      </div>
    </div>
    <% end %>
  </div>
</div>

詳細

html:app/views/customers/show.html.erb


<div class="col-lg-6 offset-lg-3 space">
  <div class="container">
    <h2>マイページ</h2>
  </div>
  <!-- お客様情報 -->
  <div class="container space">
    <div class="row">
      <div class="col-lg-5"><h3>お客様情報</h3></div>
      <div class="col-lg-2">
        <%= link_to '編集', edit_customer_path, class: 'btn-sm btn-danger' %>
      </div>
      <div class="col-lg-4">
        <%= link_to 'パスワード変更', new_customer_password_path, class: 'btn-sm btn-danger' %>
      </div>
    </div>

    <div class="row">
      <div class="col-lg-3">
        <h4>氏名</h4>
      </div>
      <div class="col-lg-9">
        <%= @customer.full_name %>
      </div>
    </div>

    <div class="row">
      <div class="col-lg-3">
        <h4>カナ</h4>
      </div>
      <div class="col-lg-9">
        <%= @customer.family_name_kana %> <%= @customer.first_name_kana %>
      </div>
    </div>

    <div class="row">
      <div class="col-lg-3">
        <h4>郵便番号</h4>
      </div>
      <div class="col-lg-9">
        〒 <%= @customer.post_code %>
      </div>
    </div>

    <div class="row">
      <div class="col-lg-3">
        <h4>住所</h4>
      </div>
      <div class="col-lg-9">
        <%= @customer.address %>
      </div>
    </div>

    <div class="row">
      <div class="col-lg-3">
        <h4>電話番号</h4>
      </div>
      <div class="col-lg-9">
        <%= @customer.tel %>
      </div>
    </div>

    <div class="row">
      <div class="col-lg-3">
        <h4>Eメール</h4>
      </div>
      <div class="col-lg-9">
        <%= @customer.email %>
      </div>
    </div>
  </div>

  <!-- その他のリンク -->
  <div class="container space">
    <div class="row">
      <div class="col-lg-3">
        <h4>配送先</h4>
      </div>
      <div class="col-lg-9">
        <%= link_to '一覧を見る', addresses_path, class: 'btn btn-danger' %>
      </div>
    </div>

    <div class="row">
      <div class="col-lg-3">
        <h4>注文履歴</h4>
      </div>
      <div class="col-lg-9">
        <%= link_to '一覧を見る', orders_path, class: 'btn btn-danger' %>
      </div>
    </div>
  </div>
</div>

退会確認

html:app/views/customers/withdraw.html.erb


<div class="col-lg-10 offset-lg-1 space">
  <div class="row space">
    <h2>本当に退会しますか?</h2>
  </div>

  <div class="row space">
    <h4>退会すると、会員登録情報や
      <br>これまでの購入履歴が閲覧できなくなります。
      <br>退会する場合は「退会する」をクリックしてください。
    </h4>
  </div>

  <div class="row space">
    <%= link_to "退会しない",customer_path(current_customer),class: "btn btn-danger" %>
    <%= link_to "退会する", customer_withdraw_done_path(current_customer), method: :put, "data-confirm" => "本当に退会しますか?", class: "btn btn-danger" %>
  </div>
</div>

SCSS

app/assets/stylesheets/application.scss


// initialize
*{
  margin:0;
  padding:0;
  box-sizing:border-box;
  color: #120136;
}

a{
  text-decoration: none;
}

.space {
  padding: 50px 0 30px;
}

後記

今回、機能そのものは難しくないのですが、画面をレスポンシブ対応にしようとするとやはり手間取りました。ボタンは後ほどテーマカラーの色に変更する予定で、今はとりあえず全て赤にしてあります。目立つので。 また、Viewファイルに書いたリンクも一応は機能しますが、飛んだ先のページにまだ何も書いていないので、例の「Find me in …」という文言が出るだけです。

メインの画面を作ったことで、ようやくサイトらしい見た目になってきましたね。機能はまだまだですが、これから実装していきます。気になっていたIK●Aカラーも、進めるうちに「これはこれでアリなんじゃないか?」と思えるようになってきました。単に見慣れただけかも知れませんが。

写真を載せればきっとパン屋さんのサイトになると信じています。次回へ続く!

参考

RailsでTwitterBootstrapを使ったflashメッセージ

データ登録の際、適当な名前や住所が思い付かない時に便利。「文月太郎」とかでは流石に味気ないので……。 日本人名前自動生成機 ランダム日本地名ジェネレータ