1、find vs. find_by vs. where

1-1、find的用法

find只能抓一個,()裡面的就是model的流水編號

> WishList.find(3) 
> 
> 這個find就是抓到該model流水編號為3的實體

find只能抓流水編號


1-1-1、find如果抓不到東西

用find,找不到東西的話,會噴ActiveRecord::RecordNotFound

1-2、find_by用法

find_by可以抓id以外的東西,除了id以外都可以

> WishList.find_by(id: params[:id]) 
> 
> find_by可以抓id以外的東西,如果今天是要抓model裡面name的欄位,就是下面這樣寫
> WishList.find_by(name: "good")

1-2-1、find_by如果抓不到東西

但是用find_by找不到東西的話,只會給你一個Nil

BUT!!如果今天是這樣寫(find_by!),也會噴錯誤給你

> WishList.find_by!(id: 999)

1-3、where用法

where抓到的資料是一個陣列(給你一堆結果塞到陣列裡面)

> WishList.where(id: 2)

> 印出 [#<WishList id: 2, title: "sdf", description: "qwe">]

1-3-1、where如果抓不到東西

where如果找不到東西,會給一個空陣列

2、ApplicationController 在做啥事

如果今天所有controller,都有共通行為,就可以直接寫在ApplicationController

2-1、狀況一

如果以後會員的登入狀態,就可以放在這邊

2-2、狀況二

把遇到ActiveRecord的狀況修改都放到這一層,這樣所有controller遇到都可以解決

> class ApplicationController < ActionController::Base
>     rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
> 
>     def record_not_found
>         #   render html: "找不到", status: 404
>           render file: "#{Rails.root}/public/404.html", status: 404, layout: false
>     end
> end

3、抽象類別

> class Animal        -> 這個東西是抽象類別
> end
> 
> class Cat < Animal
> end
> 
> kitty = Cat.new
> a = Animal.new      -> 用抽象類別產生新實體,ruby沒有支援此用法

Ps. 有些語言有支援抽象類別,但是ruby沒有

rails model裡面這個檔案 -> application_record.rb裡面有寫這一句
他的意思是,不要用此類別產生新實體喔!~

> class ApplicationRecord < ActiveRecord::Base
>   self.abstract_class = true
> end

4、WishList製作功能 - UD

4-1、第十三步驟:設定edit

路徑設定routes

今天要來做更新之前存取的檔案,所以先來設定編輯路徑
如果今天是自己客製路徑,只要在直接用前面action show寫過的路徑,在網址的最後面,加一個edit就好

get "/wish_card_info/:num/edit", to: "wish_list#edit_wish", as: "edit_wish_card_info"

設定好之後,到index那邊加上link_to
記得要帶路徑給這過path喔

> <td><%= link_to "edit", edit_wish_card_info_path(wish_list.id) %></td>    

接下來把edit的action、view做好 因為我們要抓每個實體,所以記得要抓id

def edit_wish
    @wish_list = WishList.find(params[:num])
end

接下來我們直接複製new的表單,並在controller把find放上去

# edit controller

def edit_wish
    @wish_list = WishList.find(params[:num])
end

edit view(這邊的路徑後面要改過,要改成update路徑,這邊先放錯誤的,只是要給大家看edit的演變)

> <%= form_for @wish_list, url: "/wish_card_list" do |form| %>
>     <div>
>       <%= form.label :title %>
>       <%= form.text_field :title %>
>     </div>
> 
>     <div>
>       <%= form.label :description %>
>       <%= form.text_area :description %>
>     </div>
> 
>   <%= form.submit %>
> <% end %>

此時會發現表單都有數據了!!!!原因就是因為假設今天實體是有料的,rails就會判斷你是要來更新這個實體

4-2、第十四步驟:設定update

再來更改update的路徑,記得update的動詞是用patch喔

patch "/update_wish_card_info/:num", to: "wish_list#update_wish", as: "update_wish_card_info"

先把剛剛複製到edit view的form_for路徑改掉

> <%= form_for @wish_list, url: update_wish_card_info_path(@wish_list) do |form| %>  #改新路徑,記得要帶id
>     <div>
>       <%= form.label :title %>
>       <%= form.text_field :title %>
>     </div>
> 
>     <div>
>       <%= form.label :description %>
>       <%= form.text_area :description %>
>     </div>
> 
>   <%= form.submit %>
> <% end %>

html表單的動詞只有兩種
(1) 一個是get
(2) 一個是post
但是其他的語言有另外的動詞


如果html只有兩種動詞,那為何rails這邊可以寫其他動詞呢??

原因就是如果今天有patch的動詞,rails會在html塞一個隱藏欄位 就是下面這個,這一段是form_for幫忙新增的

> <input type="hidden" name="_method" value="patch" autocomplete="off">

Ps. 所以當html更新資料的時候,還是用get/post,只是rails會多塞一個動詞,假裝是用patch方法

接著實際按下更新按鈕,並在action寫下update的params,我想要看實際送出後,抓到了哪些東西

# update的controller

def update_wish
    render html: params
end

上面我們把update的params印出來,就會印出這一串,可以發現有一個_method: “patch”,這個就是前面提到的,rails會塞一串到html

{"_method"=>"patch", "authenticity_token"=>"1qEcF-hbYEieLQCTndFM5mnDOxa6qZgEGG9AqHb45GvJoc7FodZqZlZtOEVkL9OKUecZXPcrS6Xv9V2NXzSn4A", "wish_list"=>{"title"=>"sdf", "description"=>"qwe"}, "commit"=>"Update Wish list", "controller"=>"wish_list", "action"=>"update_wish", "num"=>"2"}

確定update後,確實有抓到東西,我們就可以把資料更新進資料庫了
Ps. 直接拿create來用就好,同樣的概念,update裡面要帶clean_params

def update_wish
    @wish_list = WishList.find_by(id: params[:num])

    if @wish_list.update(clean_params)
        redirect_to make_a_wish_path, notice: "success update"
    else
        render :edit
    end
end

4-3、第十五步驟:設定刪除

因為rails的慣例,會直接跟show的路徑一樣,只是記得要改動詞,改成delete

delete "/wish_card_info/:num", to: "wish_list#destroy_wish", as: "destroy_wish_card_info"

接著到首頁把刪除的link_to加上去(記得要加上method: “delete”),才能跟show的路徑做區別

> <td><%= link_to "delete", destroy_wish_card_info_path(wish_list.id), method: "delete" %></td>

data-method是啥???

這些data的屬性,在html是沒有效果的
但是rails發現,假如你有data的屬性,而且又是站內連結,他會幫你用post這個動作幫你送連結

但是他是怎麼弄的呢?rails會動態幫你產生表單(JS用createElement產生)
document.createElement(“form action method input type hidden”)

Ps. 這很重要,因為一般HTML能用post動作的,只有表單能用post,其他人都不能用,一般a-link只能用get送東西

接下來到controller設定資料刪除,這樣就可以刪掉了

def destroy_wish
    @wish_list = WishList.find_by(id: params[:num])

    @wish_list.destroy
    redirect_to make_a_wish_path, notice: "data delete"
end

不過這樣是不是覺得丟點危險?不小心點到就直接刪除了,我們現在新增一個警示

> <td><%= link_to "delete", destroy_wish_card_info_path(wish_list.id), method: "delete", data: {confirm: "確定嗎???"} %></td>

這樣寫後,html會增加一個東西,就是data-confirm,他可以幫助你做出confirm警示

> HTML

> <a data-confirm="確定嗎???" rel="nofollow" data-method="delete" href="/wish_card_info/4">delete</a>

5、優化程式碼

首先我們先把兩個方法寫在private的地方,原因是因為這兩個方法使用過兩次以上

private
def clean_params
    params.require(:wish_list).permit(:title, :description)
end

def find_wish_list
    @wish_list = WishList.find_by(id: params[:num])
end

5-1、優化一:before_action

把在action重複做的事情,用before_action來寫,並在一開頭的地方,加上before_action

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

before_action另外一個用法,except
這個意思是,除了這三個不做用,其他都要做用
但是這個邏輯多了一圈,所以不習慣的話,可以先不用

before_action :find_wish_list, except: [:index, :new, :create]

5-2、優化二:clean_params

clean_params剛剛已經寫成方法,直接塞進create、update裡面就

# new action
@wish_list = WishList.new(clean_params)

# update action
if @wish_list.update(clean_params)

5-3、優化三:_form

表單重複的話,放到另外一個資料夾、檔案,這個概念叫做render partial(部分渲染)

> <%= render "form" %>

今天想要在_form 的地方,給一個區域變數,這個區域變數是被new or edit的html地方餵食來的
為什麼要這樣做呢??原因就是因為,假如你今天一個頁面,有三塊廣告版面,想當然這三塊版面不可能圖片都一樣
所以就要用到區域變數的概念,在_form設一個區域變數,值都是由render partial那邊餵食來的

new、edit的view介面

> <%= render "form", wish_list: @wish_list %>

上面聽不懂的話,下面舉一個更好的案例,_form先給表單,注意這邊的form_for後面接的是區域變數

> _form的view

> <%= form_for ad do |form| %>          # ad 是區域變數喔
> <% end %>

接著實體變數在這邊設定,並把區域變數餵食給_form
Ps. @ad1、@ad2、@ad3這些的值,是由action丟過來的,並且再送到_form

> new的view

> <%= render "ad", ad1: @ad1 %>         # 區域變數接住實體變數後,把區域變數丟給_form
> <%= render "ad", ad2: @ad2 %>
> <%= render "ad", ad3: @ad3 %>

6、Model.save.errors

假設今天new一個新實體,不過假如有一個欄位是必填的,在model.save時,會噴錯誤,實際情況到底發生什麼事呢??

> aa = WishList.new           > new新實體
> 
> aa.errors?                  # false  >  現在還沒有錯(還沒寫進資料庫) 
>         
> aa.save                     # false  >  儲存發生錯誤(寫不進資料庫)
> aa.errors?                  # true   >  有錯了(再次詢問是否有錯誤,有錯了)
> 
> 
> aa.errors.full_massages     # name must exist  > 把錯誤印出來

rel=”nofollow”
html的屬性,基本上是給爬蟲或是SEO抓的


7、非侵入式JavaScript(UJS = Unobtrusive JavaScript)

非侵入式JavaScript是一種將JavaScript從HTML結構抽離的設計概念,避免在HTML標籤中夾雜一堆onchange、onclick等屬性去掛載JavaScript事件,讓HTML與JavaScript分離,他的幾本原則包括

(1) 將網頁的行為層和表現層分離開
(2) 是解決傳統JavaScript編程問題(瀏覽器呈現不一致,缺乏擴充性)的最佳實踐
(3) 為可能不支援JavaScript進階特性的使用者代理(通常是瀏覽器)提供漸進增強的支援


拉一個新專案的時候,要做兩件事
(1) bundle
(2) yarn install


8、會員系統

接下來要做會員系統,先來訂model規格

8-1、Model = User

|欄位名稱|資料型態|補充| |:-:|:-:|:-:| |nickname|string|暱稱| |email|string|信箱(必填)| |password|string|密碼|

設計好規格後,可以產生model,記得要具現化表格

rails g model User nickname email password
rails db:migrate

16:20 流水編號問題

訂單編號問題