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

学生向けにプログラミングを解説。Java、C++、Ruby、PHP、データベース

Ruby on RailsでWebアプリケーション開発その25 注文フォームの作成

<<前  [TOP]  次>>


注文を受ける機能を実装してきます。


実装するordersテーブルのフィールドは以下のようにします。

  • name: 客氏名(string型)
  • address: 客住所(text型)
  • email: 客メールアドレス(string型)
  • tel_number: 客電話番号(string型)
  • pay_type: 支払い方法(string型)


    コマンドプロンプトでshopフォルダに入って「rails generate scaffold order name:string address:text email:string tel_number:string pay_type:string」と入力します。
    f:id:MrRadiology:20180301123002p:plain


    CSSを上書きするか聞いてくるので、「n」と入力して上書きしないようにします。
    f:id:MrRadiology:20180301122633j:plain


    「migrate」フォルダに「create_orders.rb」が作成されました。
    rake db:migrate」で出来たマイグレーションを適用します。
    f:id:MrRadiology:20180301123050p:plain


    ordersテーブルを見てみましょう。
    f:id:MrRadiology:20180301190913p:plain


    次にline_itemsテーブルに「order_id」フィールド(int型)を追加します。
    rails generate migration add_order_id_to_line_item order_id:integer」とコマンドプロンプトで入力します。
    f:id:MrRadiology:20180301191326p:plain


    「migrate」フォルダに「add_order_id_to_line_item.rb」が作成されました。
    rake db:migrate」で出来たマイグレーションを適用します。
    f:id:MrRadiology:20180301191446p:plain


    line_itemsテーブルに「order_id」フィールドが追加されているか確認します。
    f:id:MrRadiology:20180301191652j:plain


    次に注文受け付けフォームを作成します。
    まずは買い物カートに「注文する」ボタンを追加します。
    cartsビューの「show.html.erb」ファイルを編集します。


    【app/views/carts/show.html.erb】

    <% if notice %>
    <p id="notice"><%= notice %></p>
    <% end %>
    <br>
    <h1>Railsはじめてマート</h1>
    <h2>   カートに追加された商品</h2>
    <br>
    <%= button_to 'カートを空にする', @cart, method: :delete, data: {confirm: 'カートを空にして本当によろしいですか?'}, class: 'fbtn' %>
    <br>
    <table>
    	<tr>
    		<th></th>
    		<th>商品名</th>
    		<th>価格</th>
    		<th>数量</th>
    		<th>合計</th>
    		<th></th>
    
    	</tr>
    		<% @cart.line_items.each do |item| %>
    		<tr>
    			<td text-align:center;><img height="80" src="<%=h item.good.image_url %>"/></td>
    			<td text-align:center;><font size="4"><%= item.good.title %></font></td>
    			<td text-align:center;><font size="4"><%= (item.good.price).to_i %></font></td>
    			<td text-align:center;><font size="4"><%= item.quantity %></font></td>
    			<td text-align:center;><font size="4"><%= (item.total_price).to_i %></font></td>
    			<td><%= button_to '削除', item, method: :delete, data: {confirm: 'カートから削除してよろしいですか?'}%></td>
    		</tr>
    		<% end %>
    
    		<tr>
    			<td colspan="6"><font size="5">総計:<%= (@cart.total_price).to_i %></font></td>
    		</tr>
    
    </table>
    <br>
    <span>                                         </span><%= link_to '買い物を続ける', market_path, :class => 'btn' %><span>
    <br>
    <%= button_to '注文をする', new_order_path, method: :get, class: 'fbtn' %>
    

    ボタンの記述は以下の部分です。

    <%= button_to '注文をする', new_order_path, method: :get, class: 'fbtn' %>
    

    ordersコントローラのnewアクションにリンクします。


    f:id:MrRadiology:20180301193010j:plain


    ordersコントローラの編集です。


    【app/controllers/orders_controller.rb】

    class OrdersController < ApplicationController
      before_action :set_order, only: [:show, :edit, :update, :destroy]
    
      # GET /orders
      # GET /orders.json
      def index
        @orders = Order.all
      end
    
      # GET /orders/1
      # GET /orders/1.json
      def show
      end
    
      # GET /orders/new
      def new
    	@cart = current_cart
    	if @cart.line_items.empty?
    		redirect_to market_url, notice: 'カートは空です。'
    		return
    	end
    
    	@order = Order.new
    
    	respond_to do |format|
    		format.html
    		format.json { render json: @order }
    	end
      end
    
      # GET /orders/1/edit
      def edit
      end
    
      # POST /orders
      # POST /orders.json
      def create
        @order = Order.new(order_params)
    
        respond_to do |format|
          if @order.save
            format.html { redirect_to @order, notice: 'Order was successfully created.' }
            format.json { render :show, status: :created, location: @order }
          else
            format.html { render :new }
            format.json { render json: @order.errors, status: :unprocessable_entity }
          end
        end
      end
    
      # PATCH/PUT /orders/1
      # PATCH/PUT /orders/1.json
      def update
        respond_to do |format|
          if @order.update(order_params)
            format.html { redirect_to @order, notice: 'Order was successfully updated.' }
            format.json { render :show, status: :ok, location: @order }
          else
            format.html { render :edit }
            format.json { render json: @order.errors, status: :unprocessable_entity }
          end
        end
      end
    
      # DELETE /orders/1
      # DELETE /orders/1.json
      def destroy
        @order.destroy
        respond_to do |format|
          format.html { redirect_to orders_url, notice: 'Order was successfully destroyed.' }
          format.json { head :no_content }
        end
      end
    
      private
        # Use callbacks to share common setup or constraints between actions.
        def set_order
          @order = Order.find(params[:id])
        end
    
        # Never trust parameters from the scary internet, only allow the white list through.
        def order_params
          params.require(:order).permit(:name, :address, :email, :tel_number, :pay_type)
        end
    end
    



    new()メソッドのみ実装しています。

    def new
    	@cart = current_cart
    	if @cart.line_items.empty?
    		redirect_to market_url, notice: 'カートは空です。'
    		return
    	end
    
    	@order = Order.new
    
    	respond_to do |format|
    		format.html
    		format.json { render json: @order }
    	end
    end
    

    もしカートに何もない場合はmarketビューにリダイレクトさせています。
    その後「return」でメソッドを抜けるようにしています。


    次はordersビューの編集です。
    まずは「new.html.erb」を編集します。


    【app/views/orders/new.html.erb】

    <h1>注文情報の入力</h1>
    <br>
    <div class="order_form">
    	<fieldset>
    		<legend>お客様の情報を入力して下さい。</legend>
    		<%= render 'form' %>
    	</fieldset>
    </div>
    <br>
    <%= link_to '買い物を続ける',market_path, class: 'btn' %>
    



    続いて「_form」テンプレートの編集です。


    【app/views/orders/_form.html.erb】

    <%= form_for(@order) do |f| %>
      <% if @order.errors.any? %>
        <div id="error_explanation">
          <h2>エラーが<%= @order.errors.count %>件あります。お客様情報を入力して下さい。</h2>
    
          <ul>
          <% @order.errors.full_messages.each do |message| %>
            <li><%= message %></li>
          <% end %>
          </ul>
        </div>
      <% end %>
    <br>
      <div class="field">
        <%= f.label :氏名 %>
        <%= f.text_field :name, size: 40 %>
      </div>
      <div class="field">
        <%= f.label :住所 %>
        <%= f.text_area :address, rows: 3, cols: 40 %>
      </div>
      <div class="field">
        <%= f.label :メールアドレス %>
        <%= f.email_field :email, size: 40 %>
      </div>
      <div class="field">
        <%= f.label :電話番号 %>
        <%= f.telephone_field :tel_number %>
      </div>
      <div class="field">
        <%= f.label :お支払い方法 %>
        <%= f.select(:pay_type, [['選択して下さい', ''],['現金', '現金']]) %>
      </div>
    <br>
      <div class="actions">
        <%= f.submit '注文を確定する' %>
      </div>
    <% end %>
    



    最後にCSSを編集します。


    【app/assets/stylesheets/scaffolds.scss】

    h1:first-letter {
    	font-size: 2em;
    	color: #7172ac;
    }
    
    h1 {
    	position: relative;
    	color: #333333;
    	text-shadow: 0 0 2px white;
    }
    
    h1:before {
    	content: "";
    	position: absolute;
    	background: #9de5ff;
    	width: 50px;
    	height: 50px;
    	border-radius: 50%;
    	top: 50%;
    	/* border: dashed 1px white; */
    	left: -15px;
    	-moz-transform: translateY(-50%);
    	-webkit-transform: translateY(-50%);
    	-ms-transform: translateY(-50%);
    	transform: translateY(-50%);
    	z-index: -1;
    }
    
    table {
      width: auto;
      border-spacing: 0;
      font-size:14px;
    }
    
    table th {
      color: #fff;
      padding: 8px 15px;
      background: #7172ac;
      background:-moz-linear-gradient(rgba(34,85,136,0.7), rgba(34,85,136,0.9) 50%);
      background:-webkit-gradient(linear, 100% 0%, 100% 50%, from(rgba(34,85,136,0.7)), to(rgba(34,85,136,0.9)));
      font-weight: bold;
      border-left:1px solid #258;
      border-top:1px solid #258;
      border-bottom:1px solid #258;
      line-height: 120%;
      text-align: center;
      text-shadow:0 -1px 0 rgba(34,85,136,0.9);
      box-shadow: 0px 1px 1px rgba(255,255,255,0.3) inset;
    }
    
    table th:first-child {
      border-radius: 5px 0 0 0;
    }
    
    table th:last-child {
      border-radius:0 5px 0 0;
      border-right:1px solid #258;
      box-shadow: 2px 2px 1px rgba(0,0,0,0.1),0px 1px 1px rgba(255,255,255,0.3) inset;
    }
    
    table tr td {
      padding: 8px 15px;
      border-bottom: 1px solid #84b2e0;
      border-left: 1px solid #84b2e0;
      text-align: center;
    }
    
    table tr td:last-child {
      border-right: 1px solid #84b2e0;
      box-shadow: 2px 2px 1px rgba(0,0,0,0.1);
    }
    
    table tr {
      background: #fff;
    }
    
    table tr:nth-child(2n+1) {
      background: #f1f6fc;
    }
    
    table tr:last-child td {
      box-shadow: 2px 2px 1px rgba(0,0,0,0.1);
    }
    
    table tr:last-child td:first-child {
      border-radius: 0 0 0 5px;
    }
    
    table tr:last-child td:last-child {
      border-radius: 0 0 5px 0;
    }
    
    table tr:hover {
      background: #fffafa;
      cursor:pointer;
    }
    
    .btn {
        position: relative;
        display: inline-block;
        font-weight: bold;
        padding: 12px 0 8px;
        text-decoration: none;
        color: #67c5ff;
        transition: .4s;
    }
    
    .btn:before{
       position: absolute;
       content: '';
       width: 100%;
       height: 4px;
       top:100%;
       left: 0;
       border-radius: 3px;
       background:#67c5ff;
       transition: .2s;
    }
    
    .btn:after{
       position: absolute;
       content: '';
       width: 100%;
       height: 4px;
       top:0;
       left: 0;
       border-radius: 3px;
       background:#67c5ff;
       transition: .2s;
    }
    
    .btn:hover:before {
        top: -webkit-calc(100% - 3px);
        top: calc(100% - 3px);
    }
    
    .btn:hover:after {
        top: 3px;
    }
    
    body {
      background-color: #fff;
      color: #333;
      margin: 33px;
      font-family: verdana, arial, helvetica, sans-serif;
      font-size: 13px;
      line-height: 18px;
    }
    
    p, ol, ul, td {
      font-family: verdana, arial, helvetica, sans-serif;
      font-size: 13px;
      line-height: 18px;
    }
    
    pre {
      background-color: #eee;
      padding: 10px;
      font-size: 11px;
    }
    
    div {
      &.field, &.actions {
        margin-bottom: 10px;
      }
    }
    
    #notice {
     padding:12px;
     font-weight:850;
     color:#262626;
     background:#CCFFCC;
     border:2px solid #00CC00;
     width: 500px;
     font-size: 18px;
    }
    
    .field_with_errors {
      padding: 2px;
      background-color: red;
      display: table;
    }
    
    #error_explanation {
      width: 450px;
      border: 2px solid red;
      padding: 7px 7px 0;
      margin-bottom: 20px;
      background-color: #f0f0f0;
    
      h2 {
        text-align: left;
        font-weight: bold;
        padding: 5px 5px 5px 15px;
        font-size: 12px;
        margin: -7px -7px 0;
        background-color: #c00;
        color: #fff;
      }
    
      ul li {
        font-size: 12px;
        list-style: square;
      }
    }
    
    label {
      display: block;
    }
    
    .order_form {
    	fieldset {
    		background: #efe;
    		width: 500px;
    
    		legend {
    			color: #dfd;
    			background: #6495ed;
    		}
    	}
    }
    



    最後に以下の部分を追加しました。

    .order_form {
    	fieldset {
    		background: #efe;
    		width: 500px;
    
    		legend {
    			color: #dfd;
    			background: #6495ed;
    		}
    	}
    }
    



    f:id:MrRadiology:20180301210134p:plain


    <<前  [TOP]  次>>