Laravel 5.6 入門講義

1. 關於 Laravel

一、常用網站

  1. Laravel唸法:http://zh.forvo.com/word/laravel/#en
  2. Laravel官方網站:https://laravel.com/
  3. Laravel台灣官網:https://laravel.tw/
  4. Laravel工作坊:http://www.laravel-dojo.com/
  5. Laravel 5.6 中文手冊:https://laravel-china.org/docs/laravel/5.6
  6. Laravel 學院:http://laravelacademy.org/
  7. GitHub:https://github.com
  8. BootStrap 4 中文手冊:http://bootstrap.hexschool.com/

二、Laravel 5.6 環境需求

  • PHP >= 7.1.3
  • OpenSSL PHP 延伸套件
  • PDO PHP 延伸套件
  • Mbstring PHP 延伸套件
  • Tokenizer PHP 延伸套件
  • XML PHP 延伸套件
  • Ctype PHP 延伸套件
  • JSON PHP 延伸套件

Laravel 5.6 入門講義

1-1 為何需要框架?

一、為何需要框架?

  1. 框架本身是幫我們把一些重複的並且已經受過驗證的模式,抽象到一個已經幫你設計好的API封裝當中,幫助我們去應對這些複雜的問題。
  2. 如果是小型專案,或者開發者就一兩位,那實際上,使用一般PHP元件即可,保持靈活及彈性。
  3. 如果是多人或大型專案,則建議用框架,以利統一開發標準,及後續的維護。

二、使用框架缺點

  1. 需花時間精力學習該框架工具
  2. 若框架缺乏的功能需自行尋求整合及解決方式
  3. 框架有變成孤兒的風險(所以要慎選框架)

三、Laravel的哲學

  1. 提昇開發速度
  2. 提昇開發者的幸福
  3. 讓開發者已更輕鬆且快速的實現想法、擺脫沒必要的障礙,以產生偉大的產品。

四、Lavavel開發規範

若想知道業界都是怎樣在玩Laravel,建議參考一下這篇:https://laravel-china.org/docs/laravel-specification/5.5

 

Laravel 5.6 入門講義

1-2 快速安裝開發環境及開發工具

  1. 伺服器部份(詳見
    • 下載解壓XAMPP for windows 伺服器環境(第一次須執行 setup_xampp.bat 以進行環境設定,日後只要點擊xampp-control.exe並啟動apache及MySQL即可)
  2. 編輯器部份(詳見
  3. 其他相關工具(詳見

Laravel 5.6 入門講義

1-2-1 以XAMPP建立運作環境

一、下載XAMPP

  1. XAMPP優點是什麼作業系統都支援,而且絕對是最新的!另網站有中文界面。
  2. XAMPP官網:https://www.apachefriends.org/zh_tw/index.html
  3. 可以至https://sourceforge.net/projects/xampp/files/XAMPP%20Windows/下載最新安裝版

二、安裝XAMPP

  1. 下載後,請解壓縮到使用者目錄下(如:C:\Users\使用者名稱\xampp),當然其他目錄也行,不過底下一些設定若跟路徑有關的請自行變化。
  2. 執行 setup_xampp.bat 以進行環境設定
  3. 接著點擊xampp-control.exe,並分別按start啟動apache及MySQL
  4. 若有跳出防火牆訊息,一律選「是」
  5. 若80port被佔:
    1.檢查是否有啟動IIS,請至「控制台→系統管理工具→IIS管理員」停止之即可。
    2.檢查是否有啟動Skype或迅雷,若有請先關閉之。
    3. 開啟命令提示字元視窗,輸入以下指令:
    netstat -ano
    找出佔用Port 80的程式PID? 倘若發現PID = 4,請下達以下指令:
    net stop http
    Sc config start=disabled

    這樣子問題應該就解決了。

三、修改資料庫密碼

  1. 資料庫預設是沒密碼的,所以,得自己設一個,請先在瀏覽器輸入 http://localhost/phpmyadmin
  2. 編輯root@localhost,點擊修改密碼
  3. 設定好密碼送出即可
  4. 最後修改 \xampp\phpMyAdmin\config.inc.php,將19行改為http即可,如此,每次登入phpMyAdmin便會詢問帳號密碼。

四、設定環境變數(一些套件會用到)

  1. 若在開始選單有看到「搜尋程式及檔案」的搜尋框,可直接在裡面輸入「env」,然後點擊「編輯您的帳戶的環境變數」即可。
  2. 或者也可以點擊桌面「本機」右鍵,選擇「內容→進階系統設定」或是開啟Windows的「控制台→系統及安全性→系統→進階系統設定」,並切換到「進階」頁籤,點選「環境變數」。
  3. 點擊上方的「Path」並且按下「編輯」。若沒有「Path」,就改按「新增」來新增一個環境變數。 (其中%USERPROFILE%就是使用者家目錄,如:C:\Users\帳號)
  4. win10以下的版本,若原設定的最後面沒有分號,請自行加入;
    %USERPROFILE%\xampp\php
    //若是裝在c:\xampp請設定:
    c:\xampp\php

Laravel 5.6 入門講義

1-2-2 Visual Studio Code文字編輯器

一、安裝前準備

  1. 官網:https://code.visualstudio.com/
  2. 開發工具,請使用自己慣用的文字編輯器即可,上課用Visual Studio Code 為主
  3. 建議先安裝以下兩個軟體:
  4. 亦可安裝Visual Studio Code Portable(可攜版)
  5. 做好編輯器設定(php.exe的實際路徑請視實際情況修改)
    {
    	"git.ignoreMissingGitWarning": true,
    	"update.channel": "none",
    	// 控制字型大小 (以像素為單位)。
    	"editor.fontSize": 18,
    	//  控制是否自動換行。
    	"editor.wordWrap": "on",
    	// 控制編輯器是否應自動設定貼上的內容格式。格式器必須可供使用,而且格式器應該能夠設定檔案中一個範圍的格式。
    	"editor.formatOnPaste": true,
    	// 使用滑鼠滾輪並按住 Ctrl 時,縮放編輯器的字型
    	"editor.mouseWheelZoom": true,
    	// 指向 PHP 可執行檔。
    	"php.validate.executablePath": "C:/Users/使用者名稱/xampp/php/php.exe",
    
    	"css.remoteStyleSheets": [
    		"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
    	],
    		"css.fileExtensions": [
            	"css",
            	"scss",
            	"tpl"
    	],
    }

二、安裝套件:

  1. 這裡有全部安裝好的VSCodePortable1.24.0自解壓縮檔

  2. 不過癮的話,這裡還有更多他人推薦的套件:http://goodjack.blogspot.com/2018/03/visual-studio-code-extensions.html

三、常用快捷鍵

  1. Ctrl+N:建立新檔
  2. Ctrl+C複製
  3. Ctrl+V貼上
  4. Ctrl+S儲存
  5. Ctrl+F:搜尋
  6. Ctrl+Shift+F:跨檔搜尋
  7. Ctrl+H:取代
  8. Ctrl+/註解
  9. Ctrl+`開終端機
  10. Ctrl+B:關閉左側工具
  11. Ctrl+X:刪除目前行
  12. Ctrl+G:跳至某行
  13. Ctrl+end:跳至檔案結尾
  14. Ctrl+Z:回上個動作(復原)
  15. Ctrl+Y:回下個動作(再做)
  16. shift+alt+F:美化(格式化)語法
  17. shift+alt+滑鼠左鍵:區塊標記
  18. 完整快捷鍵整理:https://poychang.github.io/vscode/
  19. Emmet語法:https://abgne.tw/web/emmet-tutorial-series-basic.html

Laravel 5.6 入門講義

1-2-3 安裝 Composer

一、使用PHP元件

  1. 請站在巨人的肩膀上,別重複造輪子
  2. https://packagist.org 有大量元件可用!
  3. 這裡有Laravel Top 1000 的套件整理(看看大家都用什麼套件):https://laravel.csie.io/
  4. 欲使用 Packagist 的元件,請使用 Composer 來安裝。

二、安裝 Composer

  1. Composer 中文網頁:https://getcomposer.ycnets.com/
  2. https://getcomposer.org/download/ 下載 Composer(或直接按這裡下載Windows版
  3. 安裝過程中,不需要去勾選任何東西。
  4. 需先安裝好AMP環境,因為安裝時,要選擇php.exe的位置。(會自動吃環境變數的設定,所以,我們在裝XAMMPP時才需要去設定php的執行路徑)
  5. Composer需要PHP 5.3.2+才能運行。
  6. 裝好後的位置在 C:\Users\帳號\AppData\Roaming\Composer
  7. 請開啟終端機(開始→附屬應用程式→Windows PowerShell→Windows PowerShell),測試是否OK:
    composer diagnose
  8. 若是執行時出現「composer : 無法辨識 'composer' 詞彙是否為 Cmdlet、函數、指令檔或可執行程式的名稱。請檢查名稱拼字是否正確,如果包含路徑的話,請確認路徑是否正確,然後再試一次。」那影在環境變數中加入以下兩個路徑即可(其中%USERPROFILE%就是使用者家目錄,如:C:\Users\帳號):

    %USERPROFILE%\AppData\Local\ComposerSetup\bin
    %USERPROFILE%\AppData\Roaming\Composer\vendor\bin
  9. 如果有key設定失敗的問題,請執行以下指令,並連結到 https://composer.github.io/pubkeys.html 依據指示貼入金鑰設定值即可:

    composer self-update --update-keys

     

  10. 日後要更新 Composer(或太久沒用):
    composer self-update
  11. 如果更新時出現下面訊息,表示composer太舊,那就請移除composer,並安裝 > 1.73 版的composer

      [RuntimeException]
      SHA384 is not supported by your openssl extension, could not verify the phar file integrity
  12. 日後如果在composer.json有安裝新套件,可執行以下語法以安裝新套件
    composer update
  13. 如果安裝過程中有報錯,例如出現以下訊息:
    Failed to decode response: zlib_decode(): data error

    那麼可以試試執行清除快取的指令,有時候就可以解決了。

    composer clear-cache

三、用 Composer 安裝 Packagist 的元件

  1. 直接下指令即可,例如(全局安裝才需要global,會安裝至C:\Users\帳號\AppData\Roaming\Composer下,而非專案下):
    composer global require hirak/prestissimo
  2. 如此,Composer 便會自動判斷每個元件的最穩定版本編號,並下載該元件,以及更新相關元件。

  3. hirak/prestissimo 是一個可以加快composer安裝速度的套件,可以讓 composer 也能多進程並行下載。

  4. 若要查詢某套件的詳情或版本:

    composer show 套件名稱
  5. 若要解除已安裝套件

    composer remove 套件名稱
  6. 詳細指令請參考:https://getcomposer.ycnets.com/doc/03-cli.md

Laravel 5.6 入門講義

1-2-4 安裝 GitHub Desktop for Windows

一、關於Git 與 GitHub

  1. Git 是一種版本控管的系統 ,由 Linux 之父 "Linus Torvalds" 所主導開發
    1. 早期的版本控管的系統常見的有CVS及SVN,均為集中式控管。
    2. Git 則是分散式控管 ,每個人都有一份完整的儲存庫(Repository) ,有諸多優點,如: 支援本地操作、備份容易、功能強大且彈性的分支與合併等。
  2. GitHub 則是一個透過Git進行版本控制的軟體原始碼代管服務,免費用戶的原始碼一律公開,僅付費用戶可建立私有代碼倉庫。
  3. GitHub 平台有完整的 Git 版控支援、議題追蹤與管理、線上 Wiki 文件管理、友善的原始碼審核介面,容易上手。
  4. 需要更進一步了結可以觀看

二、註冊及安裝 GitHub Desktop for Windows

  1. 先註冊:https://github.com/join?source=header
    • 步驟一:填入帳號、Email、密碼
    • 步驟二:選擇免費方案
    • 步驟三:隨便填,跳過亦可
    • 步驟四:收信啟用即可
  2. 下載並安裝:https://desktop.github.com/
    • 輸入剛註冊的帳號及密碼登入之,暫時這樣就可以。

三、常見名詞

(以下節錄自30 天精通 Git 版本控管 (05):了解儲存庫、工作目錄、物件與索引之間的關係

  1. 工作目錄:一個資料夾,放程式碼或檔案的地方,例如 laravel的專案目錄
  2. 儲存庫(版本庫):用來紀錄版本的資料夾,一般在工作目錄下的 .git 資料夾中
  3. 物件: 用來保存版本庫中所有檔案與版本紀錄
  4. 索引: 用來保存當下要進版本庫之前的目錄狀態。
  5. 整體運作觀念:
    • 要使用 Git 版本控管,你必須先建立「工作目錄」與「版本庫」。(mkdir, git init)
    • 你要先在「工作目錄」進行開發,你可能會建立目錄、建立檔案、修改檔案、刪除檔案、... 等操作。
    • 然後當你想提交一個新版本到 Git 的「儲存庫」裡,一定要先更新「索引」狀態。(git add, git mv, ...)
    • 然後 Git 會依據「索引」當下的狀態,決定要把那些檔案提交到 Git 的「儲存庫」裡。(git status)
    • 最後提交變更時 (git commit),才會把版本資訊寫入到「物件儲存區」當中 (此時將會寫入 commit 物件)。

 

 

 

Laravel 5.6 入門講義

1-2-5 [建議]瀏覽器附加元件

  1. 上課示範用的瀏覽器為Firefox
  2. 建議使用的外掛如下(通常chrome也都有):

Laravel 5.6 入門講義

1-2-6 [參考]申請mailtrap帳號

  1. 這不是必要動作
  2. https://mailtrap.io/是可以用來做寄信測試的服務
  3. 連到https://mailtrap.io/後,只要用Google帳號或GitHub去進行 Sign in,立即可用,非常方便!
  4. 登入後,點擊信箱右邊設定圖示
  5. 其中設定資訊我們等一下會用到,暫時只要知道去哪裡找即可

Laravel 5.6 入門講義

1-2-7 [參考]用Homestead建構開發環境

  1. Homestead是Laravel的建議環境,有相當完善的配套,不過,安裝起來比較久,所以,參考一下,有空再玩。
  2. 首先,先安裝https://www.virtualbox.org/(因為Homestead基本上就是一個基於ubuntu的虛擬機)
  3. 然後,安裝https://www.vagrantup.com/downloads.html(vagrant用來下載建置好的Homestead虛擬機設定),裝好後須重新開機。
  4. 接著,在終端機中執行以下指令,以便下載建置好的Homestead虛擬機設定(此時會需要蠻長一段時間)
    cd ~ 
    vagrant box add laravel/homestead
  5. 接著安裝Homestead

    git clone https://github.com/laravel/homestead.git Homestead
  6. 執行Homestead初始化(用來產生Homestead.yaml等檔案)

    cd .\Homestead\
    .\init.bat
  7. 先檢查ssh的金鑰是否存在

    ls ~/.ssh
  8. 裡面若有id_rsaid_rsa.pub就OK,可以跳過此步驟。若出現「ls : 找不到 'C:\Users\使用者名稱\.ssh' 路徑,因為它不存在。」或者檔案不存在就請從開始選單打開「Git→Git Bash」,並執行以下指令產生金鑰(Email 請用GitGub的登入Email), 過程可以都選擇預設,一路 Enter 鍵即可 。

    ssh-keygen -t rsa -C "your_email@example.com"
  9. 看起來像這樣:

  10. 接著編輯設定檔Homestead.yaml(主要用來設定 Homestead 的站點和資料庫等訊息 )

    ---
    ip: "192.168.10.10"
    memory: 2048
    cpus: 1
    provider: virtualbox
    
    authorize: ~/.ssh/id_rsa.pub
    
    keys:
        - ~/.ssh/id_rsa
    
    folders:
        - map: ~/xampp/htdocs
          to: /home/vagrant/public_html
    
        - map: ~/xampp/phpMyAdmin
          to: /home/vagrant/public_html/phpmyadmin
    
    sites:
        - map: 專案目錄名稱.test
          to: /home/vagrant/public_html/專案目錄名稱/public
        - map: phpmyadmin.test
          to: /home/vagrant/public_html/phpmyadmin
    
    databases:
        - homestead
    
    # blackfire:
    #     - id: foo
    #       token: bar
    #       client-id: foo
    #       client-token: bar
    
    # ports:
    #     - send: 50000
    #       to: 5000
    #     - send: 7777
    #       to: 777
    #       protocol: udp
    
  11. 以上這樣的設定,就可以把local端的網頁目錄htdocs和Homestead的網頁目錄下public_html同步(實際上就是共享資料夾),但要注意的是,一定要多一層資料夾(如:public_html名稱可換),但不能只有/home/vagrant否則會無法登入。

  12. 另外,由於xampp裡有內建phpMyAdmin,而Homestead剛好沒有,所以,我們也設定一組對應,以便能夠操作虛擬機裡面的資料庫。

  13. 此外,僅程式是共用的,但資料庫是分別儲存的(除非設定到遠端的資料庫,例如:https://db4free.net/

  14. 編輯C:\Windows\System32\drivers\etc\hosts檔,在最後加入(輸入http://exam.test就是跑Homestead上的網站,輸入http://phpmyadmin.test則是管理Homestead上的資料庫,輸入http://exam.local則是跑xampp上的網站)

    192.168.10.10 exam56.test
    192.168.10.10 phpmyadmin.test
    127.0.0.1 exam56.local 
  15. 由於phpMyAdmin實際上是放在windows底下,所以其權限會是777,如此,當您在執行phpMyAdmin時就會出現「設定檔權限錯誤,檔案不應開啟所有寫入權!」的訊息且無法使用,因此,我們必須將該檢查關閉,才能順利使用phpMyAdmin。故開啟xampp\phpMyAdmin\libraries\config.default.php編輯之(約2998行):

    $cfg['CheckConfigurationPermissions'] = false;
  16. 最後就可以啟動Homestead囉!(須切換到Homestead目錄裡面)當設定檔有異動的時候,必須加個--provision,若沒異動,不加該參數也沒關係。(注意,須移除HyperV,且virtualbox也必須是最新版才能正常啟動)

    cd ~/Homestead
    vagrant up --provision
  17. 啟動過程中確保網路設定正確(有勾選「線路已連接」)

  18. 亦可登入操作(無須帳號密碼)

    vagrant ssh
  19. 登出請執行

    exit
  20. 關閉Homestead請執行

    vagrant halt

Laravel 5.6 入門講義

2. 建立專案

一、建立 Laravel 專案

  1. 請先切換到欲放置專案的目錄(如:C:\Users\使用者\xampp\htdocs等同~/xampp/htdocs)下,並將裡頭的東西清空。(之後凡提及「專案目錄」,請視實際情形,自行輸入正確目錄)
  2. 啟動VSCode,「檔案→開啟資料夾」,選擇欲放置專案的目錄,如C:\Users\使用者\xampp\htdocs
  3. 按下Ctrl+`,叫出終端機,在於並~/xampp/htdocs下貼上以下語法(別在Homestead中貼,會比較慢,因為Homestead中沒有安裝 hirak/prestissimo 套件),以直接建立專案(可指定版本,但要確定環境變數中的PHP是7.1.3以後的版本,若未指定版本,則會依據可抓到的PHP版本取用最新的laravel):
    composer create-project laravel/laravel exam56 "5.6.*" --prefer-dist 
  4. 如此,會在網頁目錄下建立出 exam56 資料夾,以及相關所需檔案。
  5. --prefer-dist 表示是從壓縮檔下載,可加快下載速度(不過其實還是大概需要五到十分鐘左右的安裝時間)。
  6. 或用composer 來建立 Laravel 專案亦可(若有做上述動作,以下就不用再做)
    composer require "laravel/installer"
    laravel new exam56
  7. 另外,進入專案目錄後,亦可用指令查看目前使用的laravel版本
    cd exam56
    php artisan --version

三、專案的檔案目錄結構

  1. 安裝好之後,可以開啟編輯器,如 VSCode,利用「專案→新增資料夾到專案」將exam56目錄加至左邊目錄,以方便存取。
  2. 詳細目錄說明可以參考:https://laravel-china.org/docs/laravel/5.6/structure/1354
目錄或檔名 用途說明
app 專案核心,專案程式都在裡面
-- Console 和專案相關的命令列檔案
-- Exceptions 例外狀況處理
-- Http 放置Http請求流程中所執行內容
----Controllers 控制器
----Middleware 中介層
-- Providers 放置應用程式的服務提供者,由config/app.php中的providers設定載入
-- User.php
bootstrap 框架啟動的程式碼
--cache 快取目錄,需777(寫入權限)
config 設定檔案的目錄
database 專案資料表
public 專案網站的根目錄,都是靜態檔案
resources 專案相關的資源檔案,包括 views、lang、assets等。
routes 路由目錄
storage 儲存設定目錄,需777(含底下目錄及檔案都要有寫入權限)
tests 單元測試目錄
vendor composer的套件目錄
.env 專案設定檔
.env.example 專案設定檔範本
.gitattributes git用檔案
.gitignore git忽略檔
artisan 主程式進入點,非常常用的指令
composer.json composer 檔案,紀錄所使用的php套件資訊及版本
composer.lock composer 鎖定檔,紀錄該專案下載的php套件資訊及版本
package.json 專案相關composer套件檔
phpunit.xml  
readme.md git用
server.php 啟動內建伺服器
webpack.mix.js webpack模組整合工具

    

Laravel 5.6 入門講義

2-1 將專案加入本地端的 git

  1. 先按「檔案→開啟資料夾」選取專案目錄exam56
  2. 然後按左邊第三個圖示,切換至版本控制視窗
  3. 由於尚未建立儲存庫,所以,點擊上方git小圖示後,直接點擊「初始化儲存庫」按鈕以建立之。此動作等同於終端機下指令:
    git init
  4. 接著輸入修改說明,然後按上方打勾圖示,以提交修改內容至git儲存庫中。此動作等同於終端機下指令:
    git add --all
    git commit -m "建立專案"
  5. 至此,已經建立好本地端的git儲存庫,以後有任何異動,都是不斷進行上面第4個步驟而已。

Laravel 5.6 入門講義

2-2 將專案放上GitHub

  1. 如果要將專案同步至遠端GIt(可和他人進行共同開發),那麼,最簡單的方式就是去申請個GitHub帳號,然後搭配GitHub Desktop軟體來操作。
  2. 啟動 GitHub Desktop,然後登入之。
  3. 選擇 Add a local reposiory 以便將本地端的儲存庫送至GitHub
  4. 選擇專案位置,並按Add repository
  5. 接著按下Publish repository以將本地儲存庫發佈到遠端的GitHub上
  6. 最後填寫一下儲存庫說明,並取消「Keep this code private」(否則要收費),最後送出即可。
     

 

到GitHub觀看此單元程式異動  

Laravel 5.6 入門講義

2-3 設定專案環境

一、從config/app.php設定專案

  1. 「檔案→開啟資料夾」,選擇您的專案目錄
  2. Laravel 專案所用的所有設定檔案都被存放在 config目錄下
  3. storage 目錄下的所有目錄和 bootstrap/cache 目錄需有寫入權限。
  4. 開啟/專案/config/app.php。如果VS Code已經在專案目錄下,可以直接在終端機用code命令,快速利用VS Code開啟檔案,例如:
    code config/app.php
  5. 設定 timezone locale
    'timezone'        => 'Asia/Taipei',
    'locale'          => 'zh-TW',
    'fallback_locale' => 'zh-TW',
    
  6. 若設定項目中有看到 env('xxx', 'ooo') 的,代表可以直接到 .env 檔設定即可,會以 .env 的設定值優先。後面的ooo一般並不會生效,除非中.env沒有xxx的設定項目,此時ooo才會有作用。
  7. 舉例來說'name' => env('APP_NAME', '線上測驗系統')並不會真的有作用,因為會去找.env 裡面的APP_NAME=Laravel設定,所以,屆時呈現的仍是Laravel而非線上測驗系統,除非去把.env 裡面的APP_NAME設定刪除,那才會顯示成線上測驗系統
  8. 如果要取得某個設定值,可以用這樣的方式:
    $timezone = config('app.timezone');
  9. 如果是要在程式運行中,修改某個設定值,可以這樣用:
    config(['app.name' => '我的第一個專案']);

二、用.env設定環境

  1. 同一套程式,可能會用到不同環境:開發環境、測試環境、正式營運環境,不同環境有不同設定,所以,我們可以根據每個伺服器環境修改 .env 設定(不支援中文),尤其是資料庫部份一定要修改,請開啟 /專案/.env
    APP_NAME=Exam
    
    //寄信設定(請填入mailtrap的設定資訊)
    MAIL_DRIVER=smtp
    MAIL_HOST=smtp.mailtrap.io
    MAIL_PORT=2525
    MAIL_USERNAME=5f51684abd9780
    MAIL_PASSWORD=9dc25ae9e2b955
    MAIL_ENCRYPTION=tls
  2. 其中可以用中文
    APP_NAME=線上測驗系統
  3. 若值裡頭有空白,需用引號包起來,例如:
    APP_NAME="Laravel Exam"
  4. 如果想要和Homestead共用,那資料庫帳號密碼就按照預設值來設定即可,因此,我們到phpMyAdmin來建立一個新的使用者,帳號為:homestead,密碼為secret,並建立一個同名資料庫即可。
  5. 自動建立的資料庫,最怕預設編碼錯誤,所以,我們點擊資料庫,並按「操作」修改之(或直接執行:http://localhost/phpmyadmin/db_operations.php?db=homestead),校對部份請選擇「utf8_unicode_ci」或「utf8_general_ci」即可。
  6. .env不會被送上 git,所以,環境可以使用自己的 .env 設定
  7. 若想查詢目前使用的是哪個環境,可以按Ctrl+`呼叫出終端機,並利用tinker,以互動的方式來執行一些Laravel程式,如:
    php artisan tinker
  8. 接著輸入以下語法,即可按出目前用的環境設定為何:
    App::environment();
  9. 看起來像這樣:

  10. App::environment()是印出目前的使用環境,若有傳參數進去,那就會判斷是否為該值,例如:

    if (App::environment('local')) {
        // 判斷環境是否為 local
    }
    
    if (App::environment(['local', 'staging'])) {
        // 判斷環境是否為 local 或 staging
    }
  11. .env 檔案中列出的所有變數將被加載到 PHP 的超級全局變數 $ _ENV
  12. 至此,可以在瀏覽器輸入http://localhost/exam56/public/連線到專案系統試試

三、更新至GitHub

  1. 按左邊第三個按鈕,輸入修改說明,按打勾送出,然後按三個...,選擇「同步處理」,並依照畫面提示依序輸入GitHub帳號及密碼即可同步至GitHub
  2. 若不想每次都輸入帳號密碼,可以按Ctrl+`,在終端機輸入以下指令,可以全局儲存帳號。
    git config --global credential.helper wincred

到GitHub觀看此單元程式異動   

Laravel 5.6 入門講義

2-4 Laravel運作週期

這篇可以不用看,看了會睡著...除非您真的很有興趣瞭解整個運作原理
  1. 所有的一切從/專案/public/index.php 開始,這裡是系統的入口點。
  2. index.php它主要是負責載入 Composer 生成的自動加載器,然後從 /專案/bootstrap/app.php 取得 Laravel 的服務容器物件。
  3. 根據請求類型,將請求送到/專案/app/Http/Kernel.php中的HTTP內核,HTTP內核就會傳回 HTTP 回應。
  4. HTTP內核做了很多處理請求前的準備工作,包括:
    • 設定錯誤處理
    • 設定日誌記錄
    • 檢測應用環境
    • 執行其他需要完成的任務
    • 定義了所有請求被應用程式處理之前必須經過的 HTTP 中間件的列表。中間件用來處理:
    • 加載服務提供器 (providers)。
      • 所有服務提供器都在 /專案/config/app.php 設定檔案的 providers 陣列中設定。
      • 所有提供器都會調用 register 方法,接著,由 boot 方法負責調用所有被註冊提供器。
      • 服務提供器負責引導所有框架的各種組件,如資料庫、隊列、驗證和路由組件。

  5. 接著Request 請求就會被轉交給路由器來進行調度。

  6. 路由器將請求發送到路由或控制器或任何運行於路由的特定中間件。

Laravel 5.6 入門講義

3. 建立使用者認證

  1. 在VS Code中,按下Ctrl+`,會開啟終端機。務必記得先進入到專案資料夾底下,因為 artisan指令只有在專案資夾中才能使用。
  2. 建立使用者認證機制
    php artisan make:auth
  3. Artisan 是 Laravel 內置的命令列介面。它提供了一些有用的命令協助您開發,它是由強大的 Symfony Console 組件所驅動。
  4. 由於資料表預設編碼改為 utf8mb4所以,若資料庫使用的版本是 MySQL 5.7.7 或MariaDB 10.2以下的版本沒做修改的話將無法順利執行建立資料表動作,會出現如下訊息:
  5. 原因在於文字欄位預設是 255 個字元,但用utf8mb4編碼的話,每個字元佔 4 位元組,也就是說,255個字元一共需要4*255=1020 位元組,但最大的索引長度才 767位元組,因此,最多只能塞得下191個字元,因此我們得修改文字欄位預設字元數,所以請先編輯 \專案\app\Providers\AppServiceProvider.php,先在上方加入:
    use Illuminate\Support\Facades\Schema;

    接著在boot()中加入以下語法,告知預設文字字元數為191個:

    public function boot()
    {
        Schema::defaultStringLength(191);
    }
  6. 接著建立資料表(由於扯到到資料庫,所以如果要在XAMPP中生效,須進入 ~/xampp/htdocs/專案/底下執行;若要在Homestead中生效,須vagrant ssh進入虛擬機後,到 ~/public_html/專案/下執行):
    php artisan migrate
  7. 順利的話,從phpMyAdmin或Adminer就可以看到建出三個新資料表:migrationspassword_resetsusers
  8. 連上網站試試 http://localhost/myshop/public
  9. 如果沒有裝伺服器的話,亦可直接啟動內建伺服器(關閉請按Ctrl+C
    php artisan serve
  10. 然後連上 http://localhost:8000/home 試試
  11. 連上後替自己建立一組帳號吧!

二、常用方法(底下暫時用不到,只是順帶一寫)

  1. 檢查是否已經登入?
    use Illuminate\Support\Facades\Auth;
    
    if (Auth::check()) {
        // 這個用戶已經登錄...
    }
  2. 取得使用者訊息
    $user = Auth::user();
  3. 指定條件來查找使用者:

    if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
        //某Email和密碼,且已啟用的用戶
    }
  4. 登入

    Auth::login($user);
    
    // 登錄並且「記住」用戶
    Auth::login($user, true);
  5. 登出

    Auth::logout();

到GitHub觀看此單元程式異動  

Laravel 5.6 入門講義

3-1 佈署到主機上

一、Apache的設定

  1. Apache 版本需要 2.4 以上
  2. 確認設定檔(如:\xampp\apache\conf\httpd.conf) 中的設定
    1. 確認底下module有啟動(前面無#註解符號):
      • mod_authz_host
      • mod_rewrite
    2. DocumentRoot 請設定至專案的 public 目錄,如:/安裝路徑/xampp/htdocs/exam56/public (若不打算變更的話,屆時只要在瀏覽器上輸入 http://localhost/exam56/public/ 亦可)
    3. 接著找到網頁目錄的部份,將:
      Options Indexes FollowSymLinks Includes ExecCGI

      改為

      Options Indexes FollowSymLinks MultiViews Includes ExecCGI
    4. 例如:
      DocumentRoot "/Users/tad/Dropbox/xampp/htdocs/exam56/public"
      <Directory "/Users/tad/Dropbox/xampp/htdocs/exam56/public">
          #
          # Possible values for the Options directive are "None", "All",
          # or any combination of:
          #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
          #
          # Note that "MultiViews" must be named *explicitly* --- "Options All"
          # doesn't give it to you.
          #
          # The Options directive is both complicated and important.  Please see
          # http://httpd.apache.org/docs/2.4/mod/core.html#options
          # for more information.
          #
          Options Indexes FollowSymLinks MultiViews Includes ExecCGI
      
          #
          # AllowOverride controls what directives may be placed in .htaccess files.
          # It can be "All", "None", or any combination of the keywords:
          #   AllowOverride FileInfo AuthConfig Limit
          #
          AllowOverride All
      
          #
          # Controls who can get stuff from this server.
          #
          Require all granted
      </Directory>
    5. MultiViews的作用是當訪問到目錄中不存在的檔案時( 如訪問http://localhost/test/target), 則apache會尋找該目錄下的所有target.*檔案。如果test目錄下存在target.jpg檔案, 則會把這個檔案返回給客戶, 而不是直接傳回404錯誤訊息。

二、PHP的設定

  1. 確定 php.ini 中的這幾個延伸件有啟用(基本上最新版的XAMPP都有):
    extension=openssl
    extension=fileinfo
    extension=pdo_mysql
    extension=mbstring

三、修改目錄權限

  1. 底下這幾個目錄需設為777(windows下不用)
    • bootstrap/cache
    • storage
  2. 改完後記得先stop Apache,然後再 start 以重新啟動之,此時,只要在瀏覽器上輸入 http://localhosthttp://exam56.local/ 就可以看見網站了。

四、重設app_key

  1. 若是有出現The only supported ciphers are AES-128-CBC and AES-256-CBC with the correct key lengths.的訊息,請執行以下指令重設app_key即可
    php artisan key:generate
    php artisan config:clear

     

Laravel 5.6 入門講義

3-2 新增語系

  1. 可以利用composer來直接安裝(記得切換到專案目錄下執行以下語法):
    composer require caouecs/laravel-lang:~3.0
  2. 亦可連至 https://github.com/caouecs/Laravel-lang/releases
  3. 下載 zh-TW/exam56/resources/lang/zh-TW
  4. 在登入驗證或表單驗證時會用到

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

3-3 開啟網站並登入

一、開啟網站並登入

  1. 如果有編輯C:\Windows\System32\drivers\etc\hosts檔的話,可以連上 http://exam56.local(XAMPP網站)、http://exam56.test(Homestead網站)
  2. 若沒有,直接連到 http://localhost/ 即可
  3. 從右上角註冊,分別註冊管理員、老師、學生三個帳號,以便測試
  4. 此認證機制是Laravel內建的機制,我們剛剛手動裝上的。

Laravel 5.6 入門講義

4. 了解路由的運作

當使用者在瀏覽器輸入網址,或者透過連結執行網站某個操作,這些「網址」會變成Laravel要執行什麼動作的依據,這樣的機制,就是Route(路由)。

一、路由(Route)

  1. 路由,就是讓Laravel根據使用者從瀏覽器輸入的網址來判斷接下來該做什麼事。
  2. 網頁的路由放在/專案/routes/web.php,請開啟之。
  3. 其中最下面兩行是我們執行上一單元加入內建的使用者認證機制時,自動產生的路由,暫時無須去理會它。
    Route::get('/', function () {
        return view('welcome');
    });
    
    Auth::routes();
    
    Route::get('/home', 'HomeController@index')->name('home');
    • 我們用 Route::get('網址', 動作); 來取得符合條件的網址,並告知系統要做啥事。
    • 路由都是由上往下,一旦符合條件就執行。
    • get是動詞(亦即HTTP的傳輸方式),其他動詞才還有postpatchdelete等。
    • 當接收到的網址是用get傳過來的,且在 / 目錄時,會傳回名字叫做welcome的視圖(View),該視圖會去讀取名為 welcome.blade.php 的樣板,簡寫為view('welcome')

二、視圖(View)

  1. 視圖,用來呈現畫面用,其實就是一個HTML網頁,只是裡面有包含一些樣板語法。
  2. View 一律放在「專案/resources/views/」底下,檔名一律為「xxx.blade.php」。
  3. welcome 的樣板位置在 /專案/resources/views/welcome.blade.php,高興的話,可以隨意改一下內容。

三、路由帶參數給視圖

  1. 如果路由需要帶參數給視圖,可以用->with(),如:
    Route::get('/', function () {
        return view('welcome')->with('name','tad')->with('say','嗨!');
    });
  2. 亦可用陣列:
    Route::get('/', function () {
        $data = ['name' => 'tad', 'say' => '嗨!'];
        return view('welcome', $data);
    });
    當然也可以簡寫為
    Route::get('/', function () {
        return view('welcome', ['name' => 'tad', 'say' => '嗨!']);
    });
  3. 也可以用 PHP 的 compact() 函數
    Route::get('/', function () {
        $name = 'tad';
        $say  = '嗨!';
        return view('welcome', compact('name', 'say'));
    });
  4. 此時,請修改 /專案/resources/views/welcome.blade.php 樣板,可以加入:
    <div class="title m-b-md">
        {{$name}} {{$say}} Laravel
    </div>
  5. 看起來像這樣

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

4-1 製作系統主樣板

一、製作主樣板

  1. 我們可以自己做個主要樣板來用,但從頭做麻煩,拿現成的來改簡單一點。
  2. /專案/resources/views/layouts/app.blade.php實際上是我們上個單元加入使用者認證下的產物,預設其實是沒有的。
  3. 但由於其結構挺不錯的,又支援BootStrap4,所以我們可以直接修改 /專案/resources/views/layouts/app.blade.php 並直接將 app.blade.php 作為系統的主樣板來使用。
    <!DOCTYPE html>
    <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
    
        <!-- CSRF Token -->
        <meta name="csrf-token" content="{{ csrf_token() }}">
    
        <title>{{ config('app.name', 'Laravel') }}</title>
    
        <!-- Scripts -->
        <script src="{{ asset('js/app.js') }}" defer></script>
    
        <!-- Fonts -->
        <link rel="dns-prefetch" href="https://fonts.gstatic.com">
        <link href="https://fonts.googleapis.com/css?family=Raleway:300,400,600" rel="stylesheet" type="text/css">
    
        <!-- Styles -->
        <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    </head>
    <body>
        <div id="app">
            @include('layouts.nav')
            <main class="py-4">
                @yield('content')
            </main>
        </div>
    </body>
    </html>
    
  4. 其中{{ }}裡面可以放PHP變數、函數...等內容,詳情可見:https://laravel-china.org/docs/laravel/5.6/blade/1375#6a96da
  5. 預設情況下,{{ }} 語句會自動調用 PHP的 htmlspecialchars 函數防止 XSS 攻擊。不想轉義的話,可以使用{!! !!}語法。
  6. 有時候,若是真的要在視圖上顯示{{ }},或者有其他前端套件也是使用{{ }}來進行渲染,那麼,只要寫成@{{ }}即可。
  7. 將原本25~68行左右的<nav>標籤全部移出成另一個檔案(/專案/resources/views/layouts/nav.blade.php),並於此處用@include('layouts.nav') 引入該檔案。

二、引入選單子樣板

  1. 我們將其中的選單部份獨立出來,另存為 layouts/nav.blade.php,並在主樣板中用@include() 引入子樣板即可,以保持樣板容易理解的狀態。
  2. 引入時,只要寫nav即可,不須寫完整的nav.blade.php,若該樣板放在views的子目錄下,如layouts/nav.blade.php,則寫成layouts.nav即可。
    <nav class="navbar navbar-expand-md navbar-light navbar-laravel">
        <div class="container">
            <a class="navbar-brand" href="{{ url('/') }}">
                {{ config('app.name', 'Laravel') }}
            </a>
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
                aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                <span class="navbar-toggler-icon"></span>
            </button>
    
            <div class="collapse navbar-collapse" id="navbarSupportedContent">
                <!-- Left Side Of Navbar -->
                <ul class="navbar-nav mr-auto">
    
                </ul>
    
                <!-- Right Side Of Navbar -->
                <ul class="navbar-nav ml-auto">
                    <!-- Authentication Links -->
                    @guest
                    <li class="nav-item">
                        <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
                    </li>
                    @else
                    <li class="nav-item dropdown">
                        <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true"
                            aria-expanded="false" v-pre>
                            {{ Auth::user()->name }} <span class="caret"></span>
                        </a>
    
                        <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                            <a class="dropdown-item" href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">
                                {{ __('Logout') }}
                            </a>
    
                            <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                                @csrf
                            </form>
                        </div>
                    </li>
                    @endguest
                </ul>
            </div>
        </div>
    </nav>
  3. 到這裡為止,雖然已經做好主樣板,但畫面還沒有太大的改變,也不太有感覺,稍後單元,我們就來改變入口處的畫面。

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

4-2 製作中文語系檔

  1. 在樣板檔中,會看到如{{ __('Login') }}這樣的用法,這表示此字串是可以翻譯成其他語系的。
  2. 先建立/專案/resources/lang/zh-TW.json語系檔
  3. 在VSCode中按Ctrl+Shift+F即可跨檔案搜尋,可輸入__('來搜尋所有有語系的部份。
  4. 將字串翻譯寫入zh-TW.json語系檔中(JSON格式):
    {
        "Login": "登入",
        "Password": "密碼",
        "Remember Me": "記住我",
        "E-Mail Address": "電子信箱",
        "Forgot Your Password?": "忘記密碼?",
        "Reset Password": "重設密碼",
        "Send Password Reset Link": "寄送重設密碼連結",
        "Confirm Password": "確認密碼",
        "Register": "註冊",
        "Name": "姓名",
        "Logout": "登出"
    }
  5. 如此,http://localhost/login 界面就會變成中文的囉!
  6. 詳情請看:https://laravel-china.org/docs/laravel/5.6/localization/1376#retrieving-translation-strings

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

4-3 修改首頁畫面

一、編輯預設首頁

  1. 要決定改呈現什麼畫面,目前的決定權在路由設定中,其中,當連到首頁(/)就傳回view('welcome'),也就是去呼叫 /專案/resources/views/welcome.blade.php 這個檔的意思。
    Route::get('/', function () {
        return view('welcome');
    });
  2. 所以,請開啟/專案/resources/views/welcome.blade.php以修改之:
    @extends('layouts.app')
    @section('content')
        <div class="container">
            <h1>隨機題庫系統</h1>
        </div>
    @endsection
    
  3. 此檔主要設定content樣板變數,到時後會套入主樣板 layouts/app.blade.php 中。

    • @extends('layouts.app') ,就是說,目前這個樣板是源自於(或是繼承自)layouts/app.blade.php 視圖。

    • @section('樣板變數名稱','值') 用來定義一個樣板變數,及其對應值

    • 若是只有@section('樣板變數名稱'),那麼下方可直接填入內容,然後用 @endsection 來做個結束亦可。

    • @yield('xxx') 會顯示定義的樣板變數xxx的內容

  4. 要手動排版,可按Alt+Shift+F
  5. 最後看起來像這樣

二、微調版面

  1. 視圖中,有個<div class="container"></div>把住要內容包起來,這是BootStrap的用法,讓內容的呈現範圍大約維持在1170左右。由於幾乎每一個視圖都會用到,所以,我們可以之移至主樣板,如此,就無須在每個blade.php中加入該語法。
  2. 換言之主樣板/專案/resources/views/layouts/app.blade.php變這樣:
    <!DOCTYPE html>
    <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
    
        <!-- CSRF Token -->
        <meta name="csrf-token" content="{{ csrf_token() }}">
    
        <title>{{ config('app.name', 'Laravel') }}</title>
    
        <!-- Scripts -->
        <script src="{{ asset('js/app.js') }}" defer></script>
    
        <!-- Fonts -->
        <link rel="dns-prefetch" href="https://fonts.gstatic.com">
        <link href="https://fonts.googleapis.com/css?family=Raleway:300,400,600" rel="stylesheet" type="text/css">
    
        <!-- Styles -->
        <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    </head>
    
    <body>
        <div id="app">
        @include('layouts.nav')
            <main class="py-4">
                <div class="container">
                    @yield('content')
                </div>
            </main>
        </div>
    </body>
    
    </html>
  3. 首頁視圖/專案/resources/views/welcome.blade.php變得更簡單了:

    @extends('layouts.app') 
    @section('content')
        <h1>隨機題庫系統</h1>
    @endsection

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

4-4 Blade 樣板語法

詳細 Blade 樣板語法請參考:https://laravel-china.org/docs/laravel/5.6/blade/1375

一、Blade 樣板的輸出

Blade語法 說明或用法 控制器對應
{{-- 註解 --}} 就是註解  
{!!$content!!}} 輸出$content內容(不會過濾) return view('welcome', ['content' => $content]);
{{$content}} 經過htmlentities過濾的內容(<會變成&lt; return view('welcome', ['content' => $content]);

二、Blade 樣板的邏輯控制

Blade語法 說明或用法

@if(條件)

@elseif(條件)

@else

@endif

if 判斷

@unless(條件)

@endunless

當條件為false時才成立

@for(起始值;條件;變化量)

@endfor

for迴圈

@while(條件)

@endwhile

while迴圈

@foreach(陣列 as 鍵值)

@endforeach

foreach迴圈,可用 $loop->first以及$loop->last變數

@forelse(陣列 as 鍵值)

@empty

@endforelse

效果同foreach迴圈,只是會檢查陣列是否為空,若為空會執行@empty以下的內容

三、Blade 樣板佈局控制語法

@extends('樣板名稱')

將指定的樣板中的區塊及內容繼承過來

@include('樣板名稱')

引入樣板

@yield('名稱', '預設值')

定義一個區塊,預設值非必須。若是沒預設值,則使用以下方式來定義其值

@section('名稱', '值')

使用名稱來定義一個區塊內容,亦可顯示或繼承內容

@section('名稱') + @endsection 或 @stop

標記區塊的結束,通常作為@section的結束標籤,例如:這樣只是定義content的內容,並不會直接呈現,需利用@yield('content')才能顯示。

@section('content')
    <h1>隨機題庫系統</h1>
@endsection

@section('名稱') + @show

  1. 標記區塊的結束並顯示,例如:這樣就會直接顯示。
  2. 修改專案\resources\views\layouts\nav.blade.php,在@else下方多一個 @section('my_menu'),也就是自定義一個樣板變數my_menu並直接顯示。
@guest
    <li class="nav-item">
        <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
    </li>
    <li class="nav-item">
        <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
    </li>
@else 
    @section('my_menu')
        <li><a class="nav-link" href="/home">{{ __('Home') }}</a></li>
    @show
    <li class="nav-item dropdown">
        <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true"
            aria-expanded="false" v-pre>
            {{ Auth::user()->name }} <span class="caret"></span>
        </a>

        <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
            <a class="dropdown-item" href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">
                {{ __('Logout') }}
            </a>

            <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                @csrf
            </form>
        </div>
    </li>
@endguest

 @section('名稱') + @parent

  1. 會將此定義的內容附加進去
  2. 修改專案\resources\views\welcome.blade.php,在最後加上:
    @section('my_menu')
        <li><a class="nav-link" href="/home">我的選項</a></li>
        @parent
    @stop
  3. 如此,只要到首頁,就會多出一個「我的選項」的連結
  4. 若將@parent刪除,則會以「我的選項」取代原有選項

到GitHub觀看此單元程式異動  

Laravel 5.6 入門講義

5. 安裝後台及讀寫機制

  1. 由於測驗系統必須有較多權限設定,例如:老師開設測驗,學生進行測驗,但現有使用者機制無法分辨身份,因此,必須加個角色、權限的機制才行
  2. 這部份,有許多套件可以作到,我們直接採用有完整後台功能,且支援Laravel 5.6的 Backpack for Laravel 來達成即可。
  3. 官網:https://github.com/Laravel-Backpack
  4. 可直接下載 backpack/crud 套件來安裝即可(會自動裝上backpack base)
    composer require backpack/crud
  5. 執行安裝 backpack:base,會自動綁訂backpack:base套件,這裡會需要一點時間,因為檔案蠻多的。

    php artisan backpack:base:install
  6. 執行安裝 backpack crud 套件,用來建立CRUD機制。安裝過程中會問您是否要建立elfinder檔案管理套件,請直接按Enter(即yes)進行安裝。

    php artisan backpack:crud:install
  7. 修改 /專案/vendor/backpack/base/src/resources/lang/zh-hant/專案/vendor/backpack/crud/src/resources/lang/zh-hant,將 zh-hant 改為 zh-TW 如此,然後把原本放在套件中的中文語系複製到專案中,才能看到完整中文語系,未來更新時語系也才不會消失。

    mv resources/lang/vendor/backpack/zh-hant resources/lang/vendor/backpack/zh-TW
    cp vendor/backpack/base/src/resources/lang/zh-hant/base.php resources/lang/vendor/backpack/zh-TW/base.php

     

  8. 最後,只要輸入http://localhost/admin/dashboard就可以連到後台囉!

 

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

5-1 安裝後台的使用者角色管理

一、安裝backpack/permissionmanage

  1. 前面的基礎都是為了建立使用者的角色管理機制
  2. 官網:https://github.com/Laravel-Backpack/PermissionManager
  3. 安裝backpack/permissionmanager:
    composer require backpack/permissionmanager
  4. 建立設定檔:

    php artisan vendor:publish --provider="Backpack\PermissionManager\PermissionManagerServiceProvider"
  5. 建立資料表:

    php artisan migrate
  6. 請修改/專案/app/User.php加上以下程式,如此,權限機制才能正常使用:

    <?php
    
    namespace App;
    
    use Backpack\CRUD\CrudTrait;
    use Illuminate\Foundation\Auth\User as Authenticatable;
    use Illuminate\Notifications\Notifiable;
    use Spatie\Permission\Traits\HasRoles;
    
    class User extends Authenticatable
    {
        use Notifiable;
        use CrudTrait;
        use HasRoles;
        /**
         * The attributes that are mass assignable.
         *
         * @var array
         */
  7. 接著修改/專案/config/auth.php,把原本的使用者資料模型換成此套件的使用者資料模型

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model'  => Backpack\Base\app\Models\BackpackUser::class,
        ],
    
        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],
  8. 修改 /專案/resources/views/vendor/backpack/base/inc/sidebar_content.blade.php 加入權限管理選項

    <!-- Users, Roles Permissions -->
    <li class="treeview">
      <a href="#"><i class="fa fa-group"></i> <span>使用者角色權限管理</span> <i class="fa fa-angle-left pull-right"></i></a>
      <ul class="treeview-menu">
        <li><a href="{{ url(config('backpack.base.route_prefix', 'admin') . '/user') }}"><i class="fa fa-user"></i> <span>使用者</span></a></li>
        <li><a href="{{ url(config('backpack.base.route_prefix', 'admin') . '/role') }}"><i class="fa fa-group"></i> <span>角色</span></a></li>
        <li><a href="{{ url(config('backpack.base.route_prefix', 'admin') . '/permission') }}"><i class="fa fa-key"></i> <span>權限</span></a></li>
      </ul>
    </li>
  9. 最後直接下載中文語系解壓縮到\專案\resources\lang\vendor\backpack中,然後解壓縮覆蓋!這樣日後若升級套件,加入的中文語系才不會又消失。

二、建立身份及權限

  1. 「角色」部份可以先新增「管理員」、「教師」及「學生」三個角色
  2. 「權限」部份可以先新增「後台管理」、「建立測驗」及「進行測驗」
    • 「管理員」分別綁定「後台管理」及「建立測驗」兩個權限
    • 「教師」綁定「建立測驗」權限
    • 「學生」綁定「進行測驗」權限
  3. 記得先將自己的帳號加入「管理員」(密碼有要改變才需要填,否則留空即可),另外記得到「使用者」建立一個老師及學生帳號以便測試。

三、修改選單

  1. 為了方便連結,我們可以修改\專案\resources\views\layouts\nav.blade.php的樣板檔,加入自製選項,由於只開放管理員進到後台,所以,利用@role('管理員')@endrole來進行身份判斷。:

    ...略...
    @else
        @section('my_menu')
            @role('管理員')
            <li><a class="nav-link" href="/admin">{{ __('Admin') }}</a></li>
            @endrole
        @show
    ...略...
  2. 然後,編輯一下\專案\resources\lang\zh-TW.json語系檔

    {
        "Login": "登入",
        //....略...
        "Admin":"後台管理"
    }
  3. 如此,就有方便的連結可以按了。

  4. 詳情可見:https://github.com/laravel-backpack/permissionmanager#using-blade-directives

到GitHub觀看此單元程式異動  

Laravel 5.6 入門講義

5-2 讓閒雜人等不能進入後台

  1. 目前的後台是只要知道路徑者都可以進去管理。
  2. 我們可以根據權限來控管,並在路由中利用中間件來執行權限判斷,因此,我們利用artisan來產生裝middleware檔案:
    php artisan make:middleware AdminMiddleware
  3. 編輯\專案\app\Http\Middleware\AdminMiddleware.php 內容為:
    <?php
    
    namespace App\Http\Middleware;
    
    use Closure;
    
    class AdminMiddleware
    {
        /**
         * Handle an incoming request.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  \Closure  $next
         * @return mixed
         */
        public function handle($request, Closure $next)
        {
            if (!$request->user()->hasAnyPermission('後台管理')) {
                abort(403);
            }
    
            return $next($request);
        }
    }
    
  4.  修改\專案\config\backpack\base.php的路由設定:
    'middleware_class'           => [
        \Backpack\Base\app\Http\Middleware\CheckIfAdmin::class,
        \App\Http\Middleware\AdminMiddleware::class,
    ],
  5. 該檔案上方也有一些界面上的設定,高興的話,也可以設定一下,然後就可以試試 看囉!
  6. 如果覺得預設的403畫面看不懂,那麼,也可以自行修改一下。開啟 \專案\resources\views\errors\403.blade.php,改成:
    @extends('layouts.app')
    @section('content')
        <h1>403 Forbidden.</h1>
        <p>您沒有權限可以執行目前的動作喔!</p>
    @endsection
    

    到GitHub觀看此單元程式異動    

Laravel 5.6 入門講義

5-3 安裝 laravel-debugbar 方便除錯

一、利用dd()除錯

  1. 如果想要做類似die(var_dump())這種斷點除錯,可以使用dd()這個函數
    $user = Auth::user();
    dd($user);
  2. 若想要找出某物件所有可用的方法,可以這樣用:
    $user = Auth::user();
    dd(get_class_methods($user));

二、利用laravel-debugbar套件除錯

  1. 官網:https://github.com/barryvdh/laravel-debugbar
  2. 利用composer安裝,由於只有在開發時才需要,所以加入--dev參數:
    composer require barryvdh/laravel-debugbar --dev
  3. 底下動作不做其實沒關係,因為是比較進階的用法。
  4. 為了方便我們更容易使用Laravel debugar顯示變數,建議將Laravel Debugbar加入facade,故修改\專案\config\app.php
    'aliases'         => [
        //...略...
        'View'         => Illuminate\Support\Facades\View::class,
        'Debugbar'     => Barryvdh\Debugbar\Facade::class,
    ],
  5. 最後執行以下語法以複製相關檔案
    php artisan vendor:publish --provider="Barryvdh\Debugbar\ServiceProvider"

到GitHub觀看此單元程式異動    

Laravel 5.6 入門講義

6. 建立相關資料表及模型

一、建立 Eloquent 模型

  1. 一個Eloquent 模型對應一張表,model (模型)就是用來操作資料庫資料用的。

  2. migration 可視為建立SQL資料表的方法(有點類似xxx.sql的作用),可分次建立,亦可回覆上一動。

  3. 先建立 Eloquent 模型,以便將一個資料表變成一個物件來操作,並且順便產生 migration 檔案
    php artisan make:model Exam --migration
  4. 模型的第一個字請用大寫(大駝峰命名法),單複數不拘,但似乎使用單數的人居多。

  5. 若 migration 檔案已經存在了,則用下面任一語法均可

    php artisan make:model Exam 
    php artisan make:model Exam --no-migration
    
  6. 最後會產生 /專案/app/Exam.php 模型
    <?php
    
    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Exam extends Model
    {
        //
    }
    
  7. 還有 /專案/database/migrations/日期_create_exams_table.php (如:2018_07_01_161014_create_exams_table.php)

    <?php
    
    use Illuminate\Support\Facades\Schema;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Database\Migrations\Migration;
    
    class CreateExamsTable extends Migration
    {
        /**
         * Run the migrations.
         *
         * @return void
         */
        public function up()
        {
            Schema::create('exams', function (Blueprint $table) {
                $table->increments('id');
                $table->timestamps();
            });
        }
    
        /**
         * Reverse the migrations.
         *
         * @return void
         */
        public function down()
        {
            Schema::dropIfExists('exams');
        }
    }
    

二、編輯 Eloquent 模型

  1. 如果是自動建立的 migration,其實下面動作不需要做
  2. Eloquent 會假設類別的小寫、底線、複數形式就是資料表的名稱 ,例如類別叫做Exam,其內定資料表名稱辨識exams,所以資料表名稱若不符合此內規,則可自行定義一個 $table 屬性來告知正確的資料表名稱。
  3. Eloquent 也會假設每個資料表有一個主鍵欄位叫做 id。所以,若您的主鍵不是叫做id,那麼你也可以定義一個 $primaryKey 屬性來覆寫這個慣例。(/專案/app/Exam.php
    <?php
    
    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Exam extends Model
    {
    //若符合慣例可以不用設定這些
        protected $table      = 'exams';
        protected $primaryKey = 'id';
    }
    

三、Model 慣例

  1. model 預設在 /專案/app 底下,若要放在/專案/app下的子目錄,記得也要修改 namespace
  2. 一個 Model 對應一個資料表
  3. 資料表名稱為英文 複數 全小寫,單字間用蛇底式命名法( snake case ),例如: first_name 、last_name、snake_case
  4. Model 名稱為單數,單字間用大駝峰命名法 (upper camel case) ,例如:FirstName、LastName、CamelCase

四、關於Eloquent 用法

  1. 方法詳見:https://laravel-china.org/docs/laravel/5.6/eloquent/1403

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

6-1 編輯並執行測驗(exam)的 migrate 檔案

建立一個隨機題庫,並能進行測驗,會用到許多表。為了減低複雜程度,我們暫時先做一下限制:

  1. 教師可以建立許多測驗
  2. 每一個測驗,可以有N個題目,從中隨機抽出M筆題目來考試
  3. 僅學生可以進行測驗

一、編輯測驗 migrate 檔案

  1. migrate 檔案就是用來定義資料表欄位的檔案,屆時可用指令自動建立(或增減)資料表欄位

    • 先確定 /專案/.env 中的資料庫設定有設定正確

    • 確定有啟動MySQL資料庫,並確定已經建立 DB_DATABASE 定義的資料庫

  2. 確認有無 /專案/database/migrations/日期_create_exams_table.php(上一節有做了),若是還沒有上述檔案(或者是只要建立資料表,但不需要建立Model的時候),執行以下語法會自動生出 migrate 檔案(其中,是create_exams_table檔案名稱,--create=exams則是資料表名稱,慣例為複數):

    php artisan make:migration create_exams_table --create=exams
  3. 編輯 /專案/database/migrations/日期_create_exams_table.php

    <?php
    
    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Support\Facades\Schema;
    
    class CreateExamsTable extends Migration
    {
        /**
         * Run the migrations.
         *
         * @return void
         */
        public function up()
        {
            Schema::create('exams', function (Blueprint $table) {
                $table->increments('id');
                $table->string('title');
                $table->integer('user_id')->unsigned();
                $table->foreign('user_id')->references('id')->on('users');
                $table->boolean('enable');
                $table->timestamps();
            });
        }
    
        /**
         * Reverse the migrations.
         *
         * @return void
         */
        public function down()
        {
            Schema::dropIfExists('exams');
        }
    }
    
  4. title:測驗的名稱
  5. user_id:建立測驗的使用者編號
  6. enable:是否啟用測驗, boolean() 在 MySQL 中其實是 tinyint(1) 類型,未來我們可能需要做一下型別轉換。
  7. 若是要建立 foreign key 必須在還沒有資料的時候就先建立,日後若已經有資料,要再建立 foreign key 就會比較麻煩。(因為必須資料能對應的起來才可以建立)

    $table->foreign('user_id')->references('id')->on('users');
  8. 建立各種欄位類型可參考:https://laravel-china.org/docs/laravel/5.6/migrations/1400#creating-columns

  9. 替欄位加入各種屬性請參考:https://laravel-china.org/docs/laravel/5.6/migrations/1400#column-modifiers

  10. 要修改欄位方法請參考:https://laravel-china.org/docs/laravel/5.6/migrations/1400#55682c

  11. 各種索引的建立請參考:https://laravel-china.org/docs/laravel/5.6/migrations/1400#b271e4

  12. 最後執行資料庫遷移即可建出新的資料表

    php artisan migrate
  13. 要看資料表是否順利建出可以連到http://localhost/phpmyadmin/db_structure.php?server=1&db=homestead
  14. 若想撤銷剛剛的動作,可執行:
    php artisan migrate:rollback
  15. 若想刪除全部資料表重來,可以執行

    php artisan migrate:reset

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

7. 開始定義系統各項功能

一、定義路由

  1. 這個單元先看看就好,不用實作,只是先列出總理,方便參考用。
  2. 定義各項功能基本上就是定義路由,白話一點就是說,網址輸入什麼,就去執行什麼動作,出現什麼畫面
  3. 建議先看一下「簡明RESTful API設計要點
  4. 底下的「資源」通常代表一個資料表的意思(當然不盡然如此,只是這樣想比較簡單),例如:exam、user...等。以下寫法範例,「資源」以「exam」為例:
    動詞 網址路徑 行為 路由名稱 一般路由寫法 有控制器的路由寫法
    GET /資源

    index

    列表

    資源.index

    Route::get('/exam', function () {
        return view('index');
    })->name('exam.index');

     

    Route::get('/exam', 'ExamController@index')
        ->name('exam.index');
    GET /資源/create

    create

    新增

    資源.create

    Route::get('/exam/create', function () {
        return view('create');
    })->name('exam.create');

     

    Route::get('/exam/create', 'ExamController@create')
        ->name('exam.create');

    POST

    (支援 CSRF保護)

    /資源

    store

    儲存

    資源.store Route::post('/exam', function () {
        return view('store');
    })->name('exam.store');
    Route::post('/exam/store', 'ExamController@store')
        ->name('exam.store');
    GET /資源/{id}

    show

    顯示一筆

    資源.show Route::get('/exam/{id}', function () {
        return view('show');
    })->name('exam.show');
    Route::get('/exam/{id}', 'ExamController@show')
        ->name('exam.show');
    GET /資源/{id}/edit

    edit

    編輯

    資源.edit Route::get('/exam/{id}/edit', function () {
        return view('edit');
    })->name('exam.edit');
    Route::get('/exam/{id}/edit', 'ExamController@edit')
        ->name('exam.edit');

    PUT/PATCH

    (支援 CSRF保護)

    /資源/{id}

    update

    更新

    資源.update Route::patch('/exam/{id}', function () {
        return view('update');
    })->name('exam.update');
    Route::patch('/exam/{id}', 'ExamController@update')
        ->name('exam.update');

    DELETE

    (支援 CSRF保護)

    /資源/{id}

    destroy

    刪除

    資源.destroy Route::delete('/exam/{id}', function () {
        return view('destroy');
    })->name('exam.destroy');
    Route::delete('/exam/{id}', 'ExamController@destroy')
        ->name('exam.destroy');
  5. 搭配「動詞」+「路徑」,可以執行不同「行為」
  6. 其中,盡量用符合的動詞和路徑,「行為」的命名也盡量和上面相同,如此,未來可以簡化許多程式。
  7. CSRF是跨站請求偽造英語:Cross-site request forgery

Laravel 5.6 入門講義

7-1 還沒有控制器的路由寫法

  1. 假設有個資料表叫做exams,那麼,路由可以這樣設定,左邊是路由,右邊是動作,底下的動作內容都是直接呼叫對應視圖(暫時不要跟著做,因為我們會直接用控制器來做更好):
    Route::get('/exam', function () {
        return view('index');
    })->name('exam.index');
    
    Route::get('/exam/create', function () {
        return view('create');
    })->name('exam.create');
    
    Route::post('/exam', function () {
        return view('store');
    })->name('exam.store');
    
    Route::get('/exam/{id}', function () {
        return view('show');
    })->name('exam.show');
    
    Route::get('/exam/{id}/edit', function () {
        return view('edit');
    })->name('exam.edit');
    
    Route::patch('/exam/{id}', function () {
        return view('update');
    })->name('exam.update');
    
    Route::delete('/exam/{id}', function () {
        return view('destroy');
    })->name('exam.destroy');

、相關說明:

  1. 動作一般就是一個closesure(閉包或匿名函數,亦即沒有命名的函數),如:
    function () {
      return view('視圖名稱');
    }
    
  2. 可以用name('路由名稱')加上命名,例如:
    Route::get('/exam', function () {
        return view('index');
    })->name('exam.index');
  3. 為什麼要命名?因為以後可以讓樣板呼叫用(換網址、換路徑都不用再修改程式碼),利用名稱來找到 route,如:
    <a href="{{ route('exam.index') }}">測驗一覽</a>
  4. 查詢有多少 Route 可用

    php artisan route:list

Laravel 5.6 入門講義

7-2 有控制器的路由寫法

  1. 假設有個資料表叫做exams,而且已經有控制器(或者直接就想用控制器作法),那麼,路由可以這樣設定:
    Route::get('/exam', 'ExamController@index')->name('exam.index');
    Route::get('/exam/create', 'ExamController@create')->name('exam.create');
    Route::post('/exam/store', 'ExamController@store')->name('exam.store');
    Route::get('/exam/{id}', 'ExamController@show')->name('exam.show');
    Route::get('/exam/{id}/edit', 'ExamController@edit')->name('exam.edit');
    Route::patch('/exam/{id}', 'ExamController@update')->name('exam.update');
    Route::delete('/exam/{id}', 'ExamController@destroy')->name('exam.destroy');
    
  2. 上面的設定更可以簡化寫一行即可,如:
    Route::resource('exam' , 'ExamController');
  3. 到這裡只是先有個基本概觀,底下我們會根據功能把路由一個一個依序產生出來,所以,接下來一般開發程式是:

建立路由(指定控制器)→修改控制器→修改或建立blade樣板(有畫面的話)

Laravel 5.6 入門講義

8. 建立新增測驗的操作界面

一、新增測驗的路由(/exam/create)

  1. 一般「新增」功能,其路由名稱預設是/create(強烈建議,但非強制),搭配資源,其路由便是「/exam/create
  2. 也就是說,當瀏覽器網址輸入「http://網址/exam/create」時,系統便會新增測驗的界面
  3. 由於是從網址輸入,所以其動詞是get
  4. 此界面可放在名為/專案/resources/views/exam/create.blade.php的視圖中,利用view()來呼叫之
  5. 最後,將此路由命名為exam.create,以方便連結。所以,請開啟\專案\routes\web.php網站路由並編輯之:
    Route::get('/exam/create', function () {
        return view('exam.create');
    })->name('exam.create');
  6. 若日後由做了控制器,其動作便可直接改送到 ExamController@create 去操作該做的後續動作。
    Route::get('/exam/create', 'ExamController@create')->name('exam.create');

二、建立create的視圖

  1. 接著製作一個新的子視圖 /專案/resources/views/exam/create.blade.php
    @extends('layouts.app') 
    @section('content')
        <h1>{{ __('Create Exam') }}</h1>
    @endsection
  2. 如此 create.blade.php 就會繼承 layouts/app.blade.php 的樣板,並將設定值帶入使用。

  3. 由於有用到語系,所以,記得在\專案\resources\lang\zh-TW.json增加一組語系設定

    "Create Exam":"建立測驗"
  4. 請執行「http://localhost/exam/create」試試~

三、修改選單

  1. 修改 /專案/resources/views/layouts/nav.blade.php 把建立測驗的選項加入:
    @section('my_menu')
        @can('後台管理')
            <li><a class="nav-link" href="/admin">{{ __('Admin') }}</a></li>
        @endcan
    
        @can('建立測驗')
            <li><a class="nav-link" href="{{ route('exam.create') }}">{{ __('Create Exam') }}</a></li>
        @endcan
    @show
  2. 由於教師和管理員都有「建立測驗」的權限,與其判斷兩個角色,不如判斷一個權限來的簡單,所以,利用@can('建立測驗')@endcan來判斷目前登入者有無「建立測驗」的權限。

  3. 後台管理部份原本是利用@role('管理員')來判斷,但實際上,直接根據權限來判斷會更準確。

  4. 其中連結部份我們用 route('exam.create') 的寫法,直接呼叫路由名稱即可。

  5. 詳情請看:https://github.com/laravel-backpack/permissionmanager#using-blade-directives

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

8-1 安裝marvinlabs/laravel-html-bootstrap-4套件

一、關於marvinlabs/laravel-html-bootstrap-4套件

  1. 表單套件非常多,以前講義介紹的GeneaLabs/laravel-casts套件,雖然使用簡單,但其中有不少元件無法正常使用,例如switch或radio。另外netojose/laravel-bootstrap-4-forms套件則是太過陽春,一些比較細節的地方無法施做,例如無法製作水平表單、hidden無法取消label,反而是radio無法加上laebl,雖然無關功能,但對於畫面比較要求者會玩得很痛苦。
  2. 因此,在此推薦另一個也是支援bootstrap4的表單套件marvinlabs/laravel-html-bootstrap-4
  3. 官網:https://github.com/marvinlabs/laravel-html-bootstrap-4
  4. 安裝 marvinlabs/laravel-html-bootstrap-4
    composer require marvinlabs/laravel-html-bootstrap-4

二、建立表單

  1.  預設的 POST 表單,選項部份有需要再填
    {{ bs()->openForm('post', '/exam' , [ 其他選項陣列]) }}
    {{ bs()->closeForm() }}
  2. 選項部份
    'files' => true, //需要上傳檔案時
    'model' => $exam, //綁預設值時
    'hideErrors' => true,  //隱藏錯誤
    'inline' => true,  //行內表單
    //自定義表單屬性
    'attributes' => [
        'id'       => 'create_user_form',
        'v-cloack' => '',
    ]
    
  3. 水平表單並不在這裡設定,而是在各自元件中設定。

三、各種元件用法

  1. text 文字輸入框
    {{ bs()->input('text', 'username', '吳弘凱')->placeholder('請填入姓名') }}
    {{ bs()->text('username')->placeholder('請填入姓名') }}
    {{ bs()->text('username', '小型輸入框')->sizeSmall() }}
    {{ bs()->text('username', '大型數入框')->sizeLarge() }}
    {{ bs()->hidden('op', '隱藏欄位') }}
    {{ bs()->password('pass', '密碼欄位') }}
    {{ bs()->email('email', 'Email欄位') }}
    {{ bs()->token() }}
  2. textarea 大量文字輸入框
    {{ bs()->textArea('content', '這是大量文字框') }}
  3. select 下拉選單
    {{ bs()->select('enable', ['1' => '開啟', '0' => '關閉'], '1') }}
    
    //多選
    {{ bs()->select('color', ['red' => '紅色', 'green' => '綠色', 'biue' => '藍色'])
           ->multiple()
           ->value(['red', 'green'])
           ->placeholder('可多選') }}
    
    //高興的話placeholder()也可以用第二個參數賦予值
    {{ bs()->select('color', ['red' => '紅色', 'green' => '綠色', 'biue' => '藍色'])
           ->placeholder('請選擇顏色', -1) }}
    
  4. checkbox 方框(不支援複選,欲用複選功能,請用select)
    {{ bs()->checkbox('remember')->description('記住我') }}
    //預設勾選
    {{ bs()->checkbox('remember')->description('記住我')->checked() }}
    //整合上方的一行式寫法
    {{ bs()->checkbox('remember', '記住我', true) }}
    //可以自訂值,也可以設成inline
    {{ bs()->checkbox('remember', '記住我')->value('yes')->inline() }}
  5. radio 單選圓框及radioGroup多選項
    //不指定值的話,預設的值為1
    {{ bs()->radio('enable')->description('啟用') }}
    //預設勾選
    {{ bs()->radio('enable')->description('啟用')->checked() }}
    //整合上方的一行式寫法
    {{ bs()->radio('enable', '啟用', true) }}
    //可以自訂值,也可以設成inline
    {{ bs()->radio('enable', '啟用')->value('yes')->inline() }}
    //多個選項的寫法
    {{ bs()->radioGroup('enable', [1 => '啟用', 0 => '關閉'])
           ->selectedOption(1)
           ->inline()
           ->radioDisabled()
           ->addRadioClass(['bg-light', 'my-3']) }}
  6. file檔案上傳(在form裡面務必加入 'files' => true
    {{ bs()->file('avatar2', '選擇一個檔案') }}
    {{ bs()->simpleFile('avatar') }}
  7. button 按鈕(共有以下樣式可選:primary, secondary, success, danger, warning, info, light, dark
    {{ bs()->submit('送出按鈕') }}
    {{ bs()->button('一般按鈕') }}
    //第二個參數指定樣式,第三個是否為外框按鈕
    {{ bs()->button('外框按鈕', 'success', true) }}
    //把連結做成按鈕
    {{ bs()->a('#', '把連結做成按鈕')->asButton('secondary') }}
  8. badge徽章(共有以下樣式可選:primary, secondary, success, danger, warning, info, light, dark
    {{ bs()->badge()->text('預設徽章') }}
    {{ bs()->badge()->text('顯示成藥丸狀')->pill() }}
    {{ bs()->badge('info')->text('加上連結')->link('#') }}
  9. inputGroup 表單元件組(可套用->sizeSmall() ->sizeLarge()來控制尺寸,亦可和 textareabutton搭配使用)
    {{ bs()->inputGroup()
           ->prefix('共')
           ->suffix('元')
           ->control(bs()->text('username')->placeholder('請填入金額')) }}
  10. Readonly 唯讀(可套用至text、textarea)
    {{ bs()->text('username', '這是唯讀的')->readOnly() }}
    {{ bs()->text('username', '這是唯讀的,而且顯示成一般文字(但仍是欄位)')->readOnly(true) }}
  11. Disabled 關閉(可套用至text、textarea、select、checkbox、radio )
    {{ bs()->text('username', '這是無效的')->disabled() }}

四、一般表單群組用法

  1. 只要把底下的「姓名」替換成想要的標籤,bs()->text('username')換成想要的元件即可。
    {{ bs()->formGroup()
            ->label('姓名')
            ->control(bs()->text('username'))
            ->helpText('請在此填入姓名')}}

五、水平表單群組用法

  1. 利用showAsRow()就可以使用水平表單
  2. label('姓名', false, 'text-sm-right') 的第二個參數是是否使用 sr-only,若true,標籤會消失(預設為false)。第三個參數是可以自行加入額外的class,例如用text-sm-right讓標籤只有在螢幕大於576px時才會靠右 。
  3. 只要把底下的「姓名」替換成想要的標籤,bs()->text('username')換成想要的元件即可。
    {{ bs()->formGroup()
           ->label('姓名', false, 'text-sm-right')
           ->control(bs()->text('username'))
           ->showAsRow() }}

六、提供兩個視圖元件

  1. 警告視窗(type可套用 primarysecondarysuccessdangerwarninginfolightdark),原component位於\專案\vendor\marvinlabs\laravel-html-bootstrap-4\resources\views\alert.blade.php
    @component('bs::alert', ['type' => 'info', 'animated' => true, 'dismissible' => true, 'data' => ['alert-id' => 40, 'context' => 'sample-code']])
        @slot('heading')
            info 警告視窗
        @endslot
    
        <p>dismissible 右上會出現 x 符號,用來關閉;animated 在關閉時會有漸變效果;data 可以用來設置一些屬性</p>
        <p>除了 type 外,其餘參數截可省略</p>
    @endcomponent
  2. jumbotron巨幕,原component位於\專案\vendor\marvinlabs\laravel-html-bootstrap-4\resources\views\jumbotron.blade.php
    @component('bs::jumbotron', ['fluid' => true])
        @slot('heading')
            巨幕主標題
        @endslot
        @slot('subheading')
            這裡是次標題
        @endslot
    
        <hr class="my-3">
        <p>這裡愛寫啥都行</p>
    
        @slot('actions')
            {!! bs()->a('#', '這是個連結按鈕')->asButton('primary') !!}
        @endslot
    @endcomponent

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

8-2 建立發布測驗的表單

  1. 先來製作發佈測驗的前端界面
  2. 修改或建立 /專案/resources/views/exam/create.blade.php,簡易版可以這樣寫:
    @extends('layouts.app')
    @section('content')
        <h1>{{ __('Create Exam') }}</h1>
        @can('建立測驗')
            {{ bs()->openForm('post', '/exam') }}
                {{ bs()->text('title')->placeholder('請填入測驗標題') }}
                {{ bs()->radioGroup('enable', [1 => '啟用', 0 => '關閉'])
                    ->selectedOption(1)
                    ->inline() }}
                {{ bs()->submit('建立測驗') }}
            {{ bs()->closeForm() }}
        @else
            @component('bs::alert', ['type' => 'danger'])
                @slot('heading')
                無建立測驗的權限
                @endslot
            @endcomponent
        @endcan
    @endsection
  3. 先用@can('建立測驗')來判斷有無權限,若有,就製作表單。若無,則進入@else顯示警告畫面。

  4. {{ bs()->openForm('post', '/exam') }}是用來產生表單,以{{ bs()->closeForm() }}做結尾,裡面的設定說明如下:

    • 回顧一下用來儲存的路由:動作是post,路徑是/exam,別名是exam.store詳情請看這裡

    • url('/exam')來設定表單欲傳送的位址,因為是用post方式傳遞,所以路徑直接指向測驗的路徑即/exam即可,不需要指定/exam/store

  5. 簡易版的看起來像這樣
  6. 可以加入表單群組,做成水平表單
    @extends('layouts.app')
    @section('content')
        <h1>{{ __('Create Exam') }}</h1>
        @can('建立測驗')
            {{ bs()->openForm('post', '/exam') }}
                {{ bs()->formGroup()
                        ->label('測驗標題', false, 'text-sm-right')
                        ->control(bs()->text('title')->placeholder('請填入測驗標題'))
                        ->showAsRow() }}
    
                {{ bs()->formGroup()
                        ->label('測驗狀態', false, 'text-sm-right')
                        ->control(bs()->radioGroup('enable', [1 => '啟用', 0 => '關閉'])
                                    ->selectedOption(1)
                                    ->inline())
                        ->showAsRow() }}
    
                {{ bs()->formGroup()
                        ->label('')
                        ->control(bs()->submit('建立測驗'))
                        ->showAsRow() }}
            {{ bs()->closeForm() }}
        @else
            @component('bs::alert', ['type' => 'danger'])
                @slot('heading')
                無建立測驗的權限
                @endslot
            @endcomponent
        @endcan
    @endsection
    
  7. 看起來像這樣:
     

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

9. 建立Exam的控制器controller

  1. 為了讓系統可以執行儲存的動作,需建立controller(控制器)來實現之。以下是建立一個完整(有包含CRUD 路由)的Exam控制器:
    php artisan make:controller ExamController --resource
    
  2. 會自動生出 /專案/app/Http/Controllers/ExamController.php
    <?php
    
    namespace App\Http\Controllers;
    
    use Illuminate\Http\Request;
    
    class ExamController extends Controller
    {
        /**
         * Display a listing of the resource.
         *
         * @return \Illuminate\Http\Response
         */
        public function index()
        {
            //
        }
    
        /**
         * Show the form for creating a new resource.
         *
         * @return \Illuminate\Http\Response
         */
        public function create()
        {
            //
        }
    
        /**
         * Store a newly created resource in storage.
         *
         * @param  \Illuminate\Http\Request  $request
         * @return \Illuminate\Http\Response
         */
        public function store(Request $request)
        {
            //
        }
    
        /**
         * Display the specified resource.
         *
         * @param  int  $id
         * @return \Illuminate\Http\Response
         */
        public function show($id)
        {
            //
        }
    
        /**
         * Show the form for editing the specified resource.
         *
         * @param  int  $id
         * @return \Illuminate\Http\Response
         */
        public function edit($id)
        {
            //
        }
    
        /**
         * Update the specified resource in storage.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  int  $id
         * @return \Illuminate\Http\Response
         */
        public function update(Request $request, $id)
        {
            //
        }
    
        /**
         * Remove the specified resource from storage.
         *
         * @param  int  $id
         * @return \Illuminate\Http\Response
         */
        public function destroy($id)
        {
            //
        }
    }
    
  3. 裡面的函數都已經幫我們先預先建立好,可以省下不少功夫!
    1. index() 顯示資料(一般是列表)
    2. create() 建立新資料(通常是表單界面)
    3. store($request)儲存資料
    4. show($id)顯示某筆資料
    5. edit($id)編輯某筆資料(通常是表單界面)
    6. update($request, $id)更新某筆資料
    7. destroy($id)刪除某筆資料
  4. 若只想建立一個空的控制器,可以這樣用
    php artisan make:controller ExamController

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

9-1 修改路由改用控制器

  1. 修改路由 /專案/routes/web.php,從原本直接送到視圖,改成送到控制器,由控制器去處理後續動作。
    Route::get('/exam', 'ExamController@index')->name('exam.index');
    Route::get('/exam/create', 'ExamController@create')->name('exam.create');
  2. 修改控制器 /專案/app/Http/Controllers/ExamController.php,將列表(首頁)或發佈界面,直接回傳視圖。

    public function index()
    {
        return view('welcome');
    }
    
    public function create()
    {
        return view('exam.create');
    }
  3. 再次連到http://localhost/examhttp://localhost/exam/create,如果畫面都和之前一樣的話,就表示路由已經成功由傳回視圖,改成用控制器來控制了。

到GitHub觀看此單元程式異動  

Laravel 5.6 入門講義

9-2 在視圖中取得使用者編號

  1. 由於建立測驗時,我們需要紀錄建立者的編號,所以,我們修改\專案\resources\views\exam\create.blade.php視圖,加入隱藏欄位,並用Auth::id()取得目前登入者的使用者編號:
    {{ bs()->openForm('post', '/exam') }}
        {{ bs()->formGroup()
                ->label('測驗標題', false, 'text-sm-right')
                ->control(bs()->text('title')->placeholder('請填入測驗標題'))
                ->showAsRow() }}
    
        {{ bs()->formGroup()
                ->label('測驗狀態', false, 'text-sm-right')
                ->control(bs()->radioGroup('enable', [1 => '啟用', 0 => '關閉'])
                            ->selectedOption(1)
                            ->inline())
                ->showAsRow() }}
        {{ bs()->hidden('user_id', Auth::id()) }}
        {{ bs()->formGroup()
                ->label('')
                ->control(bs()->submit('建立測驗'))
                ->showAsRow() }}
    {{ bs()->closeForm() }}
  2. Auth::id()是用來取得目前登入者的編號,詳細用法可參考:https://laravel-china.org/docs/laravel/5.6/authentication/1379#retrieving-the-authenticated-user

到GitHub觀看此單元程式異動  

Laravel 5.6 入門講義

9-3 將資料寫進資料庫

  1. 修改路由 /專案/routes/web.php,儲存部份改用控制器:
    Route::post('/exam', 'ExamController@store')->name('exam.store');
  2. 接著編輯控制器 /專案/app/Http/Controllers/ExamController.php,修改 store 方法,儲存後本來應該要轉向到建立題目的頁面,但因為相關頁面都還沒做,所以還是暫時轉回測驗首頁。
    public function store(Request $request)
    {
        $exam          = new Exam;
        $exam->title   = $request->title;
        $exam->user_id = $request->user_id;
        $exam->enable  = $request->enable;
        $exam->save();
        return redirect()->route('exam.index');
    }
  3. 記得在上方告知要使用 App\Exam 模型
    <?php
    
    namespace App\Http\Controllers;
    
    use App\Exam;
    use Illuminate\Http\Request;
  4. $request 就是使用者輸入的內容,以物件方式存在。詳情可參考:https://laravel-china.org/docs/laravel/5.6/requests/1367#accessing-the-request
  5. Exam則是Eloquent Model,也就是用來操作exam資料表的模型。儲存部份可參考:https://laravel-china.org/docs/laravel/5.6/eloquent/1403#inserting-and-updating-models
  6. redirect() 用來轉向,詳細用法可參考:https://laravel-china.org/docs/laravel/5.6/responses/1368#c51dd1
  7. 接著可以試試看是否能儲存文章囉!(請暫時先到資料庫去看結果)

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

9-4 用 fillable 批量賦值寫入

  1. 另一種更簡單的方式是改用 fillable 批量賦值(Mass Assignment)寫法
    public function store(Request $request)
    {
        Exam::create([
            'title'   => $request->title,
            'user_id' => $request->user_id,
            'enable'  => $request->enable,
        ]);
        return redirect()->route('exam.index');
    }
  2. 接著到 /專案/app/Exam.php 設定哪些欄位可以使用 fillable

    class Exam extends Model
    {
        protected $fillable   = [
            'title', 'user_id', 'enable',
        ];
    }
    
  3. 由於enableboolean類型,但在資料庫卻被存成數字,所以,需要做一下型別轉換,因此,我們利用$casts來進行型別轉換。關於型別轉換,可參考:https://laravel-china.org/docs/laravel/5.6/eloquent-mutators/1406#attribute-casting
    protected $casts = [
        'enable' => 'boolean',
    ];
  4. 若是有欄位因為資安或其他問題,不想讓它使用批量賦值,那請用 guarded 屬性,例如:
    protected $guarded = [
        'id', 'password',
    ];
  5. 批量賦值詳情可參考:https://laravel-china.org/docs/laravel/5.6/eloquent/1403#c7d398

二、更簡易的寫法

  1. 也可以用 $request->all() 取得使用者填寫的所有資料陣列
    public function store(Request $request)
    {
        Exam::create($request->all());
        return redirect()->route('exam.index');
    }
  2. 完工!

  3. 關於 Request 的詳情可以查看:https://laravel-china.org/docs/laravel/5.6/requests/

 

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

10. 表單驗證及錯誤處理

  1. 完整驗證說明:https://laravel-china.org/docs/laravel/5.6/validation/1302#189a36
  2. 編輯控制器 /專案/app/Http/Controllers/ExamController.php,修改儲存方法:
    public function store(Request $request)
    {
        $this->validate($request, [
            'title' => 'required|min:2|max:255',
        ]);
        Exam::create($request->all());
        return redirect()->route('exam.index');
    }
  3. 接著修改/專案/resources/views/exam/create.blade.php 樣板檔,當有錯誤的時候顯示出來:

    @if (count($errors) > 0)
        @component('bs::alert', ['type' => 'danger'])
            <ul>
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        @endcomponent
    @endif
  4. 如此就可以測試看看了!

  5. 若發現沒有中文訊息,可以直接將\專案\vendor\caouecs\laravel-lang\src\zh-TW\整個目錄複製到\專案\resources\lang\底下

    cp -R vendor\caouecs\laravel-lang\src\zh-TW resources\lang\
  6. 若是想自己加上屬於自己的訊息,那可以這樣做:

    public function store(Request $request)
    {
        $this->validate($request, [
            'title' => 'required|min:2|max:255',
        ], [
            'required' => '「:attribute」為必填欄位',
            'min'      => '「:attribute」至少要 :min 個字',
            'max'      => '「:attribute」最多只能 :max 個字',
        ]);
        Exam::create($request->all());
        return redirect()->route('exam.index');
    }
  7. 其中:attribute會帶出有錯誤的表單元件的name,如此,會比較清楚是哪個欄位沒填好。
  8. 關於錯誤處理可以參考:https://laravel-china.org/docs/laravel/5.6/validation/1302#working-with-error-messages

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

10-1 建立Request來統一驗證

  1. 儲存時要驗證,未來修改時也是要驗證,那是否可以統一驗證?才不用到處都寫驗證規則?而且還可以讓控制器保持乾乾淨淨?可以,善用表單請求類(request)即可。詳情可見:https://laravel-china.org/docs/laravel/5.6/validation/1372
  2. 建立 request
    php artisan make:request ExamRequest
  3. 會產生 /專案/app/Http/Requests/ExamRequest.phpauthorize()部份是用來判別目前登入者是否有權限執行此動作(驗證後要執行的動作),若懶得設可先填入 true,以免無法使用表單。但我們因為有裝了權限控制的Backpack\PermissionManager套件,所以,直接用裡面的方法can()來判斷有無權限即可。

    public function authorize()
    {
        return $this->user()->can('建立測驗');
    }
    
  4. 再將原先規則搬入rules()中。

    public function rules()
    {
        return [
            'title'  => 'required|min:2|max:255',
        ];
    }
  5. 若需要自訂訊息,只要新增一個messages函數,並將自訂訊息移入即可。
    public function messages()
    {
        return [
            'required' => '「:attribute」為必填欄位',
            'min'      => '「:attribute」至少要 :min 個字',
            'max'      => '「:attribute」最多只能 :max 個字',
        ];
    }
  6. 若是想替欄位命名則可利用attribute來進行設定

    public function attributes()
    {
        return [
            'title'      => '測驗標題',
        ];
    }
  7. 修改 /專案/app/Http/Controllers/ExamController.php 控制器,修改 store$request 的類別改用 ExamRequest,並將原本在裡面的驗證拿掉。

    public function store(ExamRequest $request)
    {
        Exam::create($request->all());
        return redirect()->route('exam.index');
    }
  8. 在上方加入使用我們自製的 ExamRequest

    use App\Http\Requests\ExamRequest;
  9. 日後若有 update 時,也是比照辦理即可,無須在控制器裡面寫驗證。
    public function update(ExamRequest $request, $id)
    {
        //
    }

 

到GitHub觀看此單元程式異動  

Laravel 5.6 入門講義

11. 讀出所有測驗

一、流程

  1. 有了測驗,就要讓具備「建立測驗」的身份者來建立題庫
  2. 建立題庫時,要先列出所有測驗主題,指定某測驗後才能建立題庫
  3. 題目架構為:題目、四個選項、一個標準答案

二、整裡頁面流程及選單

  1. 列出所有測驗的路由為動詞為 GET,路由為/exam,控制器為 ExamController@index,所以,我們先開啟routes/web.php 確認有沒有以下這個路由。
    Route::get('/exam', 'ExamController@index')->name('exam.index');
  2. 若希望以後一登入就到此畫面,亦可將原本的//home路由修改一下,控制器均改為ExamController@index,這樣的意思就是凡是連到首頁或者/home(登入後頁面)都會連到測驗首頁。

    Route::get('/', 'ExamController@index')->name('index');
    Route::get('/home', 'ExamController@index')->name('home.index');
  3. 接著開啟控制器 /專案/app/Http/Controllers/ExamController.php,編輯裡面的index()函數,為了儘量符合預設,我們把index()的樣板改為exam.index,也就是直接用view()來執行/專案/resources/views/exam/index.bload.php樣版的意思

    public function index()
    {
        return view('exam.index');
    }
  4. 由於index.bload.php樣板還不存在,所以,可以直接把/專案/resources/views/welcome.blade.php另存為 /專案/resources/views/exam/index.blade.php 樣板。

    mv resources/views/welcome.blade.php resources/views/exam/index.blade.php
  5. 如此,不管登入登出,都會直接到此頁面,並在工具列上顯示適當的工具
  6. 接著將home.blade.php裡面的顯示轉向訊息,複製到/專案/resources/views/layouts/app.blade.php中,如此,日後若有轉向訊息便可呈現出來(不管在什麼功能中)。詳情請參考:https://laravel-china.org/docs/laravel/5.6/responses/1368#1bca7f

    <div class="container">
        @yield('content')
    
        @if(session('status'))
            @component('bs::alert', ['type' => 'info'])
                {{ session('status') }}
            @endcomponent
        @endif
    </div>
  7. 然後,就可以把home.blade.php刪除(因為也用不到了)

三、列出所有測驗

  1. 接著開啟控制器 /專案/app/Http/Controllers/ExamController.php,編輯裡面的index()函數,並將所有測驗的資料陣列用compact('exams')傳至樣板

    public function index()
    {
        $exams = Exam::all();
        return view('exam.index',compact('exams'));
    }
  2. 至於Exam::all()則是Exam模型取得所有資料的用法(在上面有use App\Exam;才能這樣使用),更多方法可參考:https://laravel-china.org/docs/laravel/5.6/eloquent-collections/1405#26e4a9

  3. 接著開啟/專案/resources/views/exam/index.blade.php利用@foreach@endforeach寫法來產生迴圈:

    @extends('layouts.app')
    @section('content')
        <h1>測驗一覽</h1>
        <ul class="list-group">
            @foreach($exams as $exam)
                <li class="list-group-item">
                    {{ $exam->created_at->format("Y年m月d日") }} -
                    <a href="exam/{{ $exam->id }}">
                        {{ $exam->title }}
                    </a>
                </li>
            @endforeach
        </ul>
    @endsection
    
  4. $exam->created_at->format("Y年m月d日")是用Carbon 日期時間套件的功能,該套件已經內建於 Laravel 5 中,詳細用法可以參考:https://laravel-china.org/docs/laravel/5.6/eloquent-mutators/1335#7a3effhttp://laravel5-book.kejyun.com/package/tool/package-tool-carbon.html

  5. 預設情況下,Eloquent 會把 created_atupdated_at 字段轉換成 Carbon 實例, 它繼承了 PHP 原生的 DateTime 類,並提供了各種有用的方法。若有其他日期欄位想轉換成Carbon 實例,可以直接在Model中利用 $dates 屬性,自行定義哪些日期類型字段會被自動轉換:

    /**
    * 應被轉換為日期的屬性。
    *
    * @var array
    */
    protected $dates = ['birthday'];
  6. 至於日期格式,可以參考 PHP 手冊:http://php.net/manual/en/function.date.php

  7. 另外一種@forelse搭配@empty@endforelse寫法,可以在沒有資料的情形顯示自訂內容:

    @extends('layouts.app')
    @section('content')
        <h1>測驗一覽</h1>
        <ul class="list-group">
            @forelse($exams as $exam)
                <li class="list-group-item">
                    {{ $exam->created_at->format("Y年m月d日") }} -
                    <a href="exam/{{ $exam->id }}">
                        {{ $exam->title }}
                    </a>
                </li>
            @empty
                <li class="list-group-item">尚無任何測驗</li>
            @endforelse
        </ul>
    @endsection
    
  8. 大功告成!請直接在網址輸入:  http://localhost/examhttp://localhost(注意,後面不可以有 / ,否則路由以後會誤判)

  9. 其中每個測驗的連結均為:exam/編號,如此,符合顯示單一資料的路由寫法

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

11-1 讀出時加入各種條件

  1. 修改/專案/app/Http/Controllers/ExamController.php
    public function index()
    {
        $exams = Exam::where('enable', 1)
            ->orderBy('created_at', 'desc')
            ->take(10)
            ->get();
        return view('exam.index', compact('exams'));
    }
  2. where()用來指定條件,我們指定有啟用的測驗

  3. orderby()用來指定排序,我們指定用建立時間做反向排序(由新到舊)

  4. take()用來抓出數量

  5. get()取得資料

  6. 若要用兩個(and)篩選條件:

    $exams = Exam::where('enable', 1)
        ->where('created_at', '>', '2018-05-30')
  7. 若要用 or,則用閉包+orWhere()方式:

    $exams = Exam::where(function ($query) {
        $query->where('enable', 1)
            ->orWhere('user_id', 1);
    })
  8. 詳情可參考:https://laravel-china.org/docs/laravel/5.6/queries/1398#387ca8

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

11-2 加入分頁

  1. 修改 /專案/app/Http/Controllers/ExamController.php 控制器,將 get() 換成 paginate(數量) 即可
    public function index()
    {
        $exams = Exam::where('enable', 1)
            ->orderBy('created_at', 'desc')
            ->paginate(2);
        return view('exam.index', compact('exams'));
    }
  2. 亦可用simplePaginate(),如此,會顯示上頁、下頁。

  3. 修改 /專案/resources/views/exam/index.blade.php 樣板,加入分頁工具:

    <div class="my-3">
        {{ $exams->links() }}
    </div>
  4. 亦可用$exams->total()來顯示所有資料數量:

    <h1>測驗一覽<small>(共 {{$exams->total()}} 筆資料)</small></h1>
  5. 更多方法請參考:https://laravel-china.org/docs/laravel/5.6/pagination/1399#f704ff

 

到GitHub觀看此單元程式異動  

Laravel 5.6 入門講義

12. 讀出單一測驗

  1. 點進去某個測驗,路由是連到 exam/{{$exam->id}},所以,在/routes/web.php新增一筆路由資料
    Route::get('/exam/{id}', 'ExamController@show')->name('exam.show');
  2. 路徑中的 {id} 可以直接在控制器中變成變數$id使用,所以,無須自己帶參數過去。不過路由的參數和控制器的變數是以先後位置來對應的,無關名稱,換言之,控制器中不命名為 $id 也是可以的。
  3. 如果該參數可有可無,那麼可以寫成 {id?} 這樣的方式。
  4. 如果路由有參數,且確定其格式,那麼可以用->where() 限制參數格式(否則exam/create 中的 create 也可能會被當成id),例如:限制id 只允許 0~9 的數字,如此可以降低路由被誤判的機會
    Route::get('/exam/{id}', 'ExamController@show')->name('exam.show')->where('id', '[0-9]+');
  5. 如果同一個參數要限制格式,且有好幾個 Route 要用,則可用 Route::pattern() 方式來統一宣告(記得放最上面):
    Route::pattern('id' , '[0-9]+');
  6. 接著在 /專案/app/Http/Controllers/ExamController.php 控制器中,修改 show 的方法,其中 $id 便是從路由過來的:

    public function show($id)
    {
        $exam = Exam::find($id);
        return view('exam.show', compact('exam'));
    }
  7. find() 是 Eloquent 根據主鍵用來取出該筆資料用的。詳情請看:https://laravel-china.org/docs/laravel/5.6/eloquent/1332#retrieving-single-models

  8. 最後,只要製作好樣板,讓資料顯示出來即可,請建立 /專案/resources/views/exam/show.blade.php

    @extends('layouts.app') 
    @section('content')
        <h1 class="text-center">{{$exam->title}}</h1>
    
        <div class="text-center">
            發佈於 {{$exam->created_at->format("Y年m月d日 H:i:s")}} / 最後更新: {{$exam->updated_at->format("Y年m月d日 H:i:s")}}
        </div>
    @endsection

 

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

12-1 路由模型綁定

  1. 如果還要更簡單一點,Laravel 提供「路由模型綁定」功能來簡化找出資料的方式,它可以讓我們自己定義一個特定的參數名稱來指示路由解析器去尋找一筆Eloquent紀錄(亦即根據流水號取得該筆資料),並將該資料傳入,而非將流水號傳入。

  2. 我們先修改路由,將{id}直接改為{exam}

    Route::pattern('exam', '[0-9]+');
    //略
    Route::get('/exam/{exam}', 'ExamController@show')->name('exam.show');
    
  3. 然後修改控制器\專案\app\Http\Controllers\ExamController.php,將show()括號中的$id改為和路由傳進來的名稱一致,亦即$exam,然後在類型約束(typehint)上加入Exam模型。如此,Laravel 就會自動去抓取該編號的所有資料,因此,以下第3行的$exam實際上是整個Exam模型資料,而非編號。

    public function show(Exam $exam)
    {
        return view('exam.show', compact('exam'));
    }
  4. 一般我們稱這方式為「隱式路由模型綁定」

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

12-2 建立並執行題目的Model 及 migrate 文件

一、編輯題目的 migrate 檔案

  1. 建立Topic(題目)的 Eloquent 模型,以便將一個資料表變成一個物件來操作,並且順便產生 migration 檔案

    php artisan make:model Topic --migration
  2. 編輯 /專案/database/migrations/日期_create_topics_table.php

    <?php
    
    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Support\Facades\Schema;
    
    class CreateTopicsTable extends Migration
    {
        /**
         * Run the migrations.
         *
         * @return void
         */
        public function up()
        {
            Schema::create('topics', function (Blueprint $table) {
                $table->increments('id');
                $table->string('topic');
                $table->unsignedInteger('exam_id');
                $table->foreign('exam_id')->references('id')->on('exams')->onDelete('cascade');
                $table->string('opt1');
                $table->string('opt2');
                $table->string('opt3');
                $table->string('opt4');
                $table->unsignedTinyInteger('ans');
                $table->timestamps();
            });
        }
    
        /**
         * Reverse the migrations.
         *
         * @return void
         */
        public function down()
        {
            Schema::dropIfExists('topics');
        }
    }
  3. topic:題目
  4. exam_id:對應的測驗編號
  5. opt1~opt4:選項1~4
  6. ans:正確解答
  7. 其中 20 行的 onDelete('cascade') 是一個約束條件,也就是當測驗刪除時,連同題目也一併刪除之意。 詳情:https://laravel-china.org/docs/laravel/5.6/migrations/1400#499c95
  8. 最後執行資料庫遷移即可建出新的資料表

    php artisan migrate
  9. 一樣看一下是否有建立出來

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

12-3 新增題目編輯表單

  1. 我們打算在某一個測驗頁面(exam.show)列出該測驗標題,及所有題目和選項解答,同時,又可以在該頁面放個表單以方便建立新題目。這樣該怎麼做?首先,先來放個表單。
  2. 目前執行的路由是:
    Route::get('/exam/{exam}', 'ExamController@show')->name('exam.show');
  3. 也就是執行的控制器是ExamController@show,所以,我們開啟/專案/app/Http/Controllers/ExamController.php,找出裡面的show()來看看其前端是送到哪裡去!我們好在該樣板產生一個題目用的表單。

    public function show(Exam $exam)
    {
        return view('exam.show', ['exam' => $exam]);
    }
  4. view()可以得知,會套用/專案/resources/views/exam/show.blade.php,所以,我們開啟它,然後在裡面加入表單的語法:

    @can('建立測驗')
        {{ bs()->openForm('post', '/topic') }}
            {{ bs()->formGroup()
                    ->label('題目內容', false, 'text-sm-right')
                    ->control(bs()->textarea('topic')->placeholder('請輸入題目內容'))
                    ->showAsRow() }}
            {{ bs()->formGroup()
                    ->label('選項1', false, 'text-sm-right')
                    ->control(bs()->text('opt1')->placeholder('輸入選項1'))
                    ->showAsRow() }}
            {{ bs()->formGroup()
                    ->label('選項2', false, 'text-sm-right')
                    ->control(bs()->text('opt2')->placeholder('輸入選項2'))
                    ->showAsRow() }}
            {{ bs()->formGroup()
                    ->label('選項3', false, 'text-sm-right')
                    ->control(bs()->text('opt3')->placeholder('輸入選項3'))
                    ->showAsRow() }}
            {{ bs()->formGroup()
                    ->label('選項4', false, 'text-sm-right')
                    ->control(bs()->text('opt4')->placeholder('輸入選項4'))
                    ->showAsRow() }}
            {{ bs()->formGroup()
                    ->label('正確解答', false, 'text-sm-right')
                    ->control(bs()->select('ans',[1=>1, 2=>2, 3=>3, 4=>4])->placeholder('請設定正確解答'))
                    ->showAsRow() }}
            {{ bs()->hidden('exam_id', $exam->id) }}
            {{ bs()->formGroup()
                    ->label('')
                    ->control(bs()->submit('儲存'))
                    ->showAsRow() }}
        {{ bs()->closeForm() }}
    @endcan
  5. 一樣要用@can()來判斷權限,免得其他人來胡亂新增題目

  6. 記得設定exam_id隱藏欄位,並利用$exam->id來取得目前測驗編號,如此,才知道此題目是屬於那一個測驗的。

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

12-4 儲存題目

  1. 先建立儲存題目的路由
    Route::post('/topic', 'TopicController@store')->name('topic.store');
  2. 因為我們還沒有題目的控制器,所以,先在終端機中建立之:

    php artisan make:controller TopicController --resource
  3. 如此,會產生 /專案/app/Http/Controllers/TopicController.php,開啟之,找到 store() 函數,修改之:

    public function store(Request $request)
    {
        $topic = Topic::create($request->all());
        return redirect()->route('exam.show', $topic->exam_id);
    }
  4. 我們希望儲存會回到原畫面,以便繼續新增題目,故route()一樣指向到 exam.show,並將測驗的編號$topic->exam_id傳到路由中。詳情: https://laravel-china.org/docs/laravel/5.6/routing/1363#redirect-routes

  5. 另外,上方記得加上以下語法,如此Topic::create()才能使用

    use App\Topic;
  6. 由於是用批量賦值的寫法,所以記得到 /專案/app/Topic.php 設定哪些欄位可以使用 fillable。

  7. 修改 /專案/app/Topic.php其內容為:

    class Topic extends Model
    {
        protected $fillable = [
            'topic', 'exam_id', 'opt1', 'opt2', 'opt3', 'opt4', 'ans',
        ];
    }
  8. 批量賦值詳情可參考: https://laravel-china.org/docs/laravel/5.6/eloquent/1403#c7d398

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

12-5 列出題目

  1. 我們已經在某一個測驗頁面(exam.show)列出該測驗標題及表單以方便建立新題目,執行的路由是:
    Route::get('/exam/{exam}', 'ExamController@show')->name('exam.show');
  2. 也就是執行的控制器是ExamController@show,所以,我們若打算把該測驗的既有題目也顯示在此頁面,我們就必須開啟/專案/app/Http/Controllers/ExamController.php,找出裡面的show()來取出目前已經有的所有題目列表。

    public function show(Exam $exam)
    {
        $topics = Topic::where('exam_id', $exam->id)->get();
        return view('exam.show', compact('exam', 'topics'));
    }
  3. 另外,記得在上方加入:
    use App\Topic;
  4. 接著在 /專案/resources/views/exam/show.blade.php 樣板當中加入題目列表:
    <dl>
        @forelse ($topics as $key => $topic)
            <dt>
                <h3>
                @can('建立測驗')
                    ({{$topic->ans}})
                @endcan
                {{ bs()->badge()->text($key+1) }}
                {{$topic->topic}}
                </h3>
            </dt>
            <dd>
                {{ bs()->radioGroup("ans[$topic->id]", [
                        1=>"<span class='opt'>&#10102; $topic->opt1</span>",
                        2=>"<span class='opt'>&#10103; $topic->opt2</span>",
                        3=>"<span class='opt'>&#10104; $topic->opt3</span>",
                        4=>"<span class='opt'>&#10105; $topic->opt4</span>"
                    ])->inline()->addRadioClass(['mx-3']) }}
            </dd>
        @empty
            <div class="alert alert-danger">尚無任何題目</div>
        @endforelse
    </dl>
  5. 選項數的地方我們是利用unicode來做的,例如:&#10102;就可以顯示成,看起來比較清楚。

  6. 圈圈數字可以看這裡:http://xahlee.info/comp/unicode_circled_numbers.html,所有的符號可以看這裡:http://www.utf8-chartable.de/unicode-utf8-table.pl

  7. 修改 /public/css/app.css,加入選項的顏色設定和大小的設定

    .opt{
        color: rgb(17, 112, 136);
        font-size: 1.2em;
    }
  8. 身份為教師時畫面如下:

  9. 若是未登入者,暫時會呈現這樣(不過實際上,未登入應該不出現任何題目,以免題目外洩,這稍後處理):

到GitHub觀看此單元程式異動  

Laravel 5.6 入門講義

12-6 匯入題目

  1. 可以建立一個「環境教育」測驗
  2. 下載 topic.sql
  3. 預設 exam_id 為 2,若測驗編號不是 2,可以直接到 phpmyadmin 將 exam_id 為 2的測驗,直接改為「環境教育」。或者開啟topic.sql,按下Ctrl+H取代之,例如'2'取代為'新數字'
  4. 開啟phpmyadmin,並切換到topics來匯入之
  5. 此外,也可以把inline()拿掉,讓選項垂直排列。
    <dl>
        @forelse ($topics as $key => $topic)
            <dt>
                <h3>
                @can('建立測驗')
                    ({{$topic->ans}})
                @endcan
                {{ bs()->badge()->text($key+1) }}
                {{$topic->topic}}
                </h3>
            </dt>
            <dd>
                {{ bs()->radioGroup("ans[$topic->id]", [
                        1=>"<span class='opt'>&#10102; $topic->opt1</span>",
                        2=>"<span class='opt'>&#10103; $topic->opt2</span>",
                        3=>"<span class='opt'>&#10104; $topic->opt3</span>",
                        4=>"<span class='opt'>&#10105; $topic->opt4</span>"
                    ])->addRadioClass(['mx-3']) }}
            </dd>
        @empty
            <div class="alert alert-danger">尚無任何題目</div>
        @endforelse
    </dl>

     

到GitHub觀看此單元程式異動  

Laravel 5.6 入門講義

12-7 利用Model查詢資料的方法

  1. 讀取全部資料(抓出來的$topics 由於是多筆,所以$topicsCollection類別,可以當物件用,亦可當陣列用,甚至可以輸出json)
    $topics=\App\Topic::all();
  2. 以主索引取出單筆資料(抓出來的$topic 由於只有一筆,所以$topic 是一個Model)

    $topic=\App\Topic::find(12);
  3. 以主索引取出多筆資料

    $topic=\App\Topic::find([2,7,12,35]);
  4. 增加搜尋條件

    $topic=\App\Topic::where('欄位' , '條件' ,'值');
  5. 設定排序

    $topic=\App\Topic::orderBy('欄位' '排序方式');
  6. 串連使用

    $topic=\App\Topic::where('欄位' , '條件' ,'值')->orderBy('欄位' '排序方式')->get();
  7. 隨機抓幾筆

    $topic=\App\Topic::random(數量);
  8. 輸出成 json

    $topic=\App\Topic::all();
    $topic->toJson();
  9. 完整說明:https://laravel-china.org/docs/laravel/5.6/eloquent-collections/1405

Laravel 5.6 入門講義

12-8 測驗與題目的關聯

一、從模型設定關聯

  1. 所謂「關聯」,基本上就是:「順便去撈XX表資料」的意思,所以,一樣可以用where()orderBy()之類的方法。
  2. 從題目(topic)的角度來看,每個題目都有一個所屬(belongsTo)的測驗(exam),所以,開啟題目的模型/專案/app/Topic.php,加入對測驗的 belongsTo 關聯:
    class Topic extends Model
    {
        protected $fillable = [
            'topic', 'exam_id', 'opt1', 'opt2', 'opt3', 'opt4', 'ans',
        ];
    
        public function exam()
        {
            return $this->belongsTo('App\Exam');
        }
    }
  3. 其中App\Exam也可以寫成Exam::class, 自 PHP 5.5 起,關鍵詞 class 也可用於類名的解析。使用 類名::class 可以得到一個字串,包含了類 ClassName 的完全限定名稱,如:命名空間\類名
  4. 此外,方法名稱exam()之後會變成集合的變數名稱,例如$topic->exam
  5. 從測驗(exam)的角度來看,一個測驗可能會有多(hasMany)題目(topic),所以,開啟使用者的模型 /app/Exam.php,加入對題目的 hasMany 關聯:
    class Exam extends Model
    {
        protected $fillable = [
            'title', 'user_id', 'enable',
        ];
    
        protected $casts = [
            'enable' => 'boolean',
        ];
    
        public function topics()
        {
            return $this->hasMany('App\Topic');
        }
    }
  6. 設定好關聯後,在取得 $exam 內容時,就會自動加入 $exam->topics資料陣列,所以,可以利用「 $exam->topics['欄位名稱']」或「 $exam->topics->欄位名稱」的方式來取得題目的相關資料。
  7. 完整關聯請參考:https://laravel-china.org/docs/laravel/5.6/eloquent-relationships/1404

二、修改控制器

  1. 編輯 /專案/app/Http/Controllers/ExamController.php,修改 show() 的方法,基本上,就是回到原狀而已,寫法更簡單:
    public function show(Exam $exam)
    {
        return view('exam.show', compact('exam'));
    }

三、修改樣板

  1. 修改 /專案/resources/views/exam/show.blade.php ,把其中的$topics
    @forelse ($topics as $key => $topic)

    改為$exam->topics即可,如:

    @forelse ($exam->topics as $key => $topic)

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

13. 利用模型工廠產生隨機題目

  1. 建立一個模型工廠, 使用 Artisan 命令 make:factory 並指定模型來快速建立模型工廠:
    php artisan make:factory TopicFactory --model=Topic
  2. 這個新的模型工廠將被放置在 /專案/database/factories 目錄中。
    <?php
    
    use Faker\Generator as Faker;
    
    $factory->define(App\Topic::class, function (Faker $faker) {
        return [
            //
        ];
    });
    
  3. 修改一下內容,讓工廠自動產生加法的數學題目及答案(以加法為例):
    <?php
    
    use Faker\Generator as Faker;
    
    $factory->define(App\Topic::class, function (Faker $faker) {
        $items = [1, 2, 3, 4];
        shuffle($items);
    
        $random_date = $faker->dateTimeBetween('-3 days', '+3 days');
        $num1        = rand(1, 99);
        $num2        = rand(1, 99);
        return [
            'topic'           => $num1 . " + " . $num2,
            'opt' . $items[0] => $num1 + $num2,
            'opt' . $items[1] => $num1 . $num2,
            'opt' . $items[2] => rand(1, 99),
            'opt' . $items[3] => rand(1, 999),
            'ans'             => $items[0],
            'created_at'      => $random_date,
            'updated_at'      => $random_date,
        ];
    });
    
  4. 其中shuffle()是為了給選項隨機排序,並指定第一個選項$items[0]為正確答案,避免答案都是固定的選項。
  5. 完整的Faker用法可以參考:https://github.com/fzaninotto/Faker
  6. 這裡有各種方法的使用範例:https://www.cnblogs.com/love-snow/articles/7655450.html
  7. 若欲產生中文資料,請修改\專案\vendor\fzaninotto\faker\src\Faker\Factory.php
    <?php
    
    namespace Faker;
    
    class Factory
    {
        const DEFAULT_LOCALE = 'zh_TW';
  8. 我們可以利用tinker來測試一下(要離開tinker請輸入exit
    php artisan tinker
  9. 先建立一題試試
    factory(App\Topic::class)->make()
  10. 然後建立5題
    factory(App\Topic::class,5)->make()
  11. 建立5題並指定exam_id為1,然後存入資料庫:
    factory(App\Topic::class,5)->create(['exam_id' => 3])
  12. 可以利用此方式來建立大量測設內容,若工廠內容有改變,請離開tinker後再重新進入執行。

 

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

13-1 建立Seeder快速填充資料

  1. Seeder 就是測試資料,可以利用Artisan的方式來快速產生測試資料
  2. seeder 檔一般以「複數+TableSeeder」方式命名,當然,這不是強制性的。
  3. 建立方法:
    php artisan make:seeder TopicsTableSeeder
  4. 最後會產生\專案\database\seeds\TopicsTableSeeder.php,我們加入第15行的部份,也就是把剛剛在tinker的指令拿到這裡來。
    <?php
    
    use Illuminate\Database\Seeder;
    
    class TopicsTableSeeder extends Seeder
    {
        /**
         * Run the database seeds.
         *
         * @return void
         */
        public function run()
        {
            // 每次建立 20 個題目
            factory(\App\Topic::class, 20)->create(['exam_id' => 3]);
        }
    }
    
  5. 執行之,每執行一次就會產生20個新題目:
    php artisan db:seed --class=TopicsTableSeeder
  6. 如果seed檔要改名,則記得改完後重新手動執行以下語法,以產生新的autoload檔案
    composer dump-autoload
  7. autoload檔案在:\專案\vendor\composer\autoload_classmap.php

  8. 詳情可參考:https://laravel-china.org/docs/laravel/5.6/seeding/1401

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

14. 修改測驗

一、修改樣板,加入編輯按鈕

  1. 修改原來的 /專案/resources/views/exam/show.blade.php 樣板,加入編輯按鈕:

    <h1 class="text-center">
        {{$exam->title}}
        @can('建立測驗')
             <a href="{{route('exam.edit', $exam->id)}}" class="btn btn-warning">編輯</a>
        @endcan
    </h1>
  2. 我們必須根據不同權限顯示不同功能,老師和學生在進入測驗的畫面時,應該要看到不同功能,老師應該是要進行題目管理,學生則是進行測驗。利用@can()就可以判斷不同身份。
  3. 詳情請看:https://github.com/laravel-backpack/permissionmanager

二、修改路由

  1. 修改路由 /專案/routes/web.php 加入 edit 的路由,{exam} 放在前後其實都沒關係。
    Route::get('/exam/{exam}/edit', 'ExamController@edit')->name('exam.edit');

三、修改控制器(用同一個視圖)

  1. 修改的界面主要就是讀出原始內容,然後塞到原來的建立標單裡,也就是套用到exam/create.blade.php的表單中。這裡我們不另外做edit.blade.php用來編輯的視圖,因為編輯和建立的視圖其實90%以上都一樣,弄個兩套維護起來比較不易(容易改了A卻忘了B),所以,我們來看一下用同一個視圖,需要注意哪些事?

    • 建立的http動詞是post,修改則是patch(如此,會自動加上CSRF保護,避免跨站攻擊)

    • 建立的表單action/exam,修改則是/exam/編號

    • 建立時,啟用欄位(enable)預設值為1,修改時,則是視實際情況

  2. 所以,根據以上,我們在控制器中 /專案/app/Http/Controllers/ExamController.php,先加入 edit 的方法:

    public function edit(Exam $exam)
    {
        return view('exam.create', compact('exam'));
    }
    • $exam是一筆完整Exam資料,$exam->id就是該資料的編號

  3. 最後,修改 /專案/resources/views/exam/create.blade.php 樣板:

    @extends('layouts.app')
    @section('content')
        <h1>{{ __('Create Exam') }}</h1>
        @can('建立測驗')
            @if(isset($exam))
                {{ bs()->openForm('patch', "/exam/{$exam->id}" , [ 'model' => $exam]) }}
            @else
                {{ bs()->openForm('post', '/exam') }}
            @endif
                {{ bs()->formGroup()
                        ->label('測驗標題', false, 'text-sm-right')
                        ->control(bs()->text('title')->placeholder('請填入測驗標題'))
                        ->showAsRow() }}
    
                {{ bs()->formGroup()
                        ->label('測驗狀態', false, 'text-sm-right')
                        ->control(bs()->radioGroup('enable', [1 => '啟用', 0 => '關閉'])
                                    ->selectedOption(isset($exam)?$exam->enable:1)
                                    ->inline())
                        ->showAsRow() }}
                {{ bs()->hidden('user_id', Auth::id()) }}
                {{ bs()->formGroup()
                        ->label('')
                        ->control(bs()->submit('建立測驗'))
                        ->showAsRow() }}
            {{ bs()->closeForm() }}
    
            @if (count($errors) > 0)
                @component('bs::alert', ['type' => 'danger'])
                    <ul>
                        @foreach ($errors->all() as $error)
                            <li>{{ $error }}</li>
                        @endforeach
                    </ul>
                @endcomponent
            @endif
        @else
            @component('bs::alert', ['type' => 'danger'])
                @slot('heading')
                無建立測驗的權限
                @endslot
            @endcomponent
        @endcan
    @endsection
    
    
    • 第5行的地方,我們判斷有無傳進 $exam 物件,來決定是要新增還是修改?
    • 第6行的地方,當有測驗編號,那就是修改,http動詞我們改用patchaction路徑也不同,加入測驗編號$exam->id。此外,多了['model' => $exam],也就是此表單要套用預設值。
    • 18行用三元一次寫法,判斷有物件時才使用物件值,沒有就給1
  4. 看起來像這樣:
  5. 最後,只要完成「更新」部份的功能即可。

三、讓被關閉的測驗也能顯示出來

  1. 在修改的過程中發現,被關閉的測驗將不會被列出,故也無法修改,所以,我們需要調整一下,改成若是有「建立測驗」權限者,就可以看到被關閉測驗,若沒有,則不秀出。
  2. 所以,我們先修改控制器/專案/app/Http/Controllers/ExamController.php,編輯 index的方法:
    public function index()
    {
        $user = Auth::user();
        if ($user and $user->can('建立測驗')) {
            $exams = Exam::orderBy('created_at', 'desc')
                ->paginate(3);
        } else {
            $exams = Exam::where('enable', 1)
                ->orderBy('created_at', 'desc')
                ->paginate(2);
        }
    
        return view('exam.index', compact('exams'));
    }
    1. 主要是先取得使用者資料,此時,上方需告知要使用Illuminate\Support\Facades\Auth才行

      use Illuminate\Support\Facades\Auth;
    2. 接著判斷有無$user物件(沒登入者不會有),若有,判斷有無權限。

    3. 有權限者就不加上where('enable', 1)這個條件(顯示數也可以調多一點)

  3. 接著修改\專案\resources\views\exam\index.blade.php,加入判斷,若$exam->enable不等於1,就顯示一個關閉的徽章

    @extends('layouts.app')
    @section('content')
        <h1>測驗一覽<small>(共 {{$exams->total()}} 筆資料)</small></h1>
        <ul class="list-group">
            @forelse  ($exams as $exam)
                <li class="list-group-item">
                @if($exam->enable!=1)
                    {{ bs()->badge()->text('關閉') }}
                @endif
                    {{$exam->created_at->format("Y年m月d日") }} -
                    <a href="exam/{{$exam->id}}">
                    {{$exam->title}}
                    </a>
                </li>
            @empty
                <li class="list-group-item">尚無任何測驗</li>
            @endforelse
        </ul>
        <div class="my-3">
            {{ $exams->links() }}
        </div>
    @endsection
  4. 看起來就像這樣:

 

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

14-1 更新測驗

  1. 修改路由 /專案/routes/web.php 加入 update的對應

    Route::patch('/exam/{exam}', 'ExamController@update')->name('exam.update');
  2. 更新的方法用 patch,路徑直接給 {exam} (此時是編號)即可。

  3. 接著在控制器中 /專案/app/Http/Controllers/ExamController.php,加入用Model的update方法(一樣要fillable有設定才能用):

    public function update(ExamRequest $request, Exam $exam)
    {
        $exam->update($request->all());
        return redirect()->route('exam.show', $exam->id);
    }
  4. 搞定!

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

14-2 修改題目

一、修改路由

  1. 修改路由 /專案/routes/web.php 加入topic 的 edit 路由,一樣使用路由模型綁定,{topic}就是編號,放在前後其實都沒關係。
    Route::pattern('exam', '[0-9]+');
    Route::pattern('topic', '[0-9]+');
    //略
    Route::get('/topic/{topic}/edit', 'TopicController@edit')->name('topic.edit');

二、修改樣板,加入編輯按鈕

  1. 修改原來的 /專案/resources/views/exam/show.blade.php 樣板,加入編輯按鈕:

    <h3>
    @can('建立測驗')
        <a href="{{route('topic.edit', $topic->id)}}" class="btn btn-warning">編輯</a>
        ({{$topic->ans}})
    @endcan
    {{ bs()->badge()->text($key+1) }}
    {{ $topic->topic }}
    </h3>
  2. 如:

三、修改Topic控制器

  1. 要修改題目,會遇到和修改測驗一樣的問題(而且更複雜一點),因為是共用樣板,所以,必須針對「建立」和「修改」兩種情形來修改部份視圖的設定

    • 建立的http動詞是post,修改則是patch(如此,會自動加上CSRF保護,避免跨站攻擊)

    • 建立題目的表單action/topic,修改題目則是/topic/編號

  2. 在控制器中 /專案/app/Http/Controllers/TopicController.php,加入 edit 的方法:

    public function edit(Topic $topic)
    {
        $exam   = $topic->exam;
        return view('exam.show', compact('exam', 'topic'));
    }
    • 主要就是讀出題目$topic 的原始內容,以便套用到表單中。我們將Topic注入$topic$topic就成了一個Topic內容物件

    • 此外,測驗內容$exam也會被用到,用來顯示測驗標題,而且因為我們已經有設好關聯,所以,不用另外抓,$topic->exam就是所屬測驗的完整資料。

  3. 最後,修改 /專案/resources/views/exam/show.blade.php 樣板:

    @can('建立測驗')
        @if(isset($topic))
            {{ bs()->openForm('patch', "/topic/{$topic->id}", ['model' => $topic]) }}
        @else
            {{ bs()->openForm('post', '/topic') }}
        @endif
            {{ bs()->formGroup()
                    ->label('題目內容', false, 'text-sm-right')
                    ->control(bs()->textarea('topic')->placeholder('請輸入題目內容'))
                    ->showAsRow() }}
            {{ bs()->formGroup()
                    ->label('選項1', false, 'text-sm-right')
                    ->control(bs()->text('opt1')->placeholder('輸入選項1'))
                    ->showAsRow() }}
            {{ bs()->formGroup()
                    ->label('選項2', false, 'text-sm-right')
                    ->control(bs()->text('opt2')->placeholder('輸入選項2'))
                    ->showAsRow() }}
            {{ bs()->formGroup()
                    ->label('選項3', false, 'text-sm-right')
                    ->control(bs()->text('opt3')->placeholder('輸入選項3'))
                    ->showAsRow() }}
            {{ bs()->formGroup()
                    ->label('選項4', false, 'text-sm-right')
                    ->control(bs()->text('opt4')->placeholder('輸入選項4'))
                    ->showAsRow() }}
            {{ bs()->formGroup()
                    ->label('正確解答', false, 'text-sm-right')
                    ->control(bs()->select('ans',[1=>1, 2=>2, 3=>3, 4=>4])->placeholder('請設定正確解答'))
                    ->showAsRow() }}
            {{ bs()->hidden('exam_id', $exam->id) }}
            {{ bs()->formGroup()
                    ->label('')
                    ->control(bs()->submit('儲存'))
                    ->showAsRow() }}
        {{ bs()->closeForm() }}
    @endcan
  4. 到此就大功告成啦!
  5. 最後,只要完成「更新」部份的功能即可。

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

14-3 更新題目

  1. 修改路由 /專案/routes/web.php 加入topicupdate 的路由

    Route::patch('/topic/{topic}', 'TopicController@update')->name('topic.update');
  2. 更新的方法用 patch,路徑直接給 {topic} 編號即可。

  3. 接著在控制器中 /專案/app/Http/Controllers/TopicController.php,加入用Model的update方法(一樣要fillable有設定才能用):

    public function update(Request $request, Topic $topic)
    {
        $topic->update($request->all());
        return redirect()->route('exam.show', $topic->exam_id);
    }
  4. 搞定!
     

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

15. 刪除題目

  1. 當 route 刪除使用 delete方法時,無法直接用連結的方式來做,因為連結的方法屬於get。所以我們自行產生表單,並送出_method值為DELETE的參數,可以利用 Laravel 5.6 新的@method('delete')來達成,以及用@csrf(取代原先的csrf_field())產生令牌,以加入CSRF保護,避免跨站攻擊。詳情:https://d.laravel-china.org/docs/5.6/csrf

  2. 修改  /專案/resources/views/show.blade.php 樣板,加入刪除按鈕:

    @can('建立測驗')
        <form action="{{route('topic.destroy', $topic->id)}}"  method="post" style="display:inline">
            @csrf
            @method('delete')
            <button type="submit" class="btn btn-danger">刪除</button>
        </form>
        <a href="{{route('topic.edit', $topic->id)}}" class="btn btn-warning">編輯</a>
        ({{$topic->ans}})
    @endcan
  3. 其中style="display:inline"只是為了讓刪除按鈕可以和其他按鈕放在一起。
  4. 此外,要注意的是,form中的 method="post"也不可拿掉
  5. 編輯路由 /專案/routes/web.php 加入 topic 的刪除路由

    Route::delete('/topic/{topic}', 'TopicController@destroy')->name('topic.destroy');
  6. 接著在控制器中 /專案/app/Http/Controllers/TopicController.php,加入 delete 的方法:
    public function destroy(Topic $topic)
    {
        $topic->delete();
        return redirect()->route('exam.show', $topic->exam_id);
    }
  7. 當我們沒有使用路由模型綁定時,也可以用靜態方法的destroy()也可以,但此例我們需要抓出exam_id的值,故用destroy()也沒比較省事,因此底下參考一下即可。
    public function destroy($id)
    {
        Topic::destroy($id);
        return redirect()->route('exam.show', $id);
    }
  8. destroy()用法(詳細請參考:)https://laravel-china.org/docs/laravel/5.6/eloquent/1403#bb7a2e
    //刪除一筆
    Topic::destroy(1);
    //刪除多筆
    Topic::destroy([1,3,5,7]);
    Topic::destroy(1,3,5,7);

     

到GitHub觀看此單元程式異動  

Laravel 5.6 入門講義

15-1 刪除測驗

  1. 編輯路由 /專案/routes/web.php 加入 exam 的刪除路由

    Route::delete('/exam/{exam}', 'ExamController@destroy')->name('exam.destroy');
  2. 修改  /專案/resources/views/show.blade.php 樣板,加入測驗的刪除按鈕:

    <form action="{{route('exam.destroy', $exam->id)}}" method="POST" style="display:inline">
        @csrf
        @method('delete')
        <button type="submit"  class="btn btn-danger">刪除</button>
    </form>
  3. 如:
  4. 接著在控制器中 /專案/app/Http/Controllers/ExamController.php,加入 delete 的方法,我們一樣用delete()刪除即可,也不需要在抓取任何參數,所以,直接刪完回首頁即可。
    public function destroy(Exam $exam)
    {
        $exam->delete();
        return redirect()->route('exam.index');
    }
  5. 由於我們在migrations中有設定$table->foreign('exam_id')->references('id')->on('exams')->onDelete('cascade');,所以在刪除測驗,也會順便把相關聯的題目都一併刪除。

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

15-2 加入刪除確認

一、載入SweetAlert2

  1. 直接刪除實在太可怕,一個不小心就會把所有資料刪掉,因此,在刪除之前加個確認動作會比較好一點
  2. 開啟\專案\resources\views\layouts\app.blade.php主視圖,引入js以及子視圖,並新增一個 @yield 命令,已方便各個子視圖放置一些各自的 javascript 語法:
    <!DOCTYPE html>
    <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
    
        <!-- CSRF Token -->
        <meta name="csrf-token" content="{{ csrf_token() }}">
    
        <title>{{ config('app.name', 'Laravel') }}</title>
    
        <!-- Scripts -->
        <script src="{{ asset('js/app.js') }}" defer></script>
        <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
        <script src="https://unpkg.com/sweetalert2@7.18.0/dist/sweetalert2.all.js"></script>
    
    
        <!-- 中間略 -->
    
        @yield('js')
    </body>
    
    </html>

二、題目的確認後刪除 

  1. 開啟\專案\resources\views\layouts\show.blade.php子視圖,先把刪除題目的按鈕簡化為一般的按鈕,在class中,我們加入btn-del-topic好讓jquery可以偵測刪除題目的按鈕是否有被按下,並利用data-id來紀錄題目編號,等一下刪除時會用到。
    <h3>            
    @can('建立測驗')
        <button type="button" class="btn btn-danger btn-del-topic" data-id="{{ $topic->id }}">刪除</button>
        <a href="{{route('topic.edit', $topic->id)}}" class="btn btn-warning">編輯</a>
        ({{$topic->ans}})
    @endcan
    {{ bs()->badge()->text($key+1) }}
    {{ $topic->topic }}
    </h3>
  2. 接著在同一個檔案下方,利用@section('js')加入 sweetalert2 語法,語法可參考https://sweetalert2.github.io/
    @section('js')
        <script>
            $(document).ready(function(){
                $('.btn-del-topic').click(function(){
                    var topic_id=$(this).data('id');                
                    swal({
                        title: "確定要刪除題目嗎?",
                        text: "刪除後該題目就消失救不回來囉!",
                        type: 'warning',
                        showCancelButton: true,
                        confirmButtonColor: "#DD6B55",
                        confirmButtonText: "是!含淚刪除!",
                        cancelButtonText: "不...別刪",
                    }).then((result) => {
                        if (result.value) {                        
                            axios.delete('/topic/' + topic_id)
                            .then(function(){
                                return swal("OK!刪掉題目惹!", "該題目已經隨風而逝了...", "success");
                            }).then(function () {
                                location.reload();
                            });
                        }
                    })
                });
            });
        </script>
    @endsection
    • 利用 $('.btn-del-topic').click()來偵測該刪除按鈕被按下的事件。
    • 利用jquery的data()函數取的data-id的值,這樣才知道要刪除哪一篇
    • 若使用者有按確定刪除,我們就利用axios以ajax的方式來執行HTTP的刪除動作,關於axios的使用可參考:https://www.kancloud.cn/yunye/axios/234845
    • 刪除後,會執行location.reload();以更新畫面
  3. 由於刪除已經改成用ajax的方式來執行,因此,我們修改題目的控制器\專案\app\Http\Controllers\TopicController.php,把刪除的部份再簡化(無須轉向),到此,題目的確認後刪除就大公告成了!
    public function destroy(Topic $topic)
    {
        $topic->delete();
    }

三、測驗的確認後刪除 

  1. 一樣開啟\專案\resources\views\layouts\show.blade.php子視圖,先把測驗的刪除按鈕簡化為一般的按鈕,在class中,我們加入btn-del-exam好讓jquery可以偵測測驗的刪除按鈕是否有被按下,並利用data-id來紀錄測驗編號。
    <h1 class="text-center">
        {{$exam->title}}
        @can('建立測驗')            
            <button type="button" class="btn btn-danger btn-del-exam" data-id="{{ $exam->id }}">刪除</button>
            <a href="{{route('exam.edit', $exam->id)}}" class="btn btn-warning">編輯</a>
        @endcan
    </h1>
  2. 接著在@section('js')裡面再加上測驗刪除的事件,複製上方得來修改即可。
    // 刪除按鈕點擊事件
    $('.btn-del-exam').click(function() {
        // 獲取按鈕上 data-id 屬性的值,也就是編號
        var id = $(this).data('id');
        // 調用 sweetalert
        swal({
            title: "確定要刪除測驗嗎?",
            text: "刪除後該測驗連同所有題目就消失救不回來囉!",
            type: 'warning',
            showCancelButton: true,
            confirmButtonColor: "#DD6B55",
            confirmButtonText: "是!含淚刪除!",
            cancelButtonText: "不...別刪",
        }).then((result) => {
            if (result.value) {
                swal("OK!刪掉測驗惹!", "該測驗所有資料已經隨風而逝了...", "success");
                // 調用刪除介面,用 id 來拼接出請求的 url
                axios.delete('/exam/' + id).then(function () {
                    location.href='/exam';
                });
            }
        });
    });
    • 利用 $('.btn-del-exam').click()來偵測測驗刪除按鈕被按下的事件。
    • 利用jquery的data()函數取的data-id的值,這樣才知道要刪除哪一篇測驗
    • 刪除後,會執行location.href做轉向。
  3. 接著修改題目的控制器\專案\app\Http\Controllers\ExamController.php,一樣把刪除的部份再簡化即可!
    public function destroy(Exam $exam)
    {
        $exam->delete();
    }

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

16. 建立並執行考試的Model 及 migrate 文件

一、編輯考試的 migrate 檔案

  1. 學生進行考試時,也要有一個表來紀錄學生填答及分數。

  2. 建立Test(測驗)的 Eloquent 模型,以便將一個資料表變成一個物件來操作,並且順便產生 migration 檔案

    php artisan make:model Test --migration
  3. 編輯/專案/database/migrations/日期_create_tests_table.php

    <?php
    
    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Support\Facades\Schema;
    
    class CreateTestsTable extends Migration
    {
        /**
         * Run the migrations.
         *
         * @return void
         */
        public function up()
        {
            Schema::create('tests', function (Blueprint $table) {
                $table->increments('id');
                $table->text('content');
                $table->unsignedInteger('exam_id');
                $table->foreign('exam_id')->references('id')->on('exams');
                $table->unsignedInteger('user_id');
                $table->foreign('user_id')->references('id')->on('users');
                $table->unsignedTinyInteger('score');
                $table->timestamps();
            });
        }
    
        /**
         * Reverse the migrations.
         *
         * @return void
         */
        public function down()
        {
            Schema::dropIfExists('tests');
        }
    }
    
  4. content:紀錄此測驗的題目編號及答案
  5. exam_id:對應的測驗編號
  6. user_id:學生編號
  7. score:得分
  8. 最後執行資料庫同步即可建出新的資料表

    php artisan migrate
  9. 資料表:

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

16-1 設定考試與測驗、考生的關聯

一、設定Test的模型及關聯

  1. 首先進入某個考試時,要能夠判斷學生身份,並且讓原本的題目列表隨機列出10筆,並可送出儲存。
  2. 在這之前,我們先來設定一下Test的模型及關聯
  3. 從考試(test)的角度來看,每個考試都有一個所屬(belongsTo)的測驗(exam),所以,開啟題目的模型/專案/app/Test.php,加入對測驗的 belongsTo 關聯:
    class Test extends Model
    {
        protected $fillable = [
            'content', 'user_id', 'exam_id', 'score',
        ];
    
        public function exam()
        {
            return $this->belongsTo('App\Exam');
        }
    
        public function user()
        {
            return $this->belongsTo('App\User');
        }
    }
  4. 此外,一個考試,也一定會有一個考生,所以,我們也順便加入使用者的關聯。
  5. 設好後,日後只要取得$test資料,就可以順便帶出$test->exam測驗資料,以及$test->user使用者資料
  6. 另外,批次賦值$fillable也順便一下,這些通常都是必做的。

二、設定測驗對考試的關聯

  1. 從測驗(exam)的角度來看,一個測驗可能會有多(hasMany)考試(test),所以,開啟使用者的模型 /專案/app/Exam.php,加入對題目的 hasMany 關聯:
    class Exam extends Model
    {
        protected $fillable = [
            'title', 'user_id', 'enable',
        ];
        protected $casts = [
            'enable' => 'boolean',
        ];
    
        public function topics()
        {
            return $this->hasMany('App\Topic');
        }
    
        public function tests()
        {
            return $this->hasMany('App\Test');
        }
    }
  2. 設定好關聯後,在取得 $exam 內容時,就會自動加入 $exam->test 資料陣列,可以輕鬆的取得測試的相關資料。如果有老師想看這個測驗底下有多少考試,就可以很方便的讀出。
  3. 完整關聯請參考:https://laravel-china.org/docs/laravel/5.6/eloquent-relationships/1404

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

16-2 產生考試界面

一、在控制器依據不同權限取得不同內容

  1. 首先,先修改 /專案/app/Http/Controllers/ExamController.php 控制器,修改原本的show()
    public function show(Exam $exam)
    {
        $user = Auth::user();
        if ($user and $user->can('進行測驗')) {
            $exam->topics = $exam->topics->random(10);
        }
        return view('exam.show', compact('exam'));
        
    }
    • 有「進行測驗」權限者:
      • 題目的部份,測驗本來就有設定一對多,所以,$exam->topics會是一個集合($exam此時是Exam的資料物件),我們利用random(10)來從題目集合中,隨機取10筆來呈現。

二、先簡化單一測驗的視圖

  1. 修改/專案/resources/views/exam/show.blade.php 樣板,一樣依據權限來呈現不同畫面,不過因為程式碼越來越長,所以,我們可以把一些表單獨立成另外的視圖檔案:
    • 將編輯測驗的表單獨立成\專案\resources\views\exam\form.blade.php
      @if(isset($topic))
          {{ bs()->openForm('patch', "/topic/{$topic->id}", ['model' => $topic]) }}
      @else
          {{ bs()->openForm('post', '/topic') }}
      @endif
          {{ bs()->formGroup()
                  ->label('題目內容', false, 'text-sm-right')
                  ->control(bs()->textarea('topic')->placeholder('請輸入題目內容'))
                  ->showAsRow() }}
          {{ bs()->formGroup()
                  ->label('選項1', false, 'text-sm-right')
                  ->control(bs()->text('opt1')->placeholder('輸入選項1'))
                  ->showAsRow() }}
          {{ bs()->formGroup()
                  ->label('選項2', false, 'text-sm-right')
                  ->control(bs()->text('opt2')->placeholder('輸入選項2'))
                  ->showAsRow() }}
          {{ bs()->formGroup()
                  ->label('選項3', false, 'text-sm-right')
                  ->control(bs()->text('opt3')->placeholder('輸入選項3'))
                  ->showAsRow() }}
          {{ bs()->formGroup()
                  ->label('選項4', false, 'text-sm-right')
                  ->control(bs()->text('opt4')->placeholder('輸入選項4'))
                  ->showAsRow() }}
          {{ bs()->formGroup()
                  ->label('正確解答', false, 'text-sm-right')
                  ->control(bs()->select('ans',[1=>1, 2=>2, 3=>3, 4=>4])->placeholder('請設定正確解答'))
                  ->showAsRow() }}
          {{ bs()->hidden('exam_id', $exam->id) }}
          {{ bs()->formGroup()
                  ->label('')
                  ->control(bs()->submit('儲存'))
                  ->showAsRow() }}
      {{ bs()->closeForm() }}
    • 然後修改/專案/resources/views/exam/show.blade.php 樣板,利用@include()引入該檔案,位置放在exam\form.blade.php,引入時需寫成exam.form
      @can('建立測驗')
          @include('exam.form')
      @endcan
    • 同樣的,我們把題目的呈現也獨立成一個視圖檔案\專案\resources\views\exam\topic.blade.php

      <dl>
          @forelse ($exam->topics as $key => $topic)
              <dt>
                  <h3>
                  @can('建立測驗')
                      <button type="button" class="btn btn-danger btn-del-topic" data-id="{{ $topic->id }}">刪除</button>
                      <a href="{{route('topic.edit', $topic->id)}}" class="btn btn-warning">編輯</a>
                      ({{$topic->ans}})
                  @endcan
                  {{ bs()->badge()->text($key+1) }}
                  {{$topic->topic}}
                  </h3>
              </dt>
              <dd>
                  {{ bs()->radioGroup("ans[$topic->id]", [
                          1=>"<span class='opt'>&#10102; $topic->opt1</span>",
                          2=>"<span class='opt'>&#10103; $topic->opt2</span>",
                          3=>"<span class='opt'>&#10104; $topic->opt3</span>",
                          4=>"<span class='opt'>&#10105; $topic->opt4</span>"
                      ])->addRadioClass(['mx-3']) }}
              </dd>
          @empty
              <div class="alert alert-danger">尚無任何題目</div>
          @endforelse
      </dl>
    • 再修改/專案/resources/views/exam/show.blade.php 樣板,一樣利用@include()引入該檔案,位置放在exam\topic.blade.php,引入時需寫成exam.topic

      @include('exam.topic')

三、在視圖中依據不同權限呈現不同內容

  1. 在顯示題目的部份,如果沒有任何權限者,理論上不應該看到任何題目,所以,我們將這部份改成這樣:
    @if(Auth::check('建立測驗') || Auth::check('進行測驗'))
        @can('進行測驗')
            {{ bs()->openForm('post', '/test') }}
                @include('exam.topic')
                {{ bs()->hidden('user_id', Auth::id()) }}
                {{ bs()->hidden('exam_id', $exam->id) }}
                <div class="text-center my-5">
                    {{ bs()->submit('寫完送出') }}
                </div>
            {{ bs()->closeForm() }}
        @else
            @include('exam.topic')
        @endcan
    @else
        @component('bs::alert', ['type' => 'info'])
            共 {{ $exam->topics->count() }} 題
        @endcomponent
    @endif
    • 由於我們要判斷兩個以上的權限,所以,@can無法使用,故改用Auth::check('建立測驗')搭配@if就可以達成。
    • 若是有「進行測驗」權限者,我們就替題目加上表單,讓該表單可以送出。表單送至/test即可,讓考試(test)的模型來儲存資料即可。
    • 記得將測驗編號及受測者(登入者)編號放到隱藏欄位中。 使用者編號實際上也可以直接用Auth::id()來抓取即可。
    • 至於訪客看到的訊息,我們暫時簡單的列出題目數量即可。$exam->topics本身是一個集合,要算數量可以用count()
    • alert視窗可以用class="alert"來做,也可以用@component的方式來做。如果只是簡單的訊息,其實用單純HTML語法來做更簡單。
  2. 如:

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

16-3 儲存並計分

  1. 修改路由\專案\routes\web.php,儲存部份用test的控制器TestController來處理:
    Route::post('/test', 'TestController@store')->name('test.store');
  2. 先建立考試Test的控制器
    php artisan make:controller TestController --resource
  3. 接著編輯控制器 \專案\app\Http\Controllers\TestController.php,修改 store 方法,儲存後轉向到考試結果頁面。
    public function store(Request $request)
    {
        $content = collect($request->ans)->toJson();
        $score = 0;
        foreach ($request->ans as $topic_id => $ans) {
            $topic = Topic::find($topic_id);
            $score += ($topic->ans == $ans) ? 20 : 0;
        }
    
        $test = Test::create([
            'content' => $content,
            'user_id' => $request->user_id,
            'exam_id' => $request->exam_id,
            'score' => $score,
        ]);
        return redirect()->route('test.show', $test->id);
    }
    • $request 就是使用者輸入的內容,以物件方式存在。詳情可參考:https://laravel-china.org/docs/laravel/5.6/requests/1297#7ecd03
    • 其中$content將會以json格式來儲存題號以及使用者填寫的答案,而$request->ans是所有填答的陣列,可以用json_encode($request->ans)這個PHP內建函數來將之轉成json格式,也可以利用Laravel的collect()來將陣列轉為集合,以便用集合的toJson方法。
    • 關於集合可用的所有方法可以參考:https://laravel-china.org/docs/laravel/5.6/collections/1388
    • 這裡一樣用fillable方式來寫入資料,所以記得查看\專案\app\Test.php有沒有進行fillable設定
    • redirect() 用來轉向到考試結果頁面
  4. 在上方告知要加上 \App\TestApp\Topic模型
    use App\Test;
    use App\Topic;
  5. 路由先定義個test.show的路由,避免錯誤。
    Route::pattern('exam', '[0-9]+');
    Route::pattern('topic', '[0-9]+');
    Route::pattern('test', '[0-9]+');
    //略
    Route::get('/test/{test}', 'TestController@show')->name('test.show');
    
  6. 接著可以試試看是否能儲存測驗結果囉!(請暫時先到資料庫去看結果)

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

16-4 產生考試結果

一、修改控制器

  1. 考試結果理論上應該要看起來和考試當下的畫面差不多,唯一的差別是多了分數,並且顯示每題的作答及正確答案。
  2. 路由的test.show是用控制器TestController@show來產生,故開啟/專案/app/Http/Controllers/TestController.php,修改 show 的方法
        public function show(Test $test)
        {
            $topics  = json_decode($test->content, true);
            $content = [];
            $i       = 1;
            foreach ($topics as $topic_id => $ans) {
                $content[$i]['topic'] = Topic::find($topic_id);
                $content[$i]['ans']   = $ans;
                $i++;
    
            }
            return view('exam.test', compact('test', 'content'));
        }
  3. 首先,此處一樣用路由模型綁定,$test即為Test的資料物件,由於有設定關聯,所以,會一併抓出$test->exam$test->user的資料。

  4. 由於我們用的是比較簡易的作法,把考試者的填答做成json格式存入content中,故讀出時,可以利用json_decode($id->content, true)將json資料轉回陣列。

  5. 接著把陣列一個一個讀出,其陣列索引為題目編號$topic_id,值為使用者填寫的答案,因此每跑一圈,我們就可以利用Topic::find($topic_id)來抓取該題目的資訊,並將題目資訊存到$content陣列中。

  6. $content的索引一樣從0開始,以方便等一下製作題號。

  7. 此外,我們也順便把使用者的作答一併存入$content陣列中,以便待會比對是否作答正確。

二、修改樣板

  1. 由於控制器show的最後會呼叫exam.test樣板,因此,我們建立一個/專案/resources/views/exam/test.blade.php樣板:
    @extends('layouts.app')
    @section('content')
        <h1 class="text-center">{{$test->exam->title}}</h1>
        <h3 class="row">
            <div class="col-sm-6">時間:<u>{{$test->created_at->format("Y年m月d日 H:i:s")}}</u></div>
            <div class="col-sm-6 text-right">姓名:<u>{{$test->user->name}}</u> 得分:<u>{{$test->score}}</u></div>   
        </h3>
        <hr>
    
        <dl>
            @forelse ($content as $key => $data)
                <dt>
                    <h3>            
                        @if($data['ans']==$data['topic']->ans)
                            <img src="{{asset('yes.png')}}" alt="yes" title="正確答案為 {{$data['topic']->ans}}">
                        @else
                            <img src="{{asset('no.png')}}" alt="no" title="正確答案為 {{$data['topic']->ans}}">
                        @endif
                        {{ bs()->badge()->text($key+1) }}
                        ({{$data['ans']}})
                        {{$data['topic']->topic}}
                    </h3>
                </dt>
                
                <dd>
                    <div class="ml-5 my-2 opt">
                        &#10102; {{$data['topic']->opt1}}
                    </div>
                    <div class="ml-5 my-2 opt">
                        &#10103; {{$data['topic']->opt2}}
                    </div>
                    <div class="ml-5 my-2 opt">
                        &#10104; {{$data['topic']->opt3}}
                    </div>
                    <div class="ml-5 my-2 opt">
                        &#10105; {{$data['topic']->opt4}}
                    </div>
                </dd>
            @empty
                <div class="alert alert-danger">尚無任何題目</div>
            @endforelse
        </dl>
    
    @endsection
    
  2. 其中會用到以下兩個圖檔,故請下載之,並存到public底下:

  3. 要使用public下的資源,可以直接用asset('檔案名稱')來取用之即可。

 

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

17. 安裝設定新北市OpenID登入

一、安裝套件

  1. 此為林士立老師的作品:https://github.com/t301000/laravel-ntpc-openid,本文件內容均取自於 https://github.com/t301000/laravel-ntpc-openid/wiki
  2. 先安裝套件:
    composer require t301000/laravel-ntpc-openid
  3.  設定 service provider  在 config/app.php 中加入 service provider
    'providers' => [
        ...
        T301000\LaravelNtpcOpenid\NtpcOpenidServiceProvider::class,
    ];
  4.  發布設定檔,設定檔會發布在 config/ntpcopenid.php,預設會取回所有資料欄位,請自行依需求修改
    php artisan vendor:publish --provider="T301000\LaravelNtpcOpenid\NtpcOpenidServiceProvider" --tag=config

二、根據需求設定

  1. 設定檔位於 config/ntpcopenid.php,只要將 不要 的資料刪掉或註解起來即可
    return [
        ....略
        'required' => [
            'namePerson/friendly',      //暱稱
            'contact/email',            //公務信箱
            'namePerson',               //姓名
            'birthDate',                //出生年月日
            'person/gender',            //性別
            'contact/postalCode/home',  //識別碼
            'contact/country/home',     //單位(學校名),如:xx國中
            'pref/language',            //年級班級座號 6 碼
            'pref/timezone'             // 授權資訊[學校別、身分別、職稱別、職務別]
        ],
        ....略
    ];
  2. 設定 canLoginRules 可設定允許登入之規則,規則設定範例:
    ['unitCode' => '014569'],
    ['unitCode' => '014569', 'role' => '教師'],
    ['unitCode' => '014569', 'role' => ['教師', '學生']],
    ['role' => '教師'],
    ['unitCode' => '014569', 'title' => ['主任', '組長']],
    ['group' => '資訊組長'],
    ['openID' => ['somebody']],
     
    • 每條規則均為陣列
    • 未設定規則代表不設限 
    • 可用欄位 => unitCode 單位代碼, role 身份, title 職務, group 職稱, openID OpedID 帳號
    • unitCode 為字串之外,其餘可為字串或陣列

Laravel 5.6 入門講義

17-1 使用新北市OpenID登入

  1. 先開啟\專案\resources\views\layouts\nav.blade.php,加入登入選項:
    <li class="nav-item">
        <form action="/auth/login/openid" method="post" style="display:inline">
            @csrf
            <button class="btn btn-link">OpenID 登入</button>
        </form>
    </li>
  2. 建立控制器:
    php artisan make:controller OpenIDController
  3. 編輯OpenID控制器\專案\app\Http\Controllers\OpenIDController.php
    <?php
    
    namespace App\Http\Controllers;
    
    use Backpack\Base\app\Models\BackpackUser;
    use Illuminate\Support\Facades\Auth;
    
    class OpenIDController extends Controller
    {
        public function ntpcopenid()
        {
            $openid = app('ntpcopenid');
            return redirect($openid->authUrl());
    
        }
    
        public function get_ntpcopenid()
        {
    
            $openid = app('ntpcopenid');
    
            switch ($openid->mode) {
                case 'cancel': // 取消授權
                    return 'User Canceled';
                    break;
    
                case 'id_res': // 同意授權
                    if (!$openid->validate()) {
                        // 驗證未過
                        // 導向至登入畫面
                        return redirect('auth/login');
                    }
    
                    // 驗證通過,檢查是否允許登入
                    if ($openid->canLogin()) {
                        // 允許登入
                        // 取得 user data 陣列
                        $data = $openid->getUserData('*');
    
                        //搜尋使用者是否存在,沒有就新增
                        $user           = BackpackUser::firstOrNew(['name' => $data['namePerson']]);
                        $user->email    = $data['contact/email'];
                        $user->password = password_hash($user->email, PASSWORD_DEFAULT);
    
                        //  註冊使⽤用者
                        if (!$user->exists) {
                            $user->save();
                            if ($data['pref/timezone'][0]['role']) {
                                $user->assignRole('學生');
                            }
                        }
    
                        //  登⼊入使⽤用者
                        Auth::login($user);
                        // 將取得的資料,轉成陣列存入session中
                        session($data);
                    }
                    // 不允許登入,例如導回登入頁面或顯示訊息
                    return redirect('/');
                    break;
    
                default: // 其他,如直接輸入網址瀏覽
                    return redirect('/');
                    break;
            }
    
        }
    }
    
  4. 編輯路由\專案\routes\web.php
    // 處理表單,導向至 NTPC OpenID 登入
    Route::post('auth/login/openid', 'OpenIDController@ntpcopenid')->name('ntpcopenid');
    
    // OpenID 導回
    Route::get('auth/login/openid', 'OpenIDController@get_ntpcopenid')->name('get_ntpcopenid');
  5.  

Laravel 5.6 入門講義

17-2 異動資料表

  1. 建立異動資料表用的migration
    php artisan make:migration add_tests_cols --table=tests
  2.  編輯\專案\database\migrations\2018_08_10_001435_add_tests_cols.php
    public function up()
    {
        Schema::table('tests', function (Blueprint $table) {
            $table->unsignedTinyInteger('grade');
            $table->unsignedTinyInteger('class');
            $table->unsignedTinyInteger('num');
        });
    }

    以及

    public function down()
    {
        Schema::table('tests', function (Blueprint $table) {
            $table->dropColumn(['grade', 'class', 'num']);
        });
    }

Laravel 5.6 入門講義

17-3 修改考試的儲存動作

  1. 修改控制器\專案\app\Http\Controllers\TestController.php
    public function store(Request $request)
    {
        $content = collect($request->ans)->toJson();
        $score   = 0;
        foreach ($request->ans as $topic_id => $ans) {
            $topic = Topic::find($topic_id);
            $score += ($topic->ans == $ans) ? 20 : 0;
        }
    
        $class_info = session('pref/language');
        $test       = Test::create([
            'content' => $content,
            'user_id' => $request->user_id,
            'exam_id' => $request->exam_id,
            'score'   => $score,
            'grade'   => substr($class_info, 0, 2),
            'class'   => substr($class_info, 2, 2),
            'num'     => substr($class_info, 4, 2),
        ]);
        return redirect()->route('test.show', $test->id);
    }

     

  2. 修改考試的模型\專案\app\Test.php,主要是修改$fillable新增加入的欄位
    <?php
    
    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Test extends Model
    {
        protected $fillable = [
            'content', 'user_id', 'exam_id', 'score','grade','class','num',
        ];
    
        public function exam()
        {
            return $this->belongsTo('App\Exam');
        }
    
        public function user()
        {
            return $this->belongsTo('App\User');
        }
    }
    

     

Laravel 5.6 入門講義

18. 網站的關閉與啟動

  1. 如果網站要做維護或備份轉移,又怕中途有人亂動資料,那麼,可以先將網站關閉起來。
    php artisan down
  2. 此時,系統會傳送503訊息,網站會顯示503頁面,您也可以自行修改\專案\resources\views\errors\503.blade.php頁面內容,使之和網站風格有一致性。

    @extends('layouts.app')
    @section('content')
      @component('bs::jumbotron', ['fluid' => true])
          @slot('heading')
              503 網站目前進廠維護中
          @endslot
          @slot('subheading')
              沒什麼大事啦~例行維護嘛...您懂的...
          @endslot
    
          <hr class="my-3">
          <p>不就是改改程式,抓抓臭蟲...應該不用兩三天就好了啦!</p>
      @endcomponent
    @endsection
    
  3. 看起來像這樣:

  4.  若要啟動網站,則以下指令即可:
    php artisan up

    到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

18-1 使用路由前置字串

一、路由前置字串

  1. 如果路由有一樣路徑的,可以利用前置字串Route::prefix()將之群組起來,例如:
    Route::get('/exam/', 'ExamController@index')->name('exam.index');
    Route::get('/exam/create', 'ExamController@create')->name('exam.create');
    
  2. 由於都有個exam,因此,可利用prefix('exam')設定成:
    Route::prefix('exam')->group(function () {
        Route::get('/', 'ExamController@index')->name('exam.index');
        Route::get('/create', 'ExamController@create')->name('exam.create');
    });
  3. 因此,稍做整理,就會得到以下結果:
    Route::prefix('exam')->group(function () {
        Route::get('/', 'ExamController@index')->name('exam.index');
        Route::get('/create', 'ExamController@create')->name('exam.create');
        Route::post('', 'ExamController@store')->name('exam.store');
        Route::get('/{exam}', 'ExamController@show')->name('exam.show');
        Route::delete('/{exam}', 'ExamController@destroy')->name('exam.destroy');
        Route::get('/{exam}/edit', 'ExamController@edit')->name('exam.edit');
        Route::patch('/{exam}', 'ExamController@update')->name('exam.update');
    });
    
    Route::prefix('topic')->group(function () {
        Route::post('/', 'TopicController@store')->name('topic.store');
        Route::get('/{topic}/edit', 'TopicController@edit')->name('topic.edit');
        Route::patch('/{topic}', 'TopicController@update')->name('topic.update');
        Route::delete('/{topic}', 'TopicController@destroy')->name('topic.destroy');
    });
    
    Route::prefix('test')->group(function () {
        Route::post('/', 'TestController@store')->name('test.store');
        Route::get('/{test}', 'TestController@show')->name('test.show');
    });
    

二、路由名稱前置字串 

  1. 如果連路由名稱也想套用前置字串,那麼,只要在前置字串後面加一個.即可。
  2. 如果路由名稱也有一樣路徑的,可以利用前置字串Route::name()將之群組起來,例如:
    Route::get('/exam/', 'ExamController@index')->name('exam.index');
    Route::get('/exam/create', 'ExamController@create')->name('exam.create');
    
  3. 由於都有個exam,因此,可利用prefix('exam')設定成:
    Route::name('exam.')->group(function () {
        Route::get('/exam', 'ExamController@index')->name('index');
        Route::get('/exam/create', 'ExamController@create')->name('create');
    });

三、同時使用路由前置字串及路由名稱前置字串 

  1. 如果想要一起使用,那麼,稍微改變一下寫法即可,例如:
    Route::group([
        'prefix' => 'exam',
        'as'     => 'exam.',
    ], function () {
        Route::get('/', 'ExamController@index')->name('index');
        Route::get('/create', 'ExamController@create')->name('create');
        Route::post('', 'ExamController@store')->name('store');
        Route::get('/{exam}', 'ExamController@show')->name('show');
        Route::delete('/{exam}', 'ExamController@destroy')->name('destroy');
        Route::get('/{exam}/edit', 'ExamController@edit')->name('edit');
        Route::patch('/{exam}', 'ExamController@update')->name('update');
    });
    
    Route::group([
        'prefix' => 'topic',
        'as'     => 'topic.',
    ], function () {
        Route::post('/', 'TopicController@store')->name('store');
        Route::get('/{topic}/edit', 'TopicController@edit')->name('edit');
        Route::patch('/{topic}', 'TopicController@update')->name('update');
        Route::delete('/{topic}', 'TopicController@destroy')->name('destroy');
    });
    
    Route::group([
        'prefix' => 'test',
        'as'     => 'test.',
    ], function () {
        Route::post('/', 'TestController@store')->name('store');
        Route::get('/{test}', 'TestController@show')->name('show');
    });

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

18-2 使用資源路由

  1. 如果控制器的寫法是完全依照規範來做,其實更可以大幅簡化路由的寫法,例如:
    Route::resource('exam', 'ExamController');
    Route::resource('topic', 'TopicController');
    Route::resource('test', 'TestController');
    
  2.  這樣就完成囉!

到GitHub觀看此單元程式異動

Laravel 5.6 入門講義

19. 重建由GitHub下載的Laravel專案

一、背景知識

  1. 如果您有將專案丟到GitHub上,那麼,實際上您無須備份專案,回家後,隨時clone一份回來繼續編輯即可。
  2. 不過,放到GitHub上的並非完整檔案,所以,clone回來之後還是有好幾個步驟要做。
  3. 根據 .gitignore 檔來看,裡面有一些目錄及檔案並不會放到GitHub上,包括:
    • /vendor:用來存放composer所下載的套件。
    • /node_modules:用來存放npm所下載的Node.js 套件。
    • Homestead.yaml:Homestead vm的設定資訊。
    • Homestead.json:用來描述Homestead vm的資訊。
    • .env:放Laravel的環境資訊,包含各種帳號密碼。

二、clone並還原專案

  1. 先切換到要放專案的資料夾,然後從終端機執行:
    git clone https://github.com/您的帳號/exam56 exam56
  2. 下下來後,進入exam56目錄,然後讓composer重建相關套件,此時會重建vendor目裡的內容
    cd exam56
    composer install
  3. 接著要還原用nmp裝的套件,還原node_modules目錄
    npm install
  4. 最後要還原.env設定檔,必須先把一個範例檔複製成.env,然後利用產生器來產生APP KEY,重點還有資料庫的名稱及帳號密碼設定一定要正確。
    cp .env.example .env
    php artisan key:generate
  5. 重建資料庫(如果是在xampp下的話)
    php artisan migrate
  6. 最後只要把我們曾在vendor中修改的項目再次修改即可,例如把後台語系從zh-Hant改為zh-TW之類的。

三、重建Homestead設定

  1. 一般到這裡就可以了,若是也有使用Homestead,那麼,還得重建Homestead.yaml設定檔:
    php vendor/bin/homestead make
  2. 啟動Homestead
    vagrant up
  3. 然後登入Homestead,並重建資料庫
    vagrant ssh
    cd public_html/exam56
    php artisan migrate