壹、目錄及代辦事項

Table of Contents

待學習項目

  • -
  • -
  • -
  • -

貳、Routing

一、Routing 的用途與設定的方法

1. 段落的語意

二、Veiw - Blade / CSS / JS

1. View 的運作方式與 Blade 簡介

a. View 存放在 resource/views 的資料夾裡面,檔名後面的 .blade.php 可以省略不寫

b. 如果在 views 資料夾裡面還有分資料夾,可以使用 . 來呼叫

2. Blade 的 layout 設定方式

blade 是 Laravel 創建的 template 系統,專門用來做 layout 的,用來將 controller 和 view 分開。

a. @yield 與 @section:

用 title 指定名稱,後面塞要放的字串

用 content 指定名稱,晝面塞要放進去的 p 段落

b. @section/@show 與 @section/@endsection

@section/@show 在 Html 挖出一個空間,然後右側的 section 的 @parent 代表 'This is master sidebar' 這段文字,可以透過移動 @parent 的位置來決定文字的排序

3. Blade 的程式控制

a. @if @elseif @else @endif

@if (count($records) === 1)
    I have one record!
@elseif (count($records) > 1)
    I have multiple records!
@else
    I don't have any records!
@endif

b. @unless @endunless

除非 Auth 有 check,不然會顯示沒有登入

@unless (Auth::check()) You are not signed in. @endunless

c. Switch Statements

@switch($i)
    @case(1)
        First case
        @break
    @case(2)
        Second case
        @break
    
    @default
        Default case
@endswitch

d. Loop

  • @for @endfor
@for ($i = 0; $i < 10; $i++)
    The current value is {{ $i }}
@endfor
  • @foreach @endforeach
    檢視 $user 在 $users 中是否為空
@foreach ($users as $user)
    <p>This is user {{ $user->id }}</p>
@endforeach
  • @forelse @empty @endforeelse
    檢視 $user 在 $users 中是否為空,如果是就跳轉到 @empty,如果不是就將印
  • 出來
@forelse ($users as $user)
    <li>{{ $user->name }}</li>
@empty
    <p>No users</p>
@endforelse
  • @while @endwhile
@while (true)
    <p>I'm looping forever.</p>
@endwhile

e. @continu @break

  • 使用 @continu 或 @break 來略過或停止迴圈
@foreach ($users as $user)
    @if ($user->type == 1)
        @continue
    @endif

    <li>{{ $user->name }}</li>

    @if ($user->number == 5)
        @break
    @endif
@endforeach
  • 另外也可以更精簡的寫法,把 @continue 和 @break 寫在判斷式裡
@foreach ($users as $user)
    @continue($user->type == 1)

    <li>{{ $user->name }}</li>

    @break($user->number == 5)
@endforeach

f. The Loop Variable

loop 的相關變數使用

@foreach ($users as $user)
<!-- 當迴圈在第一個值的時候 -->
    @if ($loop->first)
        This is the first iteration.
    @endif
<!-- 當迴圈在第最後一個值的時候 -->
    @if ($loop->last)
        This is the last iteration.
    @endif

    <p>This is user {{ $user->id }}</p>
@endforeach
  • 當有巢狀迴圈時,可以透過 parent 取得上面一層的迴圈變數
@foreach ($users as $user)
    @foreach ($user->posts as $post)
        @if ($loop->parent->first)
            This is first iteration of the parent loop.
        @endif
    @endforeach
@endforeach

4. Blade 的資料顯示與 component

a. 資料顯示

使用雙花括弧{{ $variable }}包住變數要顯示的變數名稱

b. component & slot

  • 在 @component() 裡面寫上指定的 component 名稱

  • component 在雙花括弧 {{ $slot }} 內挖空可以插入 Html

  • 在 component 中挖空雙花括號 {{ $title }},在 views 中如果有使用到 component,就可以使用 @slot('title') @endslot 插入。
  • 而 {{ $slot }} 就對應到沒有指定名稱的字串

c. include subview

使用 include 做成 subview 精簡程式碼


5. CSS 與 JS 設定

a. 放置路徑

  • Develop
    • /resource/sass
    • /resource/js

npm run watch

  • Compoled
    • /public/css
    • /public/js

b. 使用 npm install 安裝所需的套件

c. 在使用 npm run watch 監控生成 public

d. 最後路徑


6. Laravel Mix

使用 Laravel Mix 自動 compile

三、實作靜態頁面

1. 開始的準備

做出 index 的頁面

  • 在 web.php 設定 route
Route::get('/', function () {
    return view('index');
});
  • 新增 resource/route/index.blade.php
  • 執行 php artisan serve 跑 serve

2. 套版 - 基本架構

  • js/css 的路徑:使用絕對路徑 /css/app.scss

如果使用相對路徑 css/app.scss,Laravel 的讀取路徑會使用相對於

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

去做讀取檔案,假如這個 Route 是

Route::get('/foo/bar', function () {
    return view('index');
});

他就會從網址路徑下 http://127.0.0.1:8000/foo/bar/css/app.css 去找 css 檔,就會找不到。

3. layout 調整

  • 將固定的內容(header、footer)使用 @include 繼承
  • 其中畫面會改變的部分(page-title、hero、content) 使用 @yield 呼叫

4. about 頁面製作

  • 因為 header 的部分在 about 頁面有些許不同。因此需要使用 @isset 去判斷,如果 @include 有傳入 $overlay 變數
@include('layout.header', ['overlay'=>true])

,就顯示 l-header_overlay

<header class="l-header @isset($overlay) l-header_overlay @endisset">
  • 另外的狀況是,有 overlay 的變數和沒有 overlay 的變數分別使用不同 class 名稱
@isset($overlay) l-navbar_t-dark-trans 
@else l-navbar_t-light 
@endisset
  • $overlay 變數的設定:
    • 只有在 index 的頁面需要出現 overlay 的變數,因此在 @extend 加上 overlay 的變數為 true
@extends('layout.app', ['overlay' => true])
    • 在 layout/app.blade.php 裡面 header 的 @include 做 overlay 的變數判斷
@include('layout.header', ['overlay' => (isset($overlay) ? $overlay :null)])

5. 導覽列 - 顯示登入後連結

a. 實現導覽列 menu 選項,在該分頁顯示按鈕顏色不同

  • 使用 request 去看目前的 path 在哪,如果在該分頁就使他 active
if ($request->is('admin/*')) {
    //
}

request 用法連結

  • log 用法
    在需要看 log 的地方打上
@php Illuminate\Support\Facades\Log::
info(request()->path()); @endphp

到 storage/laravel.log 看 log

六、 Database - migration/ model

1. Model 簡介 / 資料庫連結

a. 連結資料庫

  • 檔案路徑: config/database.php
'mysql' => [
    'driver' => 'mysql',
    'url' => env('DATABASE_URL'),
    'host' => env('DB_HOST', '127.0.0.1'),
    'port' => env('DB_PORT', '3306'),
    'database' => env('DB_DATABASE', 'forge'),
    'username' => env('DB_USERNAME', 'forge'),
    'password' => env('DB_PASSWORD', ''),
    'unix_socket' => env('DB_SOCKET', ''),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
    'prefix_indexes' => true,
    'strict' => true,
    'engine' => null,
    'options' => extension_loaded('pdo_mysql') ? array_filter([
        PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
    ]) : [],
]
  • 檔案內的路徑指到 .env,打開 .env,下面這一段就是要設定的地方
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=

b. 連接 mysql 資料庫

  • mysql 忘記密碼

    • 關閉 mysql 服務器: sudo /usr/local/mysql/support-files/mysql.server stop
    • 進入目錄: cd /usr/local/mysql/bin
    • 獲取權限: sudo su
    • 重啟伺服器: ./mysqld_safe skip-grant-tables &
    • ctr + D 退出編輯
    • 配置短指令: alias mysql=/usr/local/mysql/bin/mysql
    • 進入 mysql 命令模式: mysql
    • 進入 mysql 數據庫: use mysql
    • 獲取更改權限: flush privileges;
    • 重設密碼: set password for 'root'@'localhost'=password('新密码');
  • 使用 Sequel Pro 連線 mysql

    • host 對應 .env 的 DB_HOST
    • Username 對應 .env 的 DB_USENAME
    • 密碼則是當初安裝 mysql 的設定密碼

  • 新增資料庫

    • Database > Add Database
    • 設定 UTF-8 萬國碼
  • 建立單一使用者連線各自的資料庫

    • 權限設定

  • 回來設定 Larevel .env 檔案

  • 在命令提示字元輸入: php artisan migrate
    測試設定是否有成功

c. migration 建立欄位

  • 建立 migration 的檔案
php artisan make:migration + table 名稱
  • migration 檔案程式碼的撰寫
    撰寫 migration 要寫 up 和 down,up 是新增 table,down 是刪除 table
<?php

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

class CreatPostsTable extends Migration
{
    /**
         * Run the migrations. 
     * up 用來建立新的 posts table
     * @return void
     */
     
     
    public function up()
    {
        Schema::create('posts', function (Blueprint $table)){
            $table->increments('id');
            $table->timestamps();
        }
    }

    /**
     * Reverse the migrations. 
     * down 用來回上一步,刪除掉這個 posts table
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}

4. migration 的操作

A. Table

  • Schema
    • create: 建立 table
    • rename: 更改 table 名稱
    • drop: 丟掉 table
    • dropifExists: 確定有這個 table 才丟掉
    • table: 進入原本已經有的 table,去更改內容
  • Table Name 命名
  • Blueprint $table 的 function 去接 table,之後就可以用 $table 去建立欄位或 index

B. Columns
決定"名字"和"型態"

  • column 的型態
    • integer
    • boolean
    • string: 字串
    • text: 長字串
    • enum: (Admin Manager Member)
    • data/ dateTime/ time: 記錄日期/ 日期時間/ 時間
    • json
    • Increments(UNSIGNED BIGINT): 用在id
    • timestamp(created_at/ updated_at): 建立資料時間/ 更新時間
    • unsignedInteger: fori
    • softDeltes(deleted_at): 刪除的時候其實是更新時間

更多型態參考

C. Indexes

5. 常見的 table 型態

  • id
  • timestamps
    • created_at
    • updated_at

6. column 的 Modifier

public function up()
{
    Schema::create('posts', function (Blueprint $table)){
        $table->increments('id')->nullable; /* 可以為空的*/
        $table->string('title')->default('best title'); /* 預設 title 名稱為 best title
        $table->timestamps()->after('id'); /* 指定放在 id 後面 */
    }
}
  • nullable
  • after 可以決定在放哪個 column 後面
  • default 預設值
  • useCurrent 如果沒有給他值,他會給你現在的時間戳記
  • unsigned 如果希望與 user_table 做連結,可以改成 unsigned
  • comment 為欄位做註記

7.migration 修改/刪除欄位

安裝 package: 用於判別 Database 的狀態如何,配合所需修改,提供適合的 sql 語法。
composer require doctrine/dbal

  • Modifying Column

去修改原本就存在的 column,使用 Schema::table

/* 假如 'name' 原本就存在
Schema::table('user', function (Blueprint $table){
    $table->string('name', 50)->change()
})

去新增欄位一樣使用,Schema::table,不過是去新增一個原本不在的名稱

Schema::table('user', function (Blueprint $table){
    $table->string('nickname')
})

修改欄位的名稱,Laravel 的 renameColumn 不支援型態為 enum 的名稱,如果要修改,要全部刪除再新增

Schema::table('user', function (Blueprint $table){
    $table->renameColumn('from', 'to')
})
  • Dropping Columns
    丟棄 column
Schema::table('user', function (Blueprint $table){
    $table->dropColummn('votes') /*刪除單一 column*/
    $table->dropColummn(['votes','avator','location']) /*刪除多個 column*/
})
  • dropRemenberToken (remember_token)
  • dropSoftDeletes(deletd_at): 有點像 display:none 將這筆 column 隱藏掉
  • dropTimestamps(created_at/ updated_at)

8. migration 索引 (indexes)

a. Indexes

  • 建立索引的時機

    • 在建立 table 時建立索引
      $table->string('email')->unique()

    • 已經建立 table 後,補上索引
      $table->unique('email')

  • 建立複合式的索引,丟進一個陣列
    $table->index(['account_id', 'created_id'])

  • 指定索引的名稱,如果不指定 Laravel 會幫你命名
    $table->unique('email', 'unique_')

  • 建立索引的方法有

    • primary
      • 適用情境:用來明確識別在資料當中的每一筆資料,是不同的資料。每筆資料的主鍵不能重複
      • 例如:每一個排班紀錄都有一個不重複的流水號(CleanScheduleId)
    • unique
      • 適用情境:資料不能重複的欄位,可以接受 NULL 值。
      • 例如:家庭成員的手機號碼,可以設定為UNIQUE
    • index
      • 適用情境:設定在常用來當作查詢條件的欄位
      • 目的:藉由資料庫的演算法提高查詢速度
      • 成本:占用額外的磁碟空間
      • 舉例:家事排班表的打掃日期,經常被拿來當作查詢統計的條件

b. Drop Indexes

  • Drop 索引的方法有
    • dropPrimary: $table->dropPrimary('users_id_primary');
    • dropUnique: $table->dropUnique('users_email_unique');
    • dropIndex: $table->dropIndex('geo_state_index');
      以上方法要指定這個 index 的名稱。
      如果不想指定名稱可以使用 dropIndex(['陣列1'])
Schema::table('geo', function (Blueprint $table) {
    $table->dropIndex(['state']); // Drops index 'geo_state_index'
});

9. Foreign Key

  • 使用情境:
    在 posts 裡面的 user_id 要去和 users 的 id 做連結
Schema::table('posts', function(Blueprint $table){
    $table->unsignedInteger('user_id'); // 設定為 unsignedInteger 因為要和 user 做連結
    $table->foreign('user_id')->reference('id')->on('user') // 設定 user_id 為 foreign key => 讓他和 id 做連結 => 和 users 的 id 做連結
})
  • 延伸狀況:
    如果使用者被刪除了,那使用者所寫的文章怎麼辦?
$table->foreign('user_id')->reference('id')->on('user')->onDelete('cascade') // 如果使用者被刪掉,文章也連帶刪除
})
  • artisan migrate:rollback
    要刪除 migration 前,foreign key 可能已經和其他 table 建立連結了,所以要先刪除 foreign key
    Schema::table('posts' function(Blueprint $table){
        $table->dropForeign(['user_id']);
    })
  • 更新的因應方式 ON UPDATE
    • ON UPADTE RESTRICT (defalt) :same as RESTRICT if you try to update a company_id in table COMPANY the engine will reject the operation if one USER at least links on this company.
    • ON UPDATE NO ACTION : same as RESTRICT
    • ON UPDATE CASCADE : the best one usually : if you update a company_id in a row of table COMPANY the engine will update it accordingly on all USER rows referencing this COMPANY (but no triggers activated on USER table, warning). The engine will track the changes for you, it's good.
    • ON UPDATE SET NULL : if you update a company_id in a row of table COMPANY the engine will set related USERs company_id to NULL (should be available in USER company_id field). I cannot see any interesting thing to do with that on an update, but I may be wrong.
  • 刪除的因應方式 ON DELETE
    • ON DELETE RESTRICT (defalt) : 必須 posts 都已經刪除,user 才能刪除
    • ON DELETE NO ACTION : 要刪除作者直接可以刪,不過文章會留著
    • ON DELETE CASCADE : user 刪除,文章一起刪
    • ON DELETE SET NULL : 刪除作者後,會將刪除的作者變成 NULL

10. migration 演練

  • 建立一個 migration
    php artisan make:migration create_posts_table

  • 跑 migration
    php artisan migrate

  • 回朔 migration
    php artisan migrate:rollback

11. Model

建立 model 指令
php artisan make:model Post

Eloquent

  • ActiveRecord
    • ORM(Object Relation Mapping)
    • Active Record Pattern
      CRUS/getter/setter/properties/etc.
      Model -> table
      Model -> model(relationships)
      Validation
      OOP style query
      Convention > config 慣例大於設定

12. 生成 Model

建立 model 指令
php artisan make:model post

建立 model 指令連帶 migrate
php artisan make:model post migration 或
php artisan make:model post -m

  • Model convention
    Model 預設你會有以下這些資料
    a. Table Name
    b. Primary keys
    c. Timestamps
    d. Database Connection

  • Model customize setting
    可以自己做設定的指令
    a. 修改 table Name

protected $style = "my_post"

b. 修改 Primary keys

protected $primaryKey = "my_key"

c. 不紀錄 timestamps

@var bool
public $timestamps = false;

d. 修改到別的 database

protected $coonection = "connection_name"

13. Eloquent 取得多筆資料(retrieving models)

  • all 取得 table 全部的內容
$flight = App\Flight::all()

foreach($flights as $flight) {
    echo $flight->name
}
  • Additional Constraints
    query builder(where/ orderBy/ take/)
    get
$flight = App\Flight::where('active', 1) //取得 active 是 1 的資料
->orderBy('name', 'desc') // 以 name 做排序, desc 反著排
->take(10) // 拿 10 筆 query limit
->get() // 拿取的動作 將資料丟到 flight 變數
  • Refreshing Model
    • freash(): 不會更新自身的資料,只會再拿一次資料,並且把他指派給另一個變數
    ​​​​$flight = App\Flight::where('number', 'FR 900')->first();
    ​​​​$fresh = $flight ->fresh();
    
    • refresh()
    ​​​​$flight = App\Flight::where('number', 'FR 900')->first();
    ​​​​$flight-> number = 'FR 456' // 中間修改了 number 的值
    ​​​​$flight->refresh(); // 反悔了,重新拿取一次
    ​​​​$flight->number; // 變回 FR 900
    
  • collections : 類似 array, 就是一堆資料的集合
foreach($flights as $flight) {
echo $flight->name // collections 可以使用物件導向的方式取得 name
}
  • Chunking Result
    可以使用 chunk 指定特定數量,處理完之後再抓資料
Flight::chunk(200, function($flights){
    foreach($flights as $flight){
    //
    }
})
  • Cursor
    一次處理一筆資料
foreach(Flight::where('foo', 'bar')->cursor() as $flight) {
    //
}

14. Eloquent 取得單筆資料(Retrieving Single Model)

  • find/ first
$flight = App\Flight::find(1);
// find ID

$flight = App\Flight::where('active', 1)->first();
// 把這堆資料的第一筆取出來
// 丟 id,取得三筆資料
$flight = App\Flight::find([1,2,3]);
  • Not Found Exceptions 如果找不到的狀況
    • findOrFail/ firstOrFail
    ​​​​$model = App\Flight::findOrFail(1);
    ​​​​// 如果找不到會出現 Exception
    ​​​​$model = App\Flight::where('legs', '>', 100)->firstOrFail();
    
  • Aggregates
    • count, sum, max
    ​​​​$count = App\Flight::where('active',1)->count(); // 有幾筆資料
    ​​​​$max = App\Flight::where('active',1)->max('price') // 找到最大的數字並顯示數字
    

15. Eloquent 新增 & 更新資料 (insert & update)

  • save 一次插入或更新一筆資料
    • insert/ update
    ​​​​// insert 因為是一個新的 Flight
    ​​​​use App\Flight
    ​​​​
    ​​​​$flight = new Flight;
    ​​​​$flight->name = request()->namel
    ​​​​$flight->save();
    ​​​​
    ​​​​// update 因為是找到其中一個 Flight
    ​​​​$flight = Flight::find(1);
    ​​​​$flight->name = request()->namel
    ​​​​$flight->save();
    
  • Mass Update 一次更新多筆資料
App\Flight::where('active', 1); // 如果 fligt 有啟用
        ->where('destination', 'San Diego') // 目的地是San Diego
        ->update(['delayed'=>1]) //使用陣列 更新 延後 1
  • Mass Assignment 一次將全部資料放進 model
    得到這些使用者的資料

可以使用全部丟進去的方法

    use Illuminate\Support\Facades\input
    
    $flight = new App\Flight;
    $flight->fill(Input::all());
    $flight->save();

更簡化的做法

    use Illuminate\Support\Facades\input
    
    $flight = App\Flight::create(Input::all());

使用時機: 使用者填完表單後 submit 出去,可以用all() 來接收全部資料

:warning: 注意可能發生的安全性的問題

使用 fillable / guarded 來確認哪一筆資料可以直接放進來 / 不可以放進來,兩個完全相反,不會同時使用

// fillable
namespace App;

use Illuminate\Database\Eloquent\Model
class Flight extends Model
{
    protected $fillable = ['name'];
    // name 可以直接放進來
}

// guarded
namespace App;

use Illuminate\Database\Eloquent\Model
class Flight extends Model
{
    protected $guarded = ['price'];
    // price 不可以直接放進來
}
  • Other Creation Methods
    組合技,如果沒有什麼就執行另一個動作

    • firstOrCreate / firstOrNew
    ​​​​$flight = App\Flight::firstOrCreate(['name'=>'Flight 10']);
    ​​​​// 去找到 Flight 10 的航班,如果沒有就幫我建立一個 name 為 FLight 10 的航班
    ​​​​
    ​​​​$flight = App\Flight::firstOrCreate(
    ​​​​    ['name'=>'Flight 10'],['delay'=>1]
    ​​​​)
    ​​​​// 尋找這筆資料 ['name'=>'Flight 10'],如果沒有就生成這兩筆資料 ['name'=>'Flight 10'],['delay'=>1]
    ​​​​
    ​​​​$flight = App\Flight::firstOrNew(['name'=>'Flight 10'])
    ​​​​// 回傳一個 modal 在 php 裡面,在資料庫還沒生成,如果使用 create 就會在資料庫生成這筆資料
    ​​​​
    ​​​​$flight = App\Flight::firstOrNew(
    ​​​​    ['name'=>'Flight 10'], ['delay'=>1]
    ​​​​)
    
    • updateOrCreate
      找到這筆資料['departure'=>'Oakland', 'destination'=>'San Diego'],如果找不到直接幫我生成這兩筆資料['departure'=>'Oakland', 'destination'=>'San Diego'],
      ['price'=>90]
    ​​​​    $flight = App\Flight::updateOrCreate(
    ​​​​        ['departure'=>'Oakland', 'destination'=>'San Diego'],
    ​​​​        ['price'=>90]
    ​​​​    )
    

16. Eloquent 刪除 modal

  • delete
    先找到目標物,再刪除
$flight = App\Flight::find(1);
$flight->delete()
  • destroy
    直接給 id 位置,刪除資料
App\Flight::destroy(1);
App\Flight::destroy(1,2,3);
App\Flight::destroy([1,2,3]);
App\Flight::destroy(collect([1,2,3]));
  • Delete Models Query
    找到符合需求的資料,一次刪除
$delete = App\Flight::where('active',0)->delete();
  • Soft Delete
    做資料標記。並不是真的刪除

a. 在 migration 出 softDelete 的欄位

Schema::table('flights',function(Blueprint $table){
    $table->softDeletes();
})

b. modal

namespace App;

use Illuminate\Database\Eloquent\Modal;
use Illuminate\Database\Eloquent\SoftDeletes;

class Flight extends Model
{
    use SoftDeletes;
    
    @var array
    protected $dates = ['delete_at'];
}
}

c. 執行 Soft Delete 和 Delete 動作一樣,但因為前面的設置,會執行 Soft Delete,主要去看有沒有 Delete_at 的時間戳記,有就是有 soft Delete

$flight = App\Flight::find(1);
$flight->delete()

d. Querying Soft Deleted Models

  • withTrashed 連同被刪除的一起找
$flights=App\Flight::withTrashed()
        ->where('account_id', 1)
        ->get()
  • onlyTrashed 只找被刪除的資料
$flights=App\Flight::onlyTrashed()
        ->where('account_id', 1)
        ->get()
  • restore 回復刪除的資料
App\Flight::withTrashed()
        ->where('airline_id', 1)
        ->restore()
  • forceDelete
$flight->forceDelete();

17. Eloquent 取得常用資料

  • Query Scopes - Local Scopes
    只想選取固定範圍的資料可以使用,使用 Scopes

例如: 只想選取有 active 的資料

  1. model 中先新增 Query Scopes,注意,query 名字一定要 scope 開頭
@param\Illuminate\Database\Eloquent\Builder $query
@return\Illuminate\Database\Eloquent\Builder

public function scopeActive($query)
{
    return $query->where('active',1)
}
  1. 使用 Query
use App\Post

$posts = Post::popular()->active()->get();
  • Dynamic Scopes
    加入變數來控制 Scope
@param\Illuminate\Database\Eloquent\Builder $query
@param mixed $type
@return\Illuminate\Database\Eloquent\Builder

public
function scopeOfType($query, $type)
{
    return $query->where('type', $type)
    // 找到 query 的 type 等於帶入的變數
}

找到 type 等於 admin 的 scopes

$users = App\User::ofType('admin')->get();
  • Comparing Models
    使用 is,如果這個東西是這個東西的話
if($post->is($anotherPost)){
  \\
}

18. Query Builder 建立 Query 的工具

a. 介紹

  • Query Builder 可以提供給其他 class 使用 (Eloquent)
  • 可以與 php 完美融合
  • 安全性佳
    SELECT*FROM `USERS` WHERE `name` = 'John' 
$user = DB::table('user')->where('name', 'John')->first();
echo $user->name

b. 工具介紹
官網連結

19. relationship

兩個 table 間的關係,例:小明和小明寫的文章的關係

a. 建立 relation 的順序

a. migration 建立 foreign key

    Schema::create('post', function(Blueprint $table){
        $table->increment('id');
        $table->unsignInteger('user_id'); // 因為 foreign key 的關係,要設定 unsignInteger
        $table->timestamp();
        
        $table->foreign('user_id')->reference('id')->on('users')
        // $table 建立  foreign key->用 id 建立關係-> 與 user 的 id
    })

b. Eloquent Model 定義彼此的關連性

    namespace App;
    
    use Illuminate\Database\Eloquent\Model
    
    class User exrends Model
    {
        public function posts()
        {
            return $this->hasMany('App\Post');
        }
    }

c. 實際使用

傳統上分兩次取用資料,一次找小明,另一次找文章

use App\User
use App\Post

$user = User::where('name', '小明')->first();
$posts = Post::where('user_id', $user->id)->get();

foreach ($posts as $key => $post) {
    echo $post->title;
}

如果已經建立 foregin key

    use App\User

    $user = User::where('name', '小明')->first();
    // 只要找到小明

    foreach ($user->posts as $key => $post) {
        echo $post->title;
    }
    // 就可以找到小明下面的文章

b. relation 的關係

  • One To One

雙邊設定:每個使用者都有一個隱私權設定

    namespace App;
    
    use Illuminate\Database\Eloquent\Model
    
    class User extends Model
    {
        public function privacy()
        {
            return $this->hasOne('App\Privacy');
        }
    }

雙邊設定:隱私權是使用者的一部分

    namespace App;
    
    use Illuminate\Database\Eloquent\Model
    
    class Privacy extends Model
    {
        public function user()
        {
            return $this->belongTo('App\User');
        }
    }

Use: 可以透過 user 去找到他的隱私權設定

use App\User

$use = User::find(1);
echo $user->privacy->settings;

Use: 也可以透過隱私權去找到他的 user

use App\Privacy

$privacy= Privacy::find(1);
echo $privacy->user->name;
  • One To Many

雙邊設定: 每篇文章有很多留言

    namespace App;
    
    use Illuminate\Database\Eloquent\Model
    
    class Post exrends Model
    {
        public function comments()
        {
            return $this->hasMany('App\Comment');
        }
    }

雙邊設定: 留言是屬於這篇文章的

    namespace App;
    
    use Illuminate\Database\Eloquent\Model
    
    class Comment exrends Model
    {
        public function post()
        {
            return $this->belongsTo('App\Post');
        }
    }
  • Many To Many
    • 多對多需要一個第三個表格來記錄 post 與 tag 的關係,第三個的表格命名為 post_tag,順序以英文字母順序擺放,
    • id 名稱應命名為 post_id 與 tag_id

雙邊設定: 這篇 post 是屬於這些 tags 的

    namespace App;
    
    use Illuminate\Database\Eloquent\Model
    
    class Post exrends Model
    {
        public function tags()
        {
            return $this->belongsToMany('App\Tag');
        }
    }

雙邊設定: 這個 tag 是屬於這些 posts 的

    namespace App;
    
    use Illuminate\Database\Eloquent\Model
    
    class Tag exrends Model
    {
        public function posts()
        {
            return $this->belongsToMany('App\Post');
        }
    }

Use

    use App\Post
    
    $tags = Post::find(1)->tags()->get();

如果不使用預設名稱,可以透過下列方法更改 post_tag, post_id, post_tag 為 post_tag_refs, my_post_id, my_tag_id

    namespace App;
    
    use Illuminate\Database\Eloquent\Model
    
    class Tag exrends Model
    {
        public function posts()
        {
            return $this->belongsToMany(
                'App\Post', 'post_tag_refs', 'my_post_id', 'my_tag_id'
            );
        }
    }

20. 取得關聯式資料 (Query relations)

在 Model 定義完後,做關聯資料的取得

a. Query Method: 可以接 query builder 去做其他處理

    $suer = App\User::find(1);
    
    $user->posts()->where('active', 1)->get();

b. Dynamic property: 無法接 query builder 去做其他處理

    $suer = App\User::find(1);
    
    foreach($user->post as $post) {
        //
    }

c. Querying Relationship Existence 如果你有

  • 使用關聯去判斷: 找到有留言的文章
$posts = App\Post::has('comments')->get();
  • 過濾條件: 找到有三篇留言以上的文章
$posts = App\Post::has('comments', '>=', 3)->get();
  • 過濾條件: 內容有固定字串的留言才抓出來
$posts = App\Post::whereHas('comments', function($query) {
    $query->where('content', 'like', 'foo%');
})->get();
// 找到 comment 裡面有含 foo% 的 post
  • 多層判斷: 找到至少有一篇文章且有投票的
    $post = App\Post::has('comments.votes')->get();

d. Querying Relationship Absence 如果你沒有

  • 使用關聯去判斷: 找到沒有留言的文章
    $post = App\Post::doesntHave('comment')->get();
  • 過濾條件: 內容沒有固定字串的留言才抓出來
$posts = App\Post::doesntHave('comments', function($query) {
    $query->where('content', 'like', 'foo%');
})->get();
// 找到 comment 裡面有含 foo% 的 post
  • 使用 withCount 會多一個 table名稱 + _count 的屬性
    $post = App\Post::withCount('comment')->get();
    
    foreach ($posts as $post){
        echo $post->comments_count;
    }
  • 使用 withCount,並使用條件篩選
    $posts = App\Post::withCount(['vote', 'comment' => function($query){
    $query->where('content', 'like', 'foo%')
    }])->get();
    
    echo $posts[0]->votes_count;
    echo $posts[0]->comments_count;
  • 更改預設的 _count 名稱
$posts = App\Post::withCount([
    'comment', // 開始的 comment 
    'comment as pending_comment_count' => function($query){
        $query->where('approved', false);
    } // 透過篩選條件過的 comment
])->get();

echo $post[0]->comments_count;
echo $posts[0]->pending_comments_count;

21. 關聯式資料的讀取方式(Lazy & Eager loading)

a. Lazy Loading

在使用 query 時,如果是 Lazy Loading,在每一次的 foreach 都得去跟資料庫要一次資料。這會太耗效能

$book = App\Book::all();

foreach($books as $book) {
    echo $book->author->name;
}

b. Eager Loading

  • 使用 Eager Loading 在第一次就去資料庫把需要的資料拿出來,這樣 foreach 就只會在 php 裡面跑,不用每次都跑回資料庫。
    $book = App\Book::with('author')->get();

    foreach($books as $book) {
        echo $book->author->name;
    }
  • 可以將資料放進陣列中,一次取多筆
    $book = App\Book::with('author', 'publisher')->get();
  • 可以讀取巢狀的資料,讀取作者的聯絡資訊
    $book = App\Book::with('author.contacts')->get();

八、 Controller

route 可以指派 controller 內的 method 做特定行為,可以使 route 裡面比較乾淨

1. 建立 Controller 指令

 php artisan make:controller PostController

a. Router 與 Controller 對應

  • Router
    去抓 postController 裡面的 show
    Route::get('/posts', 'postController@show');
  • Controller
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class PostController extends Controller
{
    public function show()
    {
        # code...
    }
}

b. 在 Controller 內使用 model

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Post; // 加上 post 就可以使用 model

class PostController extends Controller
{
    public function show()
    {
        $post = new Post;
        $post->title = 'title';
        $post->content = '...';
        $post->save();
        // return view
        return view('posts.show', ['post' => $post]);
    }
}

c. Return JSON

前端單純只需要資料,可以回傳 JSON 資料就好

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class PostController extends Controller
{
    public function show(Request $request, $id)
    {
        return response()->([
            'title' => 'Good Article',
            'content' => 'This is contents.'
        ]);
    }
}

d. Return nothing

    public function delete(Request $request, int $id)
    {
        User::destroy($id);
        // 回傳前端 HTTP 200
    }

2. 注入參數 (method injection)

a. controller 使用 $request 全部接收

    namespace App\Http\Controllers
    
    use Illuminate\Http\Request
    
    class UseController extends Controller
    {
        /**
        * Store a user.
        *
        * @param Request $request
        * @return Response
        */
        public function store(Request $request)
        {
            $name = $request->name;
        }
    }

$name = $request->name; 的 name 會等於 $_REQUEST['name'] 等於將 request 的全部資料放在這
如果只要特定的 post 或 get 可以將 name 改成
$_POST['name']
$_GET['name']

b. URL + parameter

  • 當 route 使用 {id} 傳值到 controller 裡面
    Route::get('post/{id}', 'PostController@show')

可以在 function 後寫 $id 接收參數

    namespace App\Http\Controllers
    
    use Illuminate\Http\Request
    
    class UseController extends Controller
    {
        /**
        * Store a user.
        *
        * @param Request $request
        * @return Response
        */
        public function store($id)
        {
            //
        }
    }

c. URL + 變數

    Route::get('post/{user}', 'PostController@destroy')

可以在 function 後寫 model 接收參數

    namespace App\Http\Controllers
    
    use Illuminate\Http\Request
    
    class UseController extends Controller
    {
        /**
        * Store a user.
        *
        * @param Request $request
        * @return Response
        */
        public function destroy(User $user)
        {
            /** 偷偷做了 $user = User::find($id);**/
            $user->destroy();
        }
    }

3. CRUD 的好幫手:resource

a. 自動加上 CRUD 的 method

php artisan make:controller UserController --resource

b. 建立加上 model 的 coontroller

c. 自動加上 CRUD 的 method

php artisan make:controller UserModelController --model=user

d. Routes 對應的設定

  • 只需要生成以下行為
    Route::resource('photos', 'PhotoController')->only([
    'index', 'show'
    ]);
  • 不需要以下行為
    Route::resource('photos', 'PhotoController')->except([
    'create', 'store', 'update', 'destroy'
    ]);

4. MVC 總結

a. 只需回傳一個 view 回去

b. 透過 controller 執行內容再透過 view 印出東西

c. 透過 controller 使用 model 去資料庫拿東西

九、實作 CURD

使用 http 來做 destory post

  • 做一個隱藏的 form,在 input 裡面使用 delete method
  • 記得 form 要加上 csrf token
<form id="delete-form" method="post" action="/posts/id"> @csrf <input type="hidden" name="_method" value="delete"> </form>
  • 按下刪除按鈕時執行 js 替換掉 action 的路徑,然後再送出
let deletePost = function(id) { let result = confirm("Are you sure you want to delete this post ?"); if(result) { let actionUrl = '/posts/'+id; $("#delete-form").attr("action", actionUrl).submit(); } }

使用 ajax 來做 destory post

  • 在 header 加上 meta csrf 的 token
<meta name="csrf-token" content="{{ csrf_token() }}">
  • 在 resource/app.js global 的環境,將 ajax 設定成帶 csrf 參數
$.ajaxSetup({ headers: { 'X-CSRF-Token': $('meta[name=csrf-token]').attr('content') } })
  • 按下刪除按鈕時執行 js,將 ajax 帶上 delete 的方法,之後 reload 頁面
let deletePost = function(id) { let result = confirm("Are you sure you want to delete this post ?"); if(result) { let actionUrl = '/posts/'+id; $.post(actionUrl, {_method:'DELETE'}).done(function() { location.reload(); }); } }

十、Authentication

1. 簡介

使用下面指令產生 Authentication

php artisan make:auth
php artisan migrate

2. 使用方法

a. Protecting Routes

  • 使用 middleware 檢查使用者是否有登入,如果沒有登入就會被踢出去,如果有就會執行 function 內的內容
Route::get('profile', function() {
    // only authenticated user may enter...
})->middleware('auth');
  • 如果整個 controller 的每個 method 都需要驗證可以寫在 controller 的 construct 裡面
    public function _construct()
    {
        $this->middleware('auth')
    }
  • Auth:check()
    use Illuminate\Support\Facades\Auth;
    
    if(Auth::check()){
        // This user is logged in...
    }

b. 取得使用者資訊

    use Illuminates\Support\Facades\Auth
    
    $user = Auth::user();
    
    $id = Auth::id();

c. 未登入導向頁面

路徑: app/Http/Middleware/Authenticate.php

    protected function redirectTo($request)
    {
        if(! $request->expextsJson()){
            return route('login');
        }
    }

3. make:auth

指令

composer require laravel/ui --dev
php artisan ui vue --auth
php artisan migrate
php artisan make:auth

4. middleware protect

  • single protect
    Route::get('/posts/admin', 'postController@admin')->middleware('auth');
  • group protect
Route::middleware(['auth'])->group(function()
{
    // protect route...
});

5. 加上 user_id

  • 使用 migration 加上原本被刪除的 user_id
php artisan make:migration add_users_to_posts_table --table=posts
  • 設定 migration
class AddUsersToPostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->unsignedInteger('user_id');

            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->dropForeign(['user_id']);

            $table->dropColumn('user_id');
        });
    }
}

6. post 紀錄 user_id

$post->user->name

十一、Mail

1. 簡介

php 也有內建信件系統,不過會很常被當成垃圾信件。
這邊建議可以使用
dev: mailtrap
release: mailgun

十二、Validation

laravel Validation

1. 驗證機制

a. HTTP 流程


驗證成功->存入資料庫
驗證失敗->產生錯誤訊息->將錯誤訊息帶回上一個 view

  • 在 method 執行前先做 validation 驗證。

  • 出現錯誤返回上頁的錯誤訊息提示

b. AJAX 流程

  • 回報 200 成功
  • 回報 422 失敗

2. 驗證設定

a. 撰寫驗證邏輯

  • 產生錯誤訊息

如果沒有加上 bail 的話,required、unique、max 三個錯誤訊息都會產生

加上 bail 只要有一個驗證沒通過,即會產生錯誤訊息,後面的動作就不會再去驗證

  • 巢狀陣列的驗證
    使用.來代替 array 的階層

  • trimStrings/ ConvertEmptyStringsToNull

如果是空字串,將會被清掉變成 null,
以下方為例,publish_at 是一個 Option 的設定,因為空字串會被轉為 null,因此要加上 nullable 的設定。免得產生額外錯誤。

3. 印出錯誤訊息

  • first
    取出 email 的第一個錯誤
    echo $errors->first('email');
  • get
    將 email 的所有訊息印出來
    foreach($errors->get('email') as $message) {
        \\
    }

使用 * 將 attachment 裡面巢狀陣列的所有屬性的錯誤都取出來

    foreach($erroes->get(attachment.*) as $message) {
        \\
    }
  • has
    辨識是否有錯誤
    if($errors->has('email')) {
        //
    }
  • all
    取出所有的錯誤
    foreach($errors->all() as $message) {
     \\
    }

4. 自訂錯誤訊息

將錯誤訊息定義好後,丟到 $validator 的 $message
$rule => 錯誤訊息

$message = [
    'required' => 'The :attribute field is required.',
];

$validator = Validator::make($input, $rules, $message);

5. Form Request Validation

將驗證的表單分離出來,原本是寫在 method 裡面去做驗證

分離出來 StoreBlogPost,在 $request 前就做驗證了

  • 使用指令產生 Form Request Validation
php artisan make:request StoreBlogPost

產生的檔案會放在 app/Http/Requests/StoreBlogPost.

  • rules

將原本驗證的資訊寫在 rules

  • Authorize
    驗證使用這是否有權限操作下面的動作
    如果使用者 id 和 comment 的 user_id 一樣的話,就回傳 true,表示 Authorize 通過,反之就是不通過。

  • message
    如果需要提示訊息,可另外新增一個 function 來帶入

6. 自訂驗證條件

a. Use Rule Object

指令

php artisan make:rule Uppercase

驗證文字是否為大寫

引用 Custom Validation Rules 需要用 array 方式把值傳入

b. Use Closure