# 0329 MVC框架、資料庫、網站建置第一課 ###### tags: `Ruby / Rails` ## 前置作業 - 更新 Rails 到 6.1.3.1 - 更新到最新版本```$ gem install rails``` - 安裝指定版本 `$ gem install rails -v 6.1.3.1` - 建立一個新的 Rails 專案 - 在 Terminal 裡輸入:```$ rails new <專案名稱>``` - 確認可連接上 Server - 切換到剛剛建立的專案目錄位址 - 執行:`$ rails s` - 將程式碼推送至 GitHub - 在 GitHub 上創立新的 Repository (參考之前 Git 課程) - 按照步驟將專案推送到 GitHub 上 --- ## MVC 框架:Model / View / Controller 收到使用者 Request 後的第一步:傳送到 Route 尋找對應路徑 * 設定位置:config/routes.rb ![](https://i.imgur.com/8hOebZp.png) 用一張圖來說明 Rails 裡的 MVC 是怎麼運作的,[龍哥的文章](https://railsbook.tw/chapters/10-mvc.html): ![](https://i.imgur.com/vxht3ey.png) > 使用框架的目的:團隊溝通速度較快 --- 指定資料庫驅動程式(使用軟體) `$ rails new (專案名稱) -d mysql` 資料庫選擇:postgresql, mysql 次要選擇: mysql, postgresql, sqlite3, oracle, sqlserver, jdbcmysql, jdbcsqlite3, jdbcpostgresql, jdbc ## 資料庫 (database) 資料庫就是**儲存資料的地方**。資料以不重複的方式來儲存許多有用的資訊,讓使用者可以透過查詢、排序、計算等等方法,來更有效率的管理並轉換成有用的資訊。 資料庫之間的不同 oracle 與 sqlserver oracle 很貴、金融業仍在使用,完整 sqlserver 介面較簡單,需費用 ### 開源圈兩大資料庫: * MySQL * PostgreSQL 簡稱 pg / PG:不需費用,專案建議使用 * sqlite3 檔案型資料庫、負擔較小 *一般資料庫叫 SQL,職場上也可能會看到 NoSQL 介於 SQL 跟 NoSQL 中間的產品:Mongo DB 可看做一般excel用就好 ### ORM = Object–relational mapping 抽象的概念,把物件轉換成 SQL 語法 / [WIKI 英文說明](https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping) [WIKI 中文說明 (翻得不好外加資料很少)](https://zh.wikipedia.org/wiki/对象关系映射) mongoose類ORM,本質上是操作Mongo DB Rails 目前沒有支援 NoSQL 但未來如果有人寫出驅動程式的話就有可能支援(還是不建議使用,因為 Rails 就是設計給 SQL 使用的) ### 關聯式資料庫(Relational Database Systems) 普遍常見的RDBS有以下非常多種,各自都有優缺點,因應不同公司、產業類型及開發語言等因素,會採用不同的資料庫來使用。 #### 非開源資料庫 - Oracle DB(甲骨文出產,價格高昂,不少金融相關產業的公司會採用此款資料庫) - SQL Server(微軟出產,許多大公司會使用此款資料庫,有GUI方便使用及操作,每年的授權使用費用也不便宜) #### 開源資料庫 - MySQL(業界有許多PHP工程師會選擇此款資料庫為主,並搭配由PHP開發而成的phpMyAdmin來使用,可透過瀏覽器來作為操作資料庫的介面) - PostgreSQL(簡稱pg) #### 非關聯式資料庫(NoSQL) NoSQL 資料庫是為特定資料模型而建立,並且具有構建新型應用程式的彈性結構描述。NoSQL 資料庫在開發的容易性、功能性和大規模效能方面廣受肯定。一個一個文件一筆一筆資料各自存在彼此沒有關聯,好處速度快,但不好操作,以key -> value的方式組合,要自己想辦法用出關聯 【補充】NoSQL最知名常用的資料庫:MongoDB --- ### SQL 是什麼 SQL ( Structured Query Language,結構化查詢語言 ),為一種資料庫語言,在資料庫中擔任建立查詢、更新和刪除(CRUD)的範用型標準語言。 簡單來說,拿SQL語法去資料庫找東西。 詳細解釋:[WIKI](https://bit.ly/3szaXdX) ### SQL 92 標準 大部分都有支援,在 1992 年推出的標準,希望大家都能遵守標準,但是 92 共識就是沒有共識,大家還是自己寫自己的。 #### SQL包含了三個部分: * 資料定義語言(DDL : Data Definition Language) - 資料操縱語言(DML : Data Manipulation Language) * 資料控制語言(DCL : Data Control Language) - 資料定義語言(DDL : Data Definition Language)由 `CREATE`、`ALTER`與 `DROP` 三個語法所組成。 * CREATE 是負責資料庫物件的建立 * ALTER 是負責資料庫物件修改的指令,相較於 CREATE 需要定義完整的資料物件參數 * DROP 則是刪除資料庫物件的指令,並且只需要指定要刪除的資料庫物件名稱即可 #### 常見的幾種語句: ```SQL= CREATE DATABASE --建立新資料庫 ALTER DATABASE --修改資料庫 CREATE TABLE --建立新資料表 ALTER TABLE --變更(改變)資料表 DROP TABLE --刪除資料表 CREATE INDEX --建立索引(搜索鍵) DROP INDEX --刪除索引 ``` --- ### 資料操縱語言(DML : Data Manipulation Language) 資料操縱語言是用於資料庫操作,主要功能即是存取資料,主要有`INSERT`、`UPDATE`、`DELETE` 為核心,因此其語法都是以讀取與寫入資料庫為主。 而其中 `INSERT` 比較特別,其他的用法都需要搭配 `WHERE` 去篩選來存取資料,但他可以不篩選或界定範圍來做資料處理。 * `INSERT`: 將資料插入到資料庫物件中的指令 * `UPDATE`: 指令是依給定條件,將符合條件的資料表中的資料更新為新的數值 * `DELETE`: \自資料庫物件中刪除資料的指令 以上三種主要指令,再加上`SELECT` 查詢指令後,常會用 **CRUD** 的簡寫來代稱,這是由四個單字縮寫而成,分別為**建立**(Create)、**讀取**(Retrieve)、**修改**(Update)、**刪除**(Delete)。中文縮寫亦有人以**增刪改查**來代稱。 ### 資料控制語言(DCL : Data Control Language) 以控制使用者的存取權限為主,由 `GRANT` 和 `REVOKE` 兩個指令組成。 --- ![](https://i.imgur.com/XoQDuE4.png) ![](https://i.imgur.com/VHYkQLH.png) --- ## 資料表 (table) 由多筆資料紀錄(Record)所組合而成 --- ### Active Record 設計模式 - 是一種 ORM 產生的框架 - 是一種大師們遇到問題後精鍊出來的『結果』 - 解決問題的手法 - 遇到問題時該怎麼處理的方法 - 把資料庫的資料一筆一筆包成一個物件,並在物件上加上商業邏輯以操作物件,目的是讓資料存取更便利 ### Model=廣義的資料抽象概念,不是資料庫 - 透過Active Record模式設計的產物 ## 其他 * i18n * 如果一個應用程式在設計時,可以在不修改應用程式的情況下,根據不同的使用者直接採用不同的語言、數字格式、日期格式等,這樣的設計考量稱為國際化(internationalization),簡稱i18n(因為internationalization的 i~n 之間有18個字母)。 * k8s * Kubernetes(常簡稱為K8s)是用於自動部署、擴充和管理「容器化(containerized)應用程式」的開源系統。 該系統由Google設計並捐贈給Cloud Native Computing Foundation(今屬Linux基金會)來使用。 #### 語意化版號 (不成文共識) 6.1.0 6 = major:不同軟體(不同框架) 1 = minor:功能升級,但不保證向下相容 0 = patch:小地方修正,向下相容 [參考資料](https://semver.org/lang/zh-TW/) *Electron:Build cross-platform desktop apps with JavaScript, HTML, and CSS. 像是VScode, Messenger, Teams都是用 Electron做的,未來專案可以考慮做 https://www.electronjs.org ## 「 透過錯誤分析了解網站建立 」 ### iChef 實作 *新增一間新餐廳需要哪些資料? | 餐廳範例| 資料型別 | | -------- | ----------- | | 餐廳名稱 | 字串/String | | 地址 | 字串/String | | 電話 | 字串/String | | email | 字串/String | | 描述 | Text | * string:一般文字 有固定長度限制 * text binary:可以放到 4GB 大小(依資料庫不同而有不同大小限制) * Binary: 二進位 * TEXT:文字類型的資料型別,並非二進位。(資料出處為MySQL官網) --- ### [ 狀況 ] 連到網頁,出現沒有 Route 的錯誤訊息 ### * 設定 Route 如果沒有設定 Route 的話,嘗試連結會出現下方錯誤畫面 ![無Route錯誤畫面](https://i.imgur.com/qaQcBLi.png) 想功能前先想路徑 ```/restaurants``` Routes 列表網址:==名詞 + s== (範例如上面的餐廳s) - 建立新的 Route: ```ruby= Rails.application.routes.draw do get '/restaurants', to: 'a#b' # 使用 "GET" 方法連到 /restaurants 時 # 會連到 a controller 裡面的 b action # get ('/restaurants'), { to: 'a#b' } 的縮寫 # to: 'a#b' 是個hash,花括號可省略 end ``` --- ### [ 狀況 ] 建立完 Route,出現沒有 Controller 的錯誤訊息 ### *建立 Controller ![無Controller錯誤畫面](https://i.imgur.com/uRu5VlY.png) 所有 Controller 檔案位置:`app/controllers/` 檔名:books_controller (蛇式) => 方法會變成 BooksController (Rails慣例/雙駝) 檔案位置:`/app/controllers/application_controller/` ```ruby= class ApplicationController < ActionController::Base end ``` 在實際物件跟最原始層之間有一個距離,如果有共同需要使用的功能就直接加在這層即可,為了彈性 * 若重整後沒錯誤訊息且沒有更新的話,重新開一次伺服器 rails s --- ### [ 狀況 ] 建立完 Controller,出現沒有 action 的錯誤訊息 ![找不到 action 的錯誤訊息](https://i.imgur.com/Orn4s8F.png) ### *定義 action #### 讓頁面印出hello render 是 上層? Rails? 定義的方法 ```ruby= class AController < ApplicationController def b render html: 'hello' #render是rails內建的方法,繼承自Base end end ``` > https://api.rubyonrails.org/ > [render方法說明](https://rails.ruby.tw/layouts_and_rendering.html#%E4%BD%BF%E7%94%A8-render) 成功印出訊息 ![render 出訊息](https://i.imgur.com/HiRgEUJ.png) --- ### [ 狀況 ] 定義完 action 方法但是沒有實際方法或是建立 Views 底下檔案出現錯誤 ![有定義 b 方法但是沒有 render](https://i.imgur.com/FGz3kps.png) ### *建立 Views 內資料夾及檔案 換到view_file內,建a的資料夾(a controller)的b.html.erb檔案(b action) ![](https://i.imgur.com/LmC21M0.png =200x) 在b.html.erb內編輯 ![](https://i.imgur.com/0od52m9.png) --- #### 小技巧 // VScode 資料夾被群組收合無法正確建檔的調整方式: 1. 到 VScode 調整設定 ![VScode 調整設定](https://i.imgur.com/hMgSMXX.png =400x) 2. 在設定頁面解除資料夾群組 ![VScode 解除資料夾群組](https://i.imgur.com/RJKvSND.png =500x) --- ### *可以多個路徑指向同個地方 * 去思考是不是需要為了一個頁面再生一個 controller ![路徑](https://i.imgur.com/w8w5gRb.png) ```ruby= # 產生controller generate = g $ rails generate controller books $ rails g controller books # 刪除controller destroy = d $ rails destroy controller books $ rails d controller books # 不論產生or刪除,透過rails的指令會自動生成相對應名稱的controller及檔案 # 但不會幫你放在get上,要記得去加 ``` ### *Rails 會預設保護 POST 行為送出的數據,因此必須檢查驗證碼 ```html= <!-- 實務上並不這樣使用,龍哥只是為了解說 --> <form method="post" action="/abc"> <!-- 習慣上token會用hidden類型的隱藏欄位來傳送 --> <input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>"> ..... </form> ``` 如果真的要跳過驗證程序,可加下方的 code,但不建議這麼做 ```ruby= skip_before_action :verify_authenticity_token ``` 沒有加上驗證動作時,所出現的錯誤訊息 ![未驗證](https://i.imgur.com/g234SM4.png) 重新提交表單 => 重整用 POST 連接的頁面會發生的事情 #### 新增資料的頁面通常會直接 redirect_to 其他頁面 ```ruby= def list # 寫入表單 # redirect 轉址 -> ex.回列表頁 redirect_to '/restaurants' end ``` ### *設定頁面中連結的多種寫法 ```html= <!-- #html #固定路徑 --> <a href="/new_restaurant">新增餐廳</a> ``` ```erb= <!-- #混搭 --> <a href="<%= new_restaurant_path %>">新增餐廳</a> ``` ⬇︎ 龍哥建議用這個 ⬇︎ (他好像說之後都會這樣用🙃) ```ruby= #ruby <%= link_to '新增餐廳', new_restaurant_path %> ``` 補充 ```ruby= #ruby #如果連結為 "/user/create" 可以用蛇式寫法表達 <%= link_to '新增使用者', user_create_path %> ``` * 好處一 可以整理得很乾淨 * 好處二 因為是程式的關係,打錯字可以馬上知道 但效能比較差,因為會多一次的方法呼叫 *由於這邊用Ruby寫,在routes那邊也要用Ruby寫 ```ruby= Rails.application.routes.draw do get '/restaurants', to: 'restaurant#index' get '/new_restaurants', to: 'restaurant#new' ⥬ post :restaurant_list, to: 'restaurant#index' # post '/restaurant_list', to: 'restautant#index' (HTML寫法) end ``` --- ```ruby= #想要找某個方法的定義 method(:days).source_location ``` ![](https://i.imgur.com/4J7RZIr.png) 進到專案位址再 rails console,可簡寫 rails c ![](https://i.imgur.com/8D6i9h6.png) 確認要找的方法是可以執行的 ![](https://i.imgur.com/74OwBVD.png) 執行.source_location找到這個方法的定義 然後另開一個新的終端機 使用vim 進入獲得的路徑 ![](https://i.imgur.com/9PcGl8P.png) 使用J、K做上下移動,然後就會獲得這個方法是如何被定義 ![](https://i.imgur.com/S7goQxD.png) * 一定要進rails console才能找 * console,可以說是控制台的概念,進去console直接對程式/資料庫做指令 --- 比較一般A 、 link_to 還有 path 的優劣 ```ruby= # config/routes.rb Rails.application.routes.draw do post :restaurant_list, to: 'restaurant#new', path: '/ccc' end ``` --- * 提供思路走向圖(?) ![](https://i.imgur.com/UO95s3q.jpg) * 對照表,可透過打錯路徑獲得,也可用終端機輸入`$rails routes` ![](https://i.imgur.com/NjFULbt.jpg) * 如果action裡有render這個方法,就不會去找相對應的views,而是直接執行render ![](https://i.imgur.com/CkXPJxd.png)