---
# System prepended metadata

title: ＣＲＵＤ建立＆一些原理（完）

---

# ＣＲＵＤ建立＆一些原理（完）

## edit/update
先在index.html.erb加上編輯的連結

```erb
<h1>Hello World!!</h1>
<%= link_to "新增",new_book_path %>
<% @books.each do |book|%>

  <%= link_to book.title,book_path %>
  <%= link_to "編輯",edit_book_path(book) %>

<% end%>
```
edit_book_path後面加上(book)，讓他找得到book的id。

回到controller寫edit的action
```ruby
  def edit
    @book = Book.find(params[:id])
  end
  ```
  有沒有覺得很熟悉，沒錯直接複製show的就好了，他們都是去找特定id的頁面。
再來寫update的action，一樣也是複製貼上，這次複製create的內容，再一點點修改。
```ruby
  def update
    @book = Book.find(params[:id])
    if @book.update(book_params)
      redirect_to books_path
    else
      render :edit
    end
  end
```
update的資料要先清洗過，render換成edit頁面。

再來寫view，建立edit.html.erb

```erb
<h1>編輯</h1>
<%= form_with model:@book do |form| %>
  <div>標題：<%=form.text_field :title %></div>
  <div>內容：<%=form.text_area :content %></div>
  <div>價錢：<%=form.text_field :price %></div>
  <%= form.submit "編輯商品"%>
<% end %>
```
有沒有覺得很眼熟，沒錯，跟new的view一樣，複製貼上換上編輯看個字而已。
這兩個的code重複使用的時候，可以建立一個檔案，讓他們去render。
建立＿form.html.erb。名字可隨意取，但是最前面要加上_

```erb
  <%= form_with model:book do |form| %>
  <div>標題：<%=form.text_field :title %></div>
  <div>內容：<%=form.text_area :content %></div>
  <div>價錢：<%=form.text_field :price %></div>
  <%= form.submit %>
<% end %>
```

注意的兩點，把實體變數變成一般的變數，@book => book 。
再來是submit的內容因為略有不同，所以刪掉讓submit自動生字。

這時候的new.html.erb/edit.html.erb

```erb
<h1>新增</h1>

<%= render "form", book: @book %>
```

```erb
<h1>編輯</h1>

<%= render "form", book: @book %>
```
render _form.html.erb的內容，然後將變數book指定成@book。

## destroy
先在index的view寫上刪除的連結
```erb
<h1>Hello World!!</h1>
<%= link_to "新增",new_book_path %>
<% @books.each do |book|%>

  <%= link_to book.title,book_path %>
  <%= link_to "編輯",edit_book_path(book) %>
  <%= link_to "刪除",book_path  %>
<% end%>
```
當你點擊刪除的時候，你會發現你的網頁會跑向show的頁面。
![](https://i.imgur.com/lEHPnVu.png)

![](https://i.imgur.com/lhuRrau.png)

rails routes明明寫著Perfix也是book。
![](https://i.imgur.com/0t1ePqP.png)

在這裡show/update/destroy都是共用book_path，我們要再後面加上method，來區分它們。

```erb
<h1>Hello World!!</h1>
<%= link_to "新增",new_book_path %>
<% @books.each do |book|%>

  <%= link_to book.title,book_path %>
  <%= link_to "編輯",edit_book_path(book) %>
  <%= link_to "刪除",book_path,method: "delete"  %>
<% end%>
```
為了避免使用者按了就直接刪除，給他加一個警告確認
book_path可以簡寫成book

```erb
<h1>Hello World!!</h1>
<%= link_to "新增",new_book_path %>
<% @books.each do |book|%>

  <%= link_to book.title,book %>
  <%= link_to "編輯",edit_book_path(book) %>
  <%= link_to "刪除",book,method: "delete" ,data: {confirm: "是否刪除？"}  %>
<% end%>
```
![](https://i.imgur.com/7OknBnu.png)
按下去後看到眼熟的紅色框框，Unknown action，代表我們做對方向了，補上destroy action
![](https://i.imgur.com/1FSAE66.png)

```ruby
class BooksController < ApplicationController
    
  def destroy
    @book = Book.find(params[:id])
    @book.destroy
    redirect_to books_path
  end
    
end
```
這樣刪除就完成了，到這邊CRUD的功能已經具備，但是如果仔細一看，會發現```@book = Book.find(params[:id])```這行code一直不斷重複使用。

當你相同的東西一直重複使用的時候，就應該把他寫成一個方法，讓大家都能使用。

```ruby
private

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

但是如果只是這樣還不夠，難道我們要一直重複呼喚這個方法。
在Rails有一個寫法叫做before_action，他會讓方法直接默認的在action裡使用過，不用在多個action一直重複寫這方法。

before_action放在最上面，要在action之前執行
```ruby
class BooksController < ApplicationController
  before_action :find_book,only: [:show, :edit, :update, :destroy]
  
  def index
    @books = Book.order(id: :desc)
  end
  .
  .
  .
end        
```

before_action有兩種寫法，only是只有這些action執行方法，另一個except則是除了這些action以外通通都要執行。

```[:show, :edit, :update, :destroy]```這四個action的```@book = Book.find(params[:id])```就可以刪掉了。

CRUD的建立就此完成，最後放上controller全部內容，感謝閱讀。

```ruby=
class BooksController < ApplicationController

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

  def index
    @books = Book.order(id: :desc)  
  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 show
  end

  def edit
  end

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

  def destroy
    @book.destroy
    redirect_to books_path
  end

  private

  def book_params
    params.require(:book).permit(:title,:content,:price)
  end

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