iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 10
0
Modern Web

向 Rails 致敬!30天寫一個網頁框架,再拿來做一個 Todo List系列 第 10

[DAY 10] 復刻 Rails - MVC 的最後一張拼圖 - Model

當我們開始製作頁面的時候,很快會發現一件事情,沒有資料庫的網站終究只是個靜態頁面,但當我們想要結合資料庫時,接踵而來的問題就開始產生,例如怎麼連到資料庫?怎麼查詢資料?怎麼把資料丟到頁面?尤其是光想到要面對那些 SQL 語法就很頭痛

但也這就是 Rails Model 厲害的地方,你剛剛所想的問題都幫你處理好了,你只要有物件導向的觀念,會用所謂的 ORM(Object-relational mapping),就可以省下很多開發時間,讓你可以更專注在開發產品上面

也因為 Model 是如此重要,而且不容易了解,所以接下來會花比較多的篇幅來介紹 Model 的部分,為了循序漸進,我們先從透過操作 JSON 格式檔案來模擬資料庫,熟悉一下 Model 在做些什麼事情

MVC 的 M

在開始之前,要先在 Mavericks 安裝 multi_json,我們將會用它做 JSON 轉成 Hash 的處理

# mavericks/mavericks.gemspec

.
.
(略)
spec.add_runtime_dependency "rack"
spec.add_runtime_dependency "erubi"
spec.add_runtime_dependency "multi_json"

這裡會建立一個叫 file_model 的 Class,之後會分段說明程式碼

# mavericks/lib/mavericks/file_model.rb

require "multi_json"

module Mavericks
  module Model
    class FileModel
      def initialize(file)
        @file = file
        basename = File.split(file)[-1]
        @id = File.basename(basename, ".json").to_i

        obj = File.read(file)
        @hash = MultiJson.load(obj)
      end

      def [](name)
        @hash[name.to_s]
      end

      def []=(name, value)
        @hash[name.to_s] = value
      end

      def self.find(id)
        begin
         FileModel.new("db/tasks/#{id}.json")
        rescue
         return nil
        end
      end
    end
  end
end

別忘了要記得 require

# mavericks/lib/mavericks.rb

# .
# .
# (略)
require "mavericks/file_Model"

module Mavericks
  class Error < StandardError; end

  class Application
  # .
  # .
  # (略)

先看 initialize,這段程式碼要做的事情是,從檔名中取得 ID

# mavericks/lib/mavericks/file_model.rb

def initialize(file)
  @file = file
  basename = File.split(file)[-1]
  @id = File.basename(basename, ".json").to_i

  obj = File.read(file)
  @hash = MultiJson.load(obj)
end

File.split 會在 'just_do/db/tasks/1.json' 的字串中取得 1.json,接著透過 File.basename 去掉 .json 的附檔名,就可以取得 id

接著再建立兩個 method,一個叫 [],另一個是 []=,用來對物件的 attribute 做 set 和 get

# mavericks/lib/mavericks/file_model.rb

def [](name)
  @hash[name.to_s]
end

def []=(name, value)
  @hash[name.to_s] = value
end

最後建立一個 class methodfind,透過 id 來尋找單筆資料,就如同 ActiveRecord 的方式一樣

def self.find(id)
  begin
   FileModel.new("db/tasks/#{id}.json")
  rescue
   return nil
  end
end

Model 初體驗

回到 just_do 的專案,來試試看 Mavericks 的 Model,前面提到,會用 JSON 格式檔案來模擬資料庫,就跟 Rails 一樣先建立一個 db/ 資料夾,但不一樣的是我們運用 資料夾 + JSON 格式檔案 當作「資料庫」來使用,在 db/ 底下再建立一個 tasks/ 資料夾,這個 tasks/ 可以看作是一個 table,接著在 tasks/ 裡面建立一個 1.json 可以看作是一筆資料,如下面所示

# just_do/db/tasks/1.json

{
  "title": "完成30天鐵人賽",
  "content": "每天寫一篇,連續30天"
}

然後在 Controller 的 show 裡面建立一個 Model 物件,用 #find 來尋找資料

# just_do/app/controllers/tasks_controller.rb

class TasksController < Mavericks::Controller
  def index

  end

  def show
    @task = Mavericks::Model::FileModel.find(1)
  end
end

增加 show.html.erb 頁面

# just_do/app/views/tasks/show.html.erb

<div class="row">
  <div class="col-6">
    <div class="card">
      <div class="card-header">
        任務細節
      </div>
      <div class="card-body">
        <div class="row">
          <label class="col" for="task_name">任務名稱</label>
          <div class="col">
             <%= @task['title']  %>
          </div>
        </div>
        <div class="row">
          <label class="col" for="task_name">任務內容</label>
          <div class="col">
            <%= @task['content']  %>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

重新整理一下畫面,如果取得檔案內容,代表我們成功了,恭喜完成第一個 Model!

關於 include

眼尖的讀者會發現,Mavericks::Model::FileModel.find(1) 這段寫起來落落長,應該有更好的方式來處理才對,這裡我們可以用 include 來解決這個問題,還記得 includeextend 的差別嗎?前者是繼承 module 的 method 做為 instance method,後者是繼承 module 做為 class method

# mavericks/lib/mavericks/controller.rb

require 'erubi'
# 記得要 require
require "mavericks/file_model"

module Mavericks
  class Controller
    # include method 進來
    include Mavericks::Model
.
.
(略)

controller.rb 裡面 require file_model,然後在 class Controller 裡面 include Mavericks::Model

所以在 jsut_do 那邊,就可以改寫成這樣

# just_do/app/controllers/tasks_controller.rb

class TasksController < Mavericks::Controller
  def index

  end

  def show
    @task = FileModel.find(1)
  end
end

重新整理看看結果有沒有改變?正常來講應該會一樣,這樣是不是減少了很多冗長的程式碼?

開發者說...

我知道開發者想說什麼,光只有 #find 是不夠的,今天只是 Model 初體驗,明天會在補上其他的 method,讓我們的 file_model 越來越強大也越來越完整


上一篇
[DAY 9] 復刻 Rails - 終於有基本雛形了!在 View 上面加點東西
下一篇
[DAY 11] 復刻 Rails - 更多的 Model 功能
系列文
向 Rails 致敬!30天寫一個網頁框架,再拿來做一個 Todo List30

尚未有邦友留言

立即登入留言