over 2 years ago

來源:RailsFun.tw 新手教學 day2.5 HD

00:01:45

rails new 之後
要先設定database.yml

default:
    adapter:mysql2
    username:
    password:

production:
    username:
    password:

00:02:00

gem 'will_paginate'
gem 'awesome_print'
gem 'rails-pry'
gem 'devise'
gem 'paperclip'

bundle install

00:04:35

先設routes,只要他的首頁

routes.rb
resources :statics, only =>[:index]

namespace是一個特殊的結構

routes.rb
namespace :dashboard do
    resources :items
end

會變這樣:

/dashboard/items/action

00:08:00

admin建議取成亂碼

從管理介面開始打,因為管理頁面會比前台多

routes.rb
namespace :dashboard do #使用者介面

    resources :orders
    namespace :admin do #管理者介面
      resources :items
      resources :cates
      resources :orders
      resources :users
      resources :managers
     end

end

00:09:00

gem devise

00:18:00

mysql2的最新版本有問題,'0.4.0'
所以要在Gemfile裡面指定版本

gem 'mysql2' , '0.3.20'

00:19:00

rails g devise User
rails g devise Manager

之後,migration的檔案裡面要做些什麼修改

00:23:00

弄一個新的migration設定items 跟cates

create_table :items do |t|

記得要加軟刪除

t.timestamp :delete_at

00:29:00

建議要加add_index

add_index :order_items, [:order_id]

建議用array []
所有會被where到的都要加index
所有會關聯到的column都要加index

create_table :order_items do |t|
    t.interger :order_id, :null => false 要加
    t.interger :item_id, :null => false 要加
    t.interger :user_id, :null => false 要加
    t.interger :price, :null => false
end

00:31:00

gem paperclick
一定要用imagemagic

00:33:35

要做相簿式的話,可能需要一對多的表

00:34:00

參考railsfun的文章,可以把
cover_file_name, cover_content_type, content_file_size, cover_update_at都刪掉

00:35:00

manager.rb,管理員不允許被註冊,所以:registerable要砍掉

00:37:00

item.rb
講paperclip的使用,railsfun有專文

styles: {
    original: 預設會有,所以要把他設好
}

00:41:00

items要加cates的column

add_column :items, :cate_id, :integer, :null => false

一個item一定要歸在某個分類

00:45:00

開始打controller,繼承結構很重要

有兩個namespace,就要建兩個資料夾
mkdir dashboard
mkdir admin

class ApplicationController < ActionController::Base

如果不想被ApplicationController所影響,可以直接繼承ActionController::Base。
如果要偷懶,可以繼承ApplicationController。所以方便性會增加。
=> 繼承ApplicationController就好

要另外做一個dashboard_controller.rb
使用者要登入,才可以進來
之後繼承Dashboard::DashboardController,都不用再打一次before_action

dashboards_controller.rb
class Dashboard::DashboardController < ApplicationController
    before_action :authenticate_user!
end

(Dashboard::DashboardController,意思是namespace::controller,namespace下面的controller)

譬如:orders_controller.rb,就不用再打一次before_action
不過前面還是要記得加Dashboard::

oders_controller.rb : 這邊的寫法,代表繼承Dashboard::DashboardController
class Dashboard::OrdersController < Dashboard::DashboardController

end

要另外做一個admin_controller.rb

admin_controller.rb
class Dashboard::Admin::AdminController < ApplicationController
    before_action :authenticate_manager!
end

(1) Dashboard::Admin:namespace下面的namespace
(2) Admin::AdminController:這層namespace的第一個controller (第一個通常會跟namespace的那個相同名稱)
(3) Admin::AdminController < ApplicationController:第一個controller,所以要繼承ApplicationController
(4) 第一個controller:打一次before_action,之後下面繼承的就不用打了

items_controller.rb:不用再打一次before_action :authenticate_manager!
class Dashboard::Admin::ItemsController < Dashboard::Admin::AdminController 

end

00:53:00

用console 建管理員,不能夠有管理員的註冊頁面

1:00:00

開始講will_paginate

@items = @paginate = Item.paginate(:page => params[:page])

多加一個@paginate
在前端會很好用,就可以寫在:

veiws/layouts/application.html.erb
<%= will_paginate @paginate if @paginate %>

1:05:00

開始講登入、登出、註冊的按鈕
有裝devise,就可以用current_user或current_manager
取決於Model name

<% if current_user %>
    Hi <%= current_user.email %> 
    <%= link_to "登出" , destroy_user_session_path, :method => "delete" %>
<% else %>
    <% link_to "註冊" , new_user_registration_path %>
    <% link_to "登入" , new_user_session_path %>
<% end %>

1:15:00

開始手動改scaffold的檔案

1:18:00

因為有namespace的關係,所有_form.html.erb會有錯,要把<%= form_for (@item) do |f| %>
拉到外面那一層去。裡面只剩下submit就好

path也要改掉,edit的下一步是update,所以要改成update的path

edit_items_html.erb
<%= form_for(@item, {:url => dashboard_admin_item_path,:method => 'patch'} do |f| %>
    <%= render 'form' %>
<% end %>
new_items_html.erb
<%= form_for(@item, {:url => dashboard_admin_items_path,:method => 'ppost'})do |f| %>
    名稱<%= f.text_filed :name %></br>
<% end %>

在1:45:00 會重新修改
用form_for helper rails去找form_for的document
會發現form_for後面要有url

1:26:00

開始講admin / dashboard / application 的layout的差別

使用者頁面:

layouts/dashboard.html.erb

不需要 if current_user,因為已經強制登入了

後台頁面:

layouts/admin.html.erb

不需要 if current_manager,因為已經強制登入了
要改成manager.email
可以把各種管理選單打在這邊
如:商品的index頁面

<ul>
    <li><%= link_to "商品管理", dashboard_admin_items_path %></li>
</ul>

記得要到controller裡面加上

admin_controller.rb
layout 'admin'
dashboard_controller.rb
layout 'dashboard'

所有繼承他的controller都會套用layout

1:30:00

因為之前建過items.rb與cates.rb了,所以scaffold之後的migrate會出現問題,到migrate裡面把重複的檔案刪掉就好

1:31:00

解釋為什麼不能用is_admin

1:35:00

開始講faker

100.times do |i|
    Item.create(:name => Faker::Address.state, :price => rand(1..3000), :cate => '大雜燴')
end; true
Faker::Internet.user_name

3.times do |i|
  user_name = Faker::Internet.user_name
  email = "#{user_name}@example.com"
  password = "#{user_name}" + "#{user_name}"
    User.create(:name => user_name, :email => email, :password => password) 
end;true

User(id: integer, name: string, created_at: datetime, updated_at: datetime, email: string, encrypted_password: string, reset_password_token: string, reset_password_sent_at: datetime, remember_created_at: datetime, sign_in_count: integer, current_sign_in_at: datetime, last_sign_in_at: datetime, current_sign_in_ip: string, last_sign_in_ip: string, avatar_file_name: string, avatar_content_type: string, avatar_file_size: integer, avatar_updated_at: datetime) 

https://cuppycode.wordpress.com/2013/04/08/creating-dummy-users-with-faker/

10.times do |n|
      puts "[DEBUG] creating user #{n+1} of 10"
      name = Faker::Name.name
      email = "user-#{n+1}@example.com"
      password = "password"
      User.create!( name: name,
                    email: email,
                    password: password,
                    password_confirmation: password)
    end

1:37:00

item一定要有一個default的cate_id
(41:00設定:null => false)

1:39:00

後台通常都是倒敘排序:DESC

def index
    @items = @paginate = Item.order('id DESC').paginate(:page => param[:page])
end

1:40:00

redirect_to :action => :index

會等於redirect_to xxx_path

1:42:00

開始講private

def item_params
    params.require(:item).permit!
end

1:47:00

edit送出後,出現錯誤訊息nilClass
表示update的地方出錯了
加上

@item = Item.find(params[:id])

就好了

1:47:35

開始要來加圖片了

edit.html.erb
<% f.file_field :cover %>

1:52:00

在產品列表頁的地方

<%= image_tag item.cover.url(:icon), :width => 150, :height => 150 %>

1:53:00

開始講SELECT
用rails helper select form搜尋
類別

<%= f.select :cate_id, Cate.all.map{ |c| [c.name, c.id]}%>

1:57:00

view的地方要顯示類別需要一個join的動作,不然log的地方會很醜
1:58:48 log很醜
每次找item,都會到表去抓一次cate
一定要解決,不然會拖累效能
出現這樣的log代表view是有問題
解法在controller內

def index
    @items = @paginate = Item.includes(:cate).paginate(:page => params[:page])
end

加上includes就好了
先去把所有的item拉出來,去找裡面的cate id 到底有多少個,只有四個,然後再全部拉出來,再拼回去

1:59:20

醜的

SELECT `items`.* FROM `items` LIMIT 30 OFFSET 0
SELECT `cates`.* FROM `cates` WHERE `cates`.`id` = 7 LIMIT 1

2:00:00

漂亮的

SELECT `items`.* FROM `item` LIMIT 30 OFFSET 0
SELECT `cates`.* FROM `cates` WHERE `cates`.`id` IN (7,4,9,1) 

2:01

item_controller
item/index.html.erb

2:03

<td>加入購物車</td>
<%= image_tag item.cover.url(:icon) %>

2:04:45

加入購物車這件事情很困難
要配javascript跟session,不然做不起來

先去items_controller

(1) 預計會給id,所以要先去加route

def add_cart

end

(2)

resource :items do
    memeber do (單一產品,會有id)(items/:id/yoo)
    end
    collection do (當作集合體)
    end
end

(3) 先寫route

resource :items do
    memeber do (單一產品,會有id)
      get :add_cart  
    end
    collection do (當作集合體)
    end
end

2:08:20

(1) 回到items_controller

items_controller
def add_cart
  params[:id] #會得到item的:id 觀察routes

  session[:cart] ||= {}  :cart是自訂
  #hash跟array都可以,不過array會有次序性,可以追蹤足跡,購物車用{}就好

end

2:09:45

(1)

def add_cart
  
  session[:cart] ||= {}  :cart是自訂
  #這是initialize,如果沒有的話,塞進{},如果有的話,不動session[:cart]

  
  會得到params[:id],但要先驗證params[:id]存不存在
  item = Item.where(:id => param[:id].first
  #不用@變數,因為沒有view,所以用區域變數item


  if item (如果有item的話)
  session[:cart][item.id] ||= 0
  session[:cart][item.id]  +=1
  end
  render :json => {:counter => session[:cart].length}.to_json #最後得到一個:json,也就是一個counter(key)對一個數量(value)


end

2:12:20

layout/application.html.erb

2:12:40

app/helpers/application_helper
view可以直接用 => layout/application.html.erb
def get_cart_count #如果session有東西,把長度丟出去,沒有就丟0回去

    if session[:cart]
        return session[:cart].length
    else
        return 0
    end
end
layout/application.html.erb
<div>購物車數量: <span id="cart_counter"><%= get_cart_count %></span></div>

記得加span,因為等一下要用ajax的東西
get_cart_count這是剛剛的helper

2:16:00 開始要弄ajax

view/layout/application.html.erb

2:16:30

(1) 在view/layout/application.html.erb挖洞
要放javascript跟css的地方

view/layout/application.html.erb
<head>
        <%= yield :header %> =>  對應 
        <% content_for :header do %>
</head>

(2) 加到item/index.html.erb

item/index.html.erb
<% content_for :header do %>
<% end %>

(3) 這是jQuery的ready語法

<script>
    jQuery(function($)){

    };

</script>

2:18:00 加入購物車這個超連結要做的事情

<td><a href="#" class="add_cart", data-value="<% item.id %>">加入購物車</a></td>

class="add_cart"是為了jQuery
data-value="<% item.id %>

value是item的id
data-value也是jQuery的用法

2:18:00

<script>
    jQuery(function($)){
     $('.add_cart').click(function(){ 
        #當我按下去$('.add_cart')(加入購物車)之後要幹嘛,我預設按下去之後會增加到購物車的URL,而且用GET的方式去做,他就會touch他之後再回給我東西

     })
    };

</script>

2:19:45

rake routes找購物車的網址,前面是get

add_cart_item => /item/:id/add_cart

(add_cart Method)
(為了要找到item的id)

2:19:55

(1) jQuery有一個語法是$.

<script>
    jQuery(function($)){
     $('.add_cart').click(function(){ #當我click之後要幹嘛,我預設按下去之後會增加到購物車的URL,而且用GET的方式去做,他就會touch他之後再回給我東西

        $.getJSON('/item/:id/add_cart') #click之後,會跑到add_cart這個method去執行,最後render這個網址的Json

     })
    };

</script>

(2)2:20

'/item/:id/add_cart'

:id是我們需要填值的地方

<script>
    jQuery(function($)){
     $('.add_cart').click(function(){ #當我按下去之後要幹嘛,我預設按下去之後會增加到購物車的URL,而且用GET的方式去做,他就會touch他之後再回給我東西

        $.getJSON('/item/'+ $(this).attr('data-value') + '/add_cart') 
        #要把:id改成正確的網址,所以要去找到 data-value

        #預設會是id進去,所以我們要把id取回來:data-value="<% item.id %>"value是item的id


     })
    };

</script>

(3) 得到網址後,預計會得到一個jSON回來: function(json){},json回來後,要去更新購物車數量$('#cart_counter')

<script>
    jQuery(function($)){
     $('.add_cart').click(function(){ #當我按下去之後要幹嘛,我預設按下去之後會增加到購物車的URL,而且用GET的方式去做,他就會touch他之後再回給我東西

        $.getJSON('/item/'+ $(this).attr('data-value') + '/add_cart') , function(json){
            $('#cart_counter').html(json.counter);

        }
        #得到URL之後,預計會得到一個jSON回來: function(json){}

        #json是要去更新到某一個地方 => $('#cart_counter'),也就是購物車數量:0 

        (<div>購物車數量: <span id="cart_counter"><%= get_cart_count %></span></div>)

     })
    };

</script>

2:21:00

看item_controller的最後一步,會有 render :json => {:counter => session[:cart].length}.to_json
不管if的地方的結果是什麼,最後都會得到一個counter(key)對一個數量(value)

這邊function(json),最後會得到json.counter

:json => :counter

function(json){
$('#cart_counter').html(json.counter);
}

2:21:50

要在click最後面加false,因為如果不想觸發那個按下去的event的話,

2:22:30

噴了之後,要看500

去console看500
點500的連結,會跑到Network去
然後再refresh一次,會將錯誤的地方標成紅色的。
add_cart出現錯誤
看Response

其實console log也可以看到error
(這邊其實只是字打錯)

2:25:00 講debug

layout/applicationn.html.erb
<%= session[:cart] %>

就可以看出來每一個品項點了幾次

2:26:00 看為什麼品項沒有變化

items_controller

2:27:00

發現要改成string

2:27:50 發現要把item.id從string改成integer

(JC說要session[:cart][key]變string,可是打.to_i)

2:28:00

可是後來又打成.to_s

if item
    key = item.id.to_s
    session[:cart][key] ||=0
    session[:cart][key] += 1

2:28

{"1" => 6.....}沒有jax所以不會馬上動

2:29:00

開始教CSS

真正上production都會用ul跟li

2:36:30

之後就是去做結帳的動作
把session去做結帳動作
把session的item列出來
"1" => 6
item的id 數量是value
送出:建立一張order,上面就是所有的item
使用者在後台可以看到,列出所有項目,然後付款

2:38:00

建一個Status

order.rb
has_many :order_itemm

STATUS = [['新增',1], ['付款中',2], ['已對帳',3],['已出貨',4]]['問題單', 5]]

接到SELECT TAG

關掉再進入還在
登出再登入 / 換瀏覽器 就會清除session

2:42:25

可以不用session做,可以開table來塞
誰,物品,跟數量

← RailsFun tw 新手教學 day7 HD simple_GPA_calculator →