図書管理システム karitakke 製作その２


図書管理システムkaritakkeの製作記です。単なる備忘録です。
過去の投稿は以下から！

 - [システム概要](http://qiita.com/alice02/items/cb1412f77160fa354566)
 - [製作その１](http://qiita.com/alice02/items/16198c286d685f143754)

## モデルの追加
本と貸出明細とログを管理するモデルを追加しておきます。

	$ rails g model Book title:string author:string manufacturer:string publication_date:string image:string isbn:string place:string rental_count:integer book_code:string
	$ rails g model Rental rental_date:datetime Book:references User:references Log:references
	$ rails g model Log rental_date:datetime return_date:datetime book_id:integer user_id:integer 
	$ rake db:migrate

コントローラーも追加します。

	$ rails g controller Books
	$ rails g controller Rentals

ルーティングの設定を追加します。`config/routes.rb`に以下を追加します。

``` ruby:config/routes.rb
resources :books
resources :rentals
```

次にアソシエーションの設定をします。`app/models/user.rb`に以下を追加します。

``` ruby:app/models/user.rb
class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  enum role: {admin: 'admin', member: 'member'}

  # 以下を追加
  has_many :rentals
  
  validates :name, presence: true

end
```

`app/models/book.rb`に以下を追加します。

``` ruby:app/models/book.rb
class Book < ActiveRecord::Base
	# 以下を追加
	has_one :Rental
		
 	validates :title, :author, :manufacturer, :publication_date, :isbn, :book_code, presence: true
end
```

`app/models/rental.rb`はすでに以下のようになっていると思います。

``` ruby:app/models/rental.rb
class Rental < ActiveRecord::Base
	belongs_to :Log
 	belongs_to :Book
 	belongs_to :User
end
```

## Viewの編集
先ほど作成したコントローラーに対応するViewを作っていきます。編集したもの全て記載しておきます。

### 下準備
下準備として新たに```boostrap_form```というGemを使えるようにします。`Gemfile`に追加して

	$ bundle install

を実行したら`app/assets/stylesheets/application.css`に以下を追加します。

```css:app/assets/stylesheets/application.css
*= require rails_bootstrap_forms
```
さらに、`app/assets/stylesheets/rails_bootstrap_forms.css`というファイルを作成します。

```css:app/assets/stylesheets/rails_bootstrap_forms.css
.rails-bootstrap-forms-date-select select,
.rails-bootstrap-forms-time-select select,
.rails-bootstrap-forms-datetime-select select {
  display: inline-block;
  width: auto;
}

.rails-bootstrap-forms-error-summary {
  margin-top: 10px;
}
```
これで下準備は完了です。

### books
本一覧を表示し、新しい本を登録できるようにします。

#### 一覧画面
```app/views/books/index.html.erb
<h1>蔵書一覧</h1>

<%= link_to "本の追加", new_book_path, :class => "btn btn-primary" %><br>

<hr>

<% if @books.any? %>
<table class="table table-bordered table-hover">
  <thead>
     <tr class="info"><th>ID</th><th>Titile</th><th>Author</th><th>Manufacture</th><th>Release</th><th>Operation</th></tr>
  </thead>
  <tbody>
    <% @books.each do |b| %>
    <tr>
    <td><%= b.id %></td>
    <td><%= b.title %></td>
    <td><%= b.author %></td>
    <td><%= b.manufacturer %></td>
    <td><%= b.publication_date %></td>
    <td width="230" align="center">
      <div class="btn-group">
        <%= link_to 'Show', book_path(b.id), class: "btn btn-info" %>
        <%= link_to 'Edit', edit_book_path(b.id), class: "btn btn-success" %>
        <%= link_to 'Destroy', book_path(b.id), method: :delete, data: { confirm: "削除してもよろしいですか？" }, :class => "btn btn-danger" %>
      </div>
    </td>
    </tr>
    <% end %>
  </tbody>
</table>
<% else %>
  <h4>No data yet.</h4>
<% end %>
```

#### 本の追加画面
AmazonAPIを使ってISBNから本の情報を取得できるように後ほどしたいので、検索ボタンを設置しておきます。

```app/views/books/new.html.erb
<h1>本の追加</h1>

<%= bootstrap_form_for(@book) do |f| %>
  <%= f.text_field :isbn %>
  <input class="btn btn-danger" type="button" id="info_search_button" value="検索">
  <hr>
  <%= f.text_field :title %>
  <%= f.text_field :author %>
  <%= f.text_field :manufacturer %>
  <%= f.text_field :publication_date %>
  <%= f.text_field :book_code %>
  <%= f.primary "本を追加" %>
<% end %>
```

#### 本の詳細画面
```app/views/books/show.html.erb
<h1>本の詳細</h1>

<table class="table table-bordered">
  <tbody>
    <tr>
    <td width="100", class="info">タイトル</td> <td><%= @book.title %></td>
    </tr>
    <tr>
    <td width="100", class="info">著者</td> <td><%= @book.author %></td>
    </tr>
    <tr>
    <td width="100", class="info">出版社</td> <td><%= @book.manufacturer %></td>
    </tr>
    <tr>
    <td width="100", class="info">出版日</td> <td><%= @book.publication_date %></td>
    </tr>
    <tr>
    <td width="100", class="info">ISBN</td> <td><%= @book.isbn %></td>
    </tr>
    <tr>
    <td width="100", class="info">管理番号</td> <td><%= @book.book_code %></td>
    </tr>
  </tbody>
</table>
```

#### 本の編集画面
```app/views/books/edit.html.erb
<h1>本の編集</h1>

<%= bootstrap_form_for(@book) do |f| %>
  <%= f.text_field :isbn %>
  <%= f.text_field :title %>
  <%= f.text_field :author %>
  <%= f.text_field :manufacturer %>
  <%= f.text_field :publication_date %>
  <%= f.text_field :book_code %>
  <%= f.primary "本を編集" %>
<% end %>
```

### Rentals
本の貸出・返却処理をできるようにし、貸出図書一覧を表示できるようにします。

#### 一覧画面
```app/views/rentals/index.html.erb
<h1>貸出中図書一覧</h1>

<%= link_to "本の貸出", new_rental_path, class: "btn btn-primary" %>

<hr>

<% if @rentals.any? %>
<table class="table table-bordered table-hover">
  <thead>
     <tr class="info"><th>#</th><th>タイトル</th><th>貸出日時</th><th>返却処理</th></tr>
  </thead>
  <tbody>
    <% @rentals.each_with_index do |r, i| %>
    <tr>
    <td><%= i+1 %></td>
    <td><%= r.Book.title %></td>
    <td><%= r.rental_date.in_time_zone('Tokyo') %></td>
    <td width="100" align="center">
      <%= link_to '返却', rental_path(r.id), method: :delete, data: { confirm: "この本を返却します。よろしいですか？" }, :class => "btn btn-danger" %>
    </td>
    </tr>
    <% end %>
  </tbody>
</table>
<% else %>
  <h4>No data yet.</h4>
<% end %>
```

#### 新規貸出画面
こちらも本の管理番号(book_code)を入力したらデータベースから該当する本の情報を補完できるように、検索ボタンを設置しておきます。また、エラーメッセージなども表示できるようにしておきます。

```app/views/rentals/index.html.erb
<h1>本の貸出</h1>

<%= bootstrap_form_for(@book, :url => rentals_path) do |f| %>
  <%= f.text_field :book_code %>
  <input class="btn btn-danger" type="button" id="search_button", value="検索">
  <b id="error_msg">  </b>
  <hr>
  <%= f.text_field :title, :readonly => true %>
  <%= f.text_field :author, :readonly => true %>
  <%= f.text_field :manufacturer, :readonly => true %>
  <%= f.text_field :id, :readonly => true %>
  <%= f.primary "貸出", id: "rental_button", disabled: "disabled" %>
<% end %>
```

## Controllerの編集
### Books
Bookモデルに対応するコントローラです。destroyメソッドで削除しようとしている本が貸出中の場合は削除できないようにしています。
AmazonAPIから情報を取得する部分は次回実装します。


```ruby:app/controllers/books_controller.rb
class BooksController < ApplicationController

  before_action :set_book, only: [:show, :edit, :update, :destroy]

  def index
    @books = Book.all
  end

  def show
  end

  def new
    @book = Book.new
  end

  def create
    @book = Book.new(book_params)
    if @book.save
      redirect_to books_path
    else
      render 'new'
    end
  end

  def edit
  end

  def update
    if @book.update(book_params)
      redirect_to books_path
    else
      render 'edit'
    end
  end

  def destroy
    if @book.Rental_id.nil?
      @book.destroy
      redirect_to books_path
    else
      redirect_to books_path, notice: "その本は貸出中のため削除できません。"
    end
  end

  private

    def book_params
      params[:book].permit(:title, :author, :manufacturer, :publication_date, :isbn, :book_code)
    end

    def set_book
      @book = Book.find(params[:id])
    end

end
```

### Rentals
以下のようになります。createメソッドでは入力フォームに入力された情報を元に、貸し出す本を検索します。そして、モデルオブジェクトを生成し、UserとBookの紐付け、および貸出日時をセットします。さらに、貸し出す本の貸出回数のカウントをインクリメントしてRentalと紐付けをします。
destroyメソッドは本の返却を意味します。該当する本から紐付けを解除します。

```ruby:app/controllers/rentals_controller.rb
class RentalsController < ApplicationController

  def index
    @rentals = current_user.Rentals.all
  end

  def new
    @book = Book.new
    @rental = Rental.new
  end

  def create
    @book = Book.find(params[:book].require(:id))
    if @book.Rental_id.nil?
      @rental = Rental.new(rental_date: DateTime.now)
      @rental.User = current_user
      @rental.Book = @book
      if @rental.save
        @book.increment(:rental_count)
        @book.Rental_id = @rental.id
        @book.save
        redirect_to rentals_path
      else
        render 'new'
      end
    else
      redirect_to new_rental_path, notice: "その本はすでに貸出中です。"
    end
  end

  def destroy
    @rental = current_user.Rentals.find(params[:id])
    @rental.Book.update(Rental_id: nil)
    @rental.destroy
    redirect_to rentals_path
  end

end
```

## 今回はここまで！
次回はjQueryとAjaxを使ってフォームの自動入力を実装する予定です。