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

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

Ruby on RailsでWebアプリケーション開発その17 アプリケーションの再実装

<<前  [TOP]  次>>


今までSQLite環境でやってきたアプリケーションの実装をMySQL環境でやっていきます。
Railsのバージョンも4.2に変更しているので、記述が若干異なっています。


「shop」フォルダにある「Gemfile」というファイルを開いて、最後に「gem 'coffee-script-source', '1.8.0'」という記述を追加しましょう。
その後、コマンドプロンプトで「shop」フォルダに移動して「bundle update coffee-script-source」と入力します。
f:id:MrRadiology:20180129165252p:plain


コマンドプロンプトで「rails server」と入力すると「WEBrick」が起動します。
Rails5.1ではWebサーバが「Puma」でしたが、Rails4.2では「WEBrick」を使用することになります。
使用方法は変わりません。
てブラウザで「http://localhost:3000/goods」を確認してみましょう。
f:id:MrRadiology:20180220145826p:plain
このようなindexページが表示されれば大丈夫です。


「app/models/good.rb」ファイルを以下のようにしてvalidates(バリデート)を実装します。
文字コードは「UTF-8」にします。
【app/models/good.rb】

class Good < ActiveRecord::Base

	validate :price_validate

	#Railsで標準で用意されている検証メソッド
	#指定されたフィールドが存在し、その内容が空でないことを確認。
	validates_presence_of :title, :image_url, :maker, :category,
	:message => "が空の状態で保存することは出来ません。"

	#priceフィールドに数字か入力されているか検証。
	validates_numericality_of :price,
	:message => "が有効な数値ではありません。"

	#titleフィールドに保存しようとする名称が存在していないかどうかを確認。
	validates_uniqueness_of :title,
	:message => "はすでに存在しています。"

	#フィールドの値が正規表現に一致するかどうかをチェック。
	#.gif,.jpg,.pngのどれかで終わっていることを確認。
	validates_format_of :image_url,
	:with => /\a|\.jpg$|\.png$|\.gif$\z/,
	:message => "はGIF,JPG,PNG画像でなければなりません。"

	#商品の価格が正の数であることを確認する。
	#価格フィールドが空でないときだけチェックをする。
	protected
	def price_validate
		errors.add(:price, "は0より大きくなければなりません。") unless price.nil? || price > 0.0
	end

end



トップページの見た目を変えます。
【app/views/goods/index.html.erb】

<p id="notice"><%= notice %></p>

<h1>Railsはじめてマート</h1>

<table>
  <thead>
    <tr>
      <th>商品画像</th>
      <th>商品ID</th>
      <th>商品名</th>
      <th>説明</th>
      <th>画像URL</th>
      <th>価格</th>
      <th>登録日</th>
      <th>メーカー</th>
      <th>カテゴリー</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @goods.each do |good| %>
      <tr>
	<td text-align:center;><img height="80" src="<%=h good.image_url %>"/></td>
        <td><%= good.goods_id %></td>
        <td><%= good.title %></td>
        <td><%= good.description %></td>
        <td><%= good.image_url %></td>
        <td><%= (good.price).to_i %></td>
        <td><%=h ((good.date).to_date).strftime('%Y年%m月%d日') %></td>
        <td><%= good.maker %></td>
        <td><%= good.category %></td>
        <td><%= link_to '詳細', good, class: 'btn' %></td>
        <td><%= link_to '編集', edit_good_path(good), class: 'btn' %></td>
        <td><%= link_to '削除', good, method: :delete, data: { confirm: '本当に削除してもよろしいですか?' }, class: 'btn' %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to '商品登録', new_good_path, class: 'btn' %>



価格を表示する部分を整数表示させたかったので「.to_i 」メソッドを使用した記述に変更しています。

<td><%= (good.price).to_i %>円</td>



スタイルシートを実装します。
【app/assets/stylesheets/scaffold.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 {
  color: green;
}

.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;
}



次は登録フォームの実装です。
【app/views/goods/new.html.erb】

<h1>商品の登録</h1>

<%= render 'form', good: @good %>

<%= link_to 'トップページに戻る', goods_path, class: 'btn' %>



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

<%= form_for(@good) do |f| %>
  <% if @good.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@good.errors.count, "error") %> 商品を登録することが出来ません。</h2>

      <ul>
      <% @good.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>
<br>
  <div class="field">
    <%= f.label :goods_id, :商品ID %>
    <%= f.number_field :goods_id, id: :good_goods_id %>
  </div>

  <div class="field">
    <%= f.label :title, :商品名 %>
    <%= f.text_area :title, id: :good_title %>
  </div>

  <div class="field">
    <%= f.label :description, :詳細 %>
    <%= f.text_area :description, id: :good_description %>
  </div>

  <div class="field">
    <%= f.label :image_url, :画像URL %>
    <%= f.text_area :image_url, id: :good_image_url %>
  </div>

  <div class="field">
    <%= f.label :price, :価格 %>
    <%= f.number_field :price, id: :good_price %><span></span>
  </div>

  <div class="field">
    <%= f.label :date, :登録日 %>
    <%= f.datetime_select :date, id: :good_date, :use_month_numbers => true, :default => Time.now %>
  </div>

  <div class="field">
    <%= f.label :maker, :メーカー %>
    <%= f.text_area :maker, id: :good_maker %>
  </div>

  <div class="field">
    <%= f.label :category, :カテゴリー %>
    <%= f.select(:category, [['選択して下さい', ''],['本', '本'],['家電', '家電'],['おもちゃ', 'おもちゃ'], ['生活用品', '生活用品']]) %>
  </div>

  <div class="actions">
    <%= f.submit('登録', :class=> 'fbtn') %>
  </div>
<% end %>



変更点は「date」フィールドの部分に「datetime_select」メソッドを使用したところです。

<%= f.datetime_select :date, id: :good_date, :use_month_numbers => true, :default => Time.now %>

オプションで「:use_month_numbers => true」と指定することで、月の英語表示を数字に変えることが出来ます。
さらに「:default => Time.now」とすることで、デフォルトを現在の日時に指定しています。


コントローラの実装です。
【app/controllers/goods_controller.rb】

class GoodsController < ApplicationController
  before_action :set_good, only: [:show, :edit, :update, :destroy]

  # GET /goods
  # GET /goods.json
  def index
    @goods = Good.all
  end

  # GET /goods/1
  # GET /goods/1.json
  def show
  end

  # GET /goods/new
  def new
    @good = Good.new
  end

  # GET /goods/1/edit
  def edit
  end

  # POST /goods
  # POST /goods.json
  def create
    @good = Good.new(good_params)

    respond_to do |format|
      if @good.save
        format.html { redirect_to @good, notice: '商品が登録されました。' }
        format.json { render :show, status: :created, location: @good }
      else
        format.html { render :new }
        format.json { render json: @good.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /goods/1
  # PATCH/PUT /goods/1.json
  def update
    respond_to do |format|
      if @good.update(good_params)
        format.html { redirect_to @good, notice: '商品の登録内容が更新されました。' }
        format.json { render :show, status: :ok, location: @good }
      else
        format.html { render :edit }
        format.json { render json: @good.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /goods/1
  # DELETE /goods/1.json
  def destroy
    @good.destroy
    respond_to do |format|
      format.html { redirect_to goods_url, notice: '商品を削除しました。' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_good
      @good = Good.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def good_params
      params.require(:good).permit(:goods_id, :title, :description, :image_url, :price, :date, :maker, :category)
    end
end



表示(show)の実装です。
【app/views/goods/show.html.erb】

<h1>商品内容の表示</h1>
<br>
<p id="notice"><%= notice %></p>
<span>  </span><img height="100" style="border:ridge 5px #c0c0c0" src="<%= @good.image_url %>"/>
<br>
<br>
<p>
<font size="4px">
  <strong>【商品ID:】 </strong>
  <%= @good.goods_id %>
</font>
</p>

<p>
<font size="4px">
  <strong>【商品名:】 </strong>
  <%= @good.title %>
</font>
</p>

<p>
<font size="4px">
  <strong>【説明:】 </strong>
  <%= @good.description %>
</font>
</p>

<p>
<font size="4px">
  <strong>【画像URL:】 </strong>
  <%= @good.image_url %>
</font>
</p>

<p>
<font size="4px">
  <strong>【価格:】 </strong>
  <%= (@good.price).to_i %><span></span>
</font>
</p>

<p>
<font size="4px">
  <strong>【登録日:】 </strong>
  <%=h ((@good.date).to_date).strftime('%Y年%m月%d日') %>
</font>
</p>

<p>
<font size="4px">
  <strong>【メーカー:】 </strong>
  <%= @good.maker %>
</font>
</p>

<p>
<font size="4px">
  <strong>【カテゴリー:】 </strong>
  <%= @good.category %>
</font>
</p>
<br>
<span>  </span><%= link_to '編集', edit_good_path(@good), class: 'btn' %> <span> </span>
<%= link_to '戻る', goods_path, class: 'btn' %>



編集(edit)の実装です。
【app/views/goods/edit.html.erb】

<h1>商品の編集</h1>

<%= render 'form', good: @good %>
<br>
<%= link_to '表示', @good , class: 'btn'%><span> </span>
<%= link_to '戻る', goods_path , class: 'btn' %>



レイアウトの実装です。
【app/views/layouts/application.html.erb】

<!DOCTYPE html>
<html>
  <head>
    <title>Railsはじめてマート</title>
    <%= csrf_meta_tags %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>



<<前  [TOP]  次>>