Laravel 7 CRUD 教學

Laravel 7 CRUD 教學
Read Time:5 Minute, 0 Second

最近突然想自己寫幾個小服務來玩玩,看了一下目前Laravel好像很多人在用,但在網路上找了很久,大多是官方文件與翻譯文件,找不到幾篇繁中的教學,昨天試玩了一下,就自己來寫一篇簡單的入門教學吧~

開發環境網路上有很多的教學,我就預設各位已經完成開發環境的建置,已經能在執行

laravel new project_name

來建立新專案的狀態囉,喔對了,我不是專職的後端工程師,所以這篇教學如果有不正確的觀念之處,還請大家多多指教~@o@/

先來建立個專案吧~

了解一個Framework,就從CRUD開始,CRUD(Create、Read、Update、Delete),我們來做個簡單的小站台,把增刪改查的功能實做一遍

先建立一個專案,名字叫「corona」,在終端機執行以下的命令

laravel new corona

建立完之後,先到專案路徑下將 storage目錄 與 bootstrap/cache目錄權限先改為777
*這個作法不安全,但本篇的重點在CRUD的實作,先讓Laravel起來吧www

cd corona
chmod -R 777 storage/
chmod -R 777 bootstrap/cache/ 

為了美觀與方便接下來拉使用者介面,我們裝一下Bootstrap,詳細可以看這一篇

composer require laravel/ui
php artisan ui bootstrap
npm install && npm run dev

然後記得設置一下資料庫連線(.env)

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=corona
DB_USERNAME=corona
DB_PASSWORD=+g/HwxCHTECYRVhUQt1fFbsz2JjdN2NKn7mcJcAX7Nc=

完成之後來做一次 migrate,把專案預設的table寫進DB,順便測試資料庫連線是否正常

php artisan migrate

看到這個表示資料庫連線設定OK了

Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.03 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (0.01 seconds)

建立個資料模型(model)與遷移檔(migration file)吧~

Model就想像成是你拿來定義資料會長什麼樣子,以及之後你與資料庫之間做增刪改查命令的執行者

用以下的指令來建立Model吧

php artisan make:model Corona -m

會看到以下的訊息,表示Migration檔案被產生了出來,它的位置在 database/migrations 下

Model created successfully.
Created Migration: 2020_04_03_062921_create_coronas_table

Migration檔案的日期會依照你執行當下的時間自動命名,上面的訊息表示我在2020年4月3日的6點29分21秒下的指令(應該還要再加8小時,因為專案的時區預設用UTC)
另外,在Laravel裡,Model通常用單數命名,而table name則會是複數,例如Model名是Corona,資料表名是coronas

打開這個Migration檔案,會看到它宣告了兩個function,up()與down()

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateCoronasTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('coronas', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('coronas');
    }
}

up()主要是建立資料表用的,而down()是刪除資料表,但仔細看up()在建議資料表那裡只宣告了兩個欄位,id與timestamps,這裡稍微提一下,過去我們在建立資料表的時候,通常會在最後保留兩個欄位,created_at(建立時間)與updated_at(最後更新),在這裡$table->timestamps(); 就是會產生這兩個欄位,而不用開發者另外宣告

所以我們要來在up()裡宣告除了id與created_at及updated_at之外的欄位

    public function up()
    {
        Schema::create('coronas', function (Blueprint $table) {
            $table->id();
	    $table->string('country_name');
            $table->string('symptoms');
            $table->integer('cases');
            $table->timestamps();
        });
    }

這裡應該看得出來,欄位宣告的方式就是 $table->型態->(‘欄位名稱’);

宣告完之後來做一次 migrate,把Corona table寫進DB

php artisan migrate

正常完成的話會看到以下兩行

Migrating: 2020_04_03_062921_create_coronas_table
Migrated:  2020_04_03_062921_create_coronas_table (0.02 seconds)

這時到資料庫裡看,Corona資料表已經被建立好了,如果發現欄位名稱或資料型態有錯誤,想改的話,只要用 migrate:rollback 就會回到上一次migrate前的狀態

php artisan migrate:rollback

每一次的migrate:rollback都只會回到上一步,當沒有上一步可以rollback時,就會顯示以下的訊息

Nothing to rollback.

到這裡,資料表建立完成,在Laravel裡,Model預設是放在app目錄下,打開 app/Corona.php,會看到以下的內容

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Corona extends Model
{
    //
}

我們在Model裡先做一個fillable屬性(可被填寫的欄位)的宣告

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Corona extends Model
{
    protected $fillable = ['country_name', 'symptoms', 'cases'];
}

到這裡,Model基本的建立完成了

來建立路由(routes)與控制器(controller)吧~

現在我們需要來建立一個控制器(接下來都稱controller),可以用以下的命名來建立

php artisan make:controller CoronaController --resource

加上 –resource 可以自動生成預設的七個方法,如果不加 –resoure,Controller會長這樣

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SomeController extends Controller
{
    //
}

Laravel的controller都放在app/Http/Controllers目錄底下
打開CoronaController.php,就會看到預設的七個方法,簡單地來說明一下這幾個方法的用途

  1. Index(這個方法是用來顯示資源列表,像是列出所有的文章、會員等等)
  2. Create(這個方法是用來顯示新增資源的畫面,像是新增文章、用戶等等)
  3. Store(這個方法是用來處理寫入資料庫的)
  4. Show(這個方法是用來顯示)
  5. Edit(這個方法是用來顯示修改資料介面)
  6. Update(這個方法是用來更新資料庫內指定的資料)
  7. Destroy(這個方法是用來刪除資料庫內指定的資料)

我們先在終端機執行以下指令,查看目前網站可以接受的所有路由有哪些

php artisan route:list

這時應該會看到

+--------+----------+----------+------+---------+--------------+
| Domain | Method   | URI      | Name | Action  | Middleware   |
+--------+----------+----------+------+---------+--------------+
|        | GET|HEAD | /        |      | Closure | web          |
|        | GET|HEAD | api/user |      | Closure | api,auth:api |
+--------+----------+----------+------+---------+--------------+

因為現在 routes/web.php 只有宣告根目錄( / )路由,及專案自帶的API用的api/user

打開位在 routes 的 web.php,增加一個route宣告

<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

Route::resource('coronas', 'CoronaController'); //增加這行,讓request來自網址 coronas 的都交給 CoronaController 處理

這時我們再回到終端機執行以下指令,查看目前網站可以接受的所有路由

php artisan route:list

如果上面增加route有成功的話,會看到目前網站的路由會變成這樣

+--------+-----------+-----------------------+-----------------+-----------------------------------------------+--------------+
| Domain | Method    | URI                   | Name            | Action                                        | Middleware   |
+--------+-----------+-----------------------+-----------------+-----------------------------------------------+--------------+
|        | GET|HEAD  | /                     |                 | Closure                                       | web          |
|        | GET|HEAD  | api/user              |                 | Closure                                       | api,auth:api |
|        | GET|HEAD  | coronas               | coronas.index   | App\Http\Controllers\CoronaController@index   | web          |
|        | POST      | coronas               | coronas.store   | App\Http\Controllers\CoronaController@store   | web          |
|        | GET|HEAD  | coronas/create        | coronas.create  | App\Http\Controllers\CoronaController@create  | web          |
|        | GET|HEAD  | coronas/{corona}      | coronas.show    | App\Http\Controllers\CoronaController@show    | web          |
|        | PUT|PATCH | coronas/{corona}      | coronas.update  | App\Http\Controllers\CoronaController@update  | web          |
|        | DELETE    | coronas/{corona}      | coronas.destroy | App\Http\Controllers\CoronaController@destroy | web          |
|        | GET|HEAD  | coronas/{corona}/edit | coronas.edit    | App\Http\Controllers\CoronaController@edit    | web          |
+--------+-----------+-----------------------+-----------------+-----------------------------------------------+--------------+

目前為止網站已經準備好可以上表列出的URI接受request了

接下來設置視圖(views)吧~

Laravel用的Blade模版,也支援Layout,關於Layout,你可以這樣想像,每個頁面都有頁首(header)與頁尾(footer),想像一下,如果頁首或頁尾需要修改,每個頁面我們都要更新到也太累了,所以實務上都會把每個頁面都會用到的部份放在Layout裡

OK,我們先在 resources/vies 目錄下,建立一個 layout.blade.php

上面是基本的HTML結構,其中有幾個需要特別說明的地方

記得在建立專案那一part最後面我們有執行過

composer require laravel/ui
php artisan ui bootstrap
npm install && npm run dev

來安裝Bootstrap 4嗎?如果沒執行,可以執行一下,這幾個命令是讓我們能在這個專案裡使用Bootstrap 4,但安裝完了之後,仍需要在頁面中引用,所以我們在Layout裡引用CSS與JS,這樣就可以讓所有頁面都能用到

先看看Layout的<head>…</head>區塊的最尾巴

<link href="{{ asset('css/app.css') }}" rel="stylesheet" type="text/css" />

以及<body>…</body>區塊的最尾巴

<script src="{{ asset('js/app.js') }}" type="text/js"></script>

這兩段就是在引用Bootstrap 4的CSS與JS的部份

OK,再來我們再來建立三個view

  1. create.blade.php
  2. edit.blade.php
  3. index.blade.php

然後先照著將以下的code塞進去

create.blade.php

@extends('layout')

@section('content')
<style>
  .uper {
    margin-top: 40px;
  }
</style>
<div class="card uper">
  <div class="card-header">
    新增武漢肺炎案例資料
  </div>
  <div class="card-body">
    @if ($errors->any())
      <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
              <li>{{ $error }}</li>
            @endforeach
        </ul>
      </div><br />
    @endif
      <form method="post" action="{{ route('coronas.store') }}">
          <div class="form-group">
              @csrf
              <label for="country_name">國家:</label>
              <input type="text" class="form-control" name="country_name"/>
          </div>
          <div class="form-group">
              <label for="symptoms">症狀 :</label>
              <textarea rows="5" columns="5" class="form-control" name="symptoms"></textarea>
          </div>
          <div class="form-group">
              <label for="cases">案例數 :</label>
              <input type="text" class="form-control" name="cases"/>
          </div>
          <button type="submit" class="btn btn-primary">新增</button>
      </form>
  </div>
</div>
@endsection

然後我們先在CoronaController.php裡的 create() 方法中宣告 return view(‘create’)

// CoronaController.php

public function create()
{
   return view('create');
}

這時我們就可以存取看看網站的 /coronas/create ,會看到以下的畫面

驗證資料與寫進資料庫

現在,我們要讓CoronaController能夠使用Corona模型來與資料庫溝通,所以打開CoronaController.php

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Corona; //新增這一行,引用Corona模型

然後就可以在 store() 方法中寫入以下的處理

// CoronaController.php

public function store(Request $request)
{
        $validatedData = $request->validate([
            'country_name' => 'required|max:255',
            'symptoms' => 'required',
            'cases' => 'required|numeric',
        ]);
        $show = Corona::create($validatedData);
   
        return redirect('/coronas')->with('success', '武漢肺炎案例已成功儲存');
}

現在來看一下,store() 方法做了哪些事情

store()方法宣告了一個從外部傳進來的request參數,類型是用Laravel內建的 Request,透過這個request參數,可以取得表單傳進來的各欄位資料,像是country_name、symptoms、cases這些欄位的值

然後validateData變數,是用request的validate()方法來進行自定的欄位檢查,以上述為例,我們宣告了三個欄位都是必填(required),且country_name的文字長度最大為255個字元,cases只能是數字

如果表單送進來的欄位值不符合上述的條件,則 $request->validate()會回傳錯誤,也會立即顯示在畫面上,如下圖

關於Laravel 的 Validation詳細有支援哪些,可以看一下官方文件,列得很清楚~

更新待續w