--- title: 'Laravel 筆記' disqus: Pai --- # 壹、目錄及代辦事項 ## Table of Contents [TOC] ## 待學習項目 - [ ] - - [ ] - - [ ] - - [ ] - # 貳、Routing ## 一、Routing 的用途與設定的方法 ### 1. 段落的語意 ## 二、Veiw - Blade / CSS / JS ### 1. View 的運作方式與 Blade 簡介 #### a. View 存放在 resource/views 的資料夾裡面,檔名後面的 .blade.php 可以省略不寫 ![](https://i.imgur.com/JKRT5PN.png) #### b. 如果在 views 資料夾裡面還有分資料夾,可以使用 . 來呼叫 ![](https://i.imgur.com/hZlOUIX.png) ### 2. Blade 的 layout 設定方式 blade 是 Laravel 創建的 template 系統,專門用來做 layout 的,用來將 controller 和 view 分開。 ![](https://i.imgur.com/paeKikm.png) #### a. @yield 與 @section: 用 title 指定名稱,後面塞要放的字串 ![](https://i.imgur.com/hjk5lPD.png) 用 content 指定名稱,晝面塞要放進去的 p 段落 ![](https://i.imgur.com/AJIWfdD.png) #### b. @section/@show 與 @section/@endsection @section/@show 在 Html 挖出一個空間,然後右側的 section 的 @parent 代表 'This is master sidebar' 這段文字,可以透過移動 @parent 的位置來決定文字的排序 ![](https://i.imgur.com/wN8yeJI.png) ### 3. Blade 的程式控制 #### a. @if @elseif @else @endif ```php @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,不然會顯示沒有登入 ```php= @unless (Auth::check()) You are not signed in. @endunless ``` #### c. Switch Statements ```php @switch($i) @case(1) First case @break @case(2) Second case @break @default Default case @endswitch ``` #### d. Loop - @for @endfor ```php @for ($i = 0; $i < 10; $i++) The current value is {{ $i }} @endfor ``` - @foreach @endforeach 檢視 $user 在 $users 中是否為空 ```php @foreach ($users as $user) <p>This is user {{ $user->id }}</p> @endforeach ``` - @forelse @empty @endforeelse 檢視 $user 在 $users 中是否為空,如果是就跳轉到 @empty,如果不是就將印 <li> 出來 ```php @forelse ($users as $user) <li>{{ $user->name }}</li> @empty <p>No users</p> @endforelse ``` - @while @endwhile ```php @while (true) <p>I'm looping forever.</p> @endwhile ``` #### e. @continu @break - 使用 @continu 或 @break 來略過或停止迴圈 ```php @foreach ($users as $user) @if ($user->type == 1) @continue @endif <li>{{ $user->name }}</li> @if ($user->number == 5) @break @endif @endforeach ``` - 另外也可以更精簡的寫法,把 @continue 和 @break 寫在判斷式裡 ```php @foreach ($users as $user) @continue($user->type == 1) <li>{{ $user->name }}</li> @break($user->number == 5) @endforeach ``` #### f. The Loop Variable loop 的相關變數使用 ![](https://i.imgur.com/uTVtLgP.png) ```php @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 取得上面一層的迴圈變數 ```php @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 名稱 ![](https://i.imgur.com/s48JcAE.png) - component 在雙花括弧 {{ $slot }} 內挖空可以插入 Html ![](https://i.imgur.com/Z3W9Hgc.png) - 在 component 中挖空雙花括號 {{ $title }},在 views 中如果有使用到 component,就可以使用 @slot('title') @endslot 插入。 - 而 {{ $slot }} 就對應到沒有指定名稱的字串 ![](https://i.imgur.com/mRqyf0K.png) #### c. include subview 使用 include 做成 subview 精簡程式碼 ![](https://i.imgur.com/LdlXFGu.png) --- ### 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. 最後路徑 ![](https://i.imgur.com/OpKGrOM.png) --- ### 6. Laravel Mix 使用 Laravel Mix 自動 compile ## 三、實作靜態頁面 ### 1. 開始的準備 做出 index 的頁面 - 在 web.php 設定 route ```php Route::get('/', function () { return view('index'); }); ``` - 新增 resource/route/index.blade.php - 執行 php artisan serve 跑 serve ### 2. 套版 - 基本架構 - js/css 的路徑:使用絕對路徑 /css/app.scss ![](https://i.imgur.com/dpjDCdr.png) 如果使用相對路徑 css/app.scss,Laravel 的讀取路徑會使用相對於 ```php Route::get('/', function () { return view('index'); }); ``` 去做讀取檔案,假如這個 Route 是 ```php 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 變數 ```php @include('layout.header', ['overlay'=>true]) ``` ,就顯示 l-header_overlay ```htmlmixed= <header class="l-header @isset($overlay) l-header_overlay @endisset"> ``` - 另外的狀況是,有 overlay 的變數和沒有 overlay 的變數分別使用不同 class 名稱 ```php @isset($overlay) l-navbar_t-dark-trans @else l-navbar_t-light @endisset ``` - $overlay 變數的設定: - 只有在 index 的頁面需要出現 overlay 的變數,因此在 @extend 加上 overlay 的變數為 true ```php @extends('layout.app', ['overlay' => true]) ``` - - 在 layout/app.blade.php 裡面 header 的 @include 做 overlay 的變數判斷 ```php= @include('layout.header', ['overlay' => (isset($overlay) ? $overlay :null)]) ``` ### 5. 導覽列 - 顯示登入後連結 #### a. 實現導覽列 menu 選項,在該分頁顯示按鈕顏色不同 - 使用 request 去看目前的 path 在哪,如果在該分頁就使他 active ```php if ($request->is('admin/*')) { // } ``` request 用法[連結](https://laravel.tw/docs/5.3/requests) - log 用法 在需要看 log 的地方打上 ```laravel @php Illuminate\Support\Facades\Log:: info(request()->path()); @endphp ``` 到 storage/laravel.log 看 log ## 六、 Database - migration/ model ### 1. Model 簡介 / 資料庫連結 #### a. 連結資料庫 - 檔案路徑: config/database.php ```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,下面這一段就是要設定的地方 ```php 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 的設定密碼 ![](https://i.imgur.com/q6B1Rk7.png) ![](https://i.imgur.com/LsAGpfa.png) - 新增資料庫 - Database > Add Database - 設定 UTF-8 萬國碼 ![](https://i.imgur.com/QIRXMYT.png) - 建立單一使用者連線各自的資料庫 ![](https://i.imgur.com/2ldlVhn.jpg) - 權限設定 ![](https://i.imgur.com/pxG0ER2.png) - 回來設定 Larevel .env 檔案 ![](https://i.imgur.com/QN0WnMd.png) - 在命令提示字元輸入: php artisan migrate 測試設定是否有成功 #### c. migration 建立欄位 - 建立 migration 的檔案 ```php= php artisan make:migration + table 名稱 ``` - migration 檔案程式碼的撰寫 撰寫 migration 要寫 up 和 down,up 是新增 table,down 是刪除 table ```laravel <?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): 刪除的時候其實是更新時間 更多型態[參考](https://laravel.tw/docs/5.3/migrations#columns) C. Indexes #### 5. 常見的 table 型態 - id - timestamps - created_at - updated_at ### 6. column 的 Modifier ```php 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 ```laravel /* 假如 'name' 原本就存在 Schema::table('user', function (Blueprint $table){ $table->string('name', 50)->change() }) ``` 去新增欄位一樣使用,Schema::table,不過是去新增一個原本不在的名稱 ```laravel Schema::table('user', function (Blueprint $table){ $table->string('nickname') }) ``` 修改欄位的名稱,Laravel 的 renameColumn 不支援型態為 enum 的名稱,如果要修改,要全部刪除再新增 ```laravel Schema::table('user', function (Blueprint $table){ $table->renameColumn('from', 'to') }) ``` - Dropping Columns 丟棄 column ```laravel 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']) ```php Schema::table('geo', function (Blueprint $table) { $table->dropIndex(['state']); // Drops index 'geo_state_index' }); ``` ### 9. Foreign Key - 使用情境: 在 posts 裡面的 user_id 要去和 users 的 id 做連結 ```php 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 做連結 }) ``` - 延伸狀況: 如果使用者被刪除了,那使用者所寫的文章怎麼辦? ```php $table->foreign('user_id')->reference('id')->on('user')->onDelete('cascade') // 如果使用者被刪掉,文章也連帶刪除 }) ``` - artisan migrate:rollback 要刪除 migration 前,foreign key 可能已經和其他 table 建立連結了,所以要先刪除 foreign key ```laravel 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) ![](https://i.imgur.com/AoAGLtn.png) - 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 ```laravel protected $style = "my_post" ``` b. 修改 Primary keys ```laravel protected $primaryKey = "my_key" ``` c. 不紀錄 timestamps ```laravel @var bool public $timestamps = false; ``` d. 修改到別的 database ```laravel protected $coonection = "connection_name" ``` ### 13. Eloquent 取得多筆資料(retrieving models) - all 取得 table 全部的內容 ```laravel $flight = App\Flight::all() foreach($flights as $flight) { echo $flight->name } ``` - Additional Constraints query builder(where/ orderBy/ take/...) get ```laravel $flight = App\Flight::where('active', 1) //取得 active 是 1 的資料 ->orderBy('name', 'desc') // 以 name 做排序, desc 反著排 ->take(10) // 拿 10 筆 query limit ->get() // 拿取的動作 將資料丟到 flight 變數 ``` - Refreshing Model - freash(): 不會更新自身的資料,只會再拿一次資料,並且把他指派給另一個變數 ```laravel $flight = App\Flight::where('number', 'FR 900')->first(); $fresh = $flight ->fresh(); ``` - refresh() ``` laravel $flight = App\Flight::where('number', 'FR 900')->first(); $flight-> number = 'FR 456' // 中間修改了 number 的值 $flight->refresh(); // 反悔了,重新拿取一次 $flight->number; // 變回 FR 900 ``` - collections : 類似 array, 就是一堆資料的集合 ```laravel foreach($flights as $flight) { echo $flight->name // collections 可以使用物件導向的方式取得 name } ``` - Chunking Result 可以使用 chunk 指定特定數量,處理完之後再抓資料 ```laravel Flight::chunk(200, function($flights){ foreach($flights as $flight){ // } }) ``` - Cursor 一次處理一筆資料 ```laravel foreach(Flight::where('foo', 'bar')->cursor() as $flight) { // } ``` ### 14. Eloquent 取得單筆資料(Retrieving Single Model) - find/ first ```laravel $flight = App\Flight::find(1); // find ID $flight = App\Flight::where('active', 1)->first(); // 把這堆資料的第一筆取出來 ``` ```laravel // 丟 id,取得三筆資料 $flight = App\Flight::find([1,2,3]); ``` - Not Found Exceptions 如果找不到的狀況 - findOrFail/ firstOrFail ```laravel $model = App\Flight::findOrFail(1); // 如果找不到會出現 Exception $model = App\Flight::where('legs', '>', 100)->firstOrFail(); ``` - Aggregates - count, sum, max ```laravel $count = App\Flight::where('active',1)->count(); // 有幾筆資料 $max = App\Flight::where('active',1)->max('price') // 找到最大的數字並顯示數字 ``` ### 15. Eloquent 新增 & 更新資料 (insert & update) - save 一次插入或更新一筆資料 - insert/ update ```laravel // 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 一次更新多筆資料 ```laravel App\Flight::where('active', 1); // 如果 fligt 有啟用 ->where('destination', 'San Diego') // 目的地是San Diego ->update(['delayed'=>1]) //使用陣列 更新 延後 1 ``` - Mass Assignment 一次將全部資料放進 model 得到這些使用者的資料 ![](https://i.imgur.com/MgWCbqD.png) 可以使用全部丟進去的方法 ```laravel use Illuminate\Support\Facades\input $flight = new App\Flight; $flight->fill(Input::all()); $flight->save(); ``` 更簡化的做法 ```laravel use Illuminate\Support\Facades\input $flight = App\Flight::create(Input::all()); ``` 使用時機: 使用者填完表單後 submit 出去,可以用all() 來接收全部資料 ![](https://i.imgur.com/YjISLX4.png) :warning: 注意可能發生的安全性的問題 使用 fillable / guarded 來確認哪一筆資料可以直接放進來 / 不可以放進來,兩個完全相反,不會同時使用 ```laravel // 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 ```laravel $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] ```laravel $flight = App\Flight::updateOrCreate( ['departure'=>'Oakland', 'destination'=>'San Diego'], ['price'=>90] ) ``` ### 16. Eloquent 刪除 modal - delete 先找到目標物,再刪除 ```laravel $flight = App\Flight::find(1); $flight->delete() ``` - destroy 直接給 id 位置,刪除資料 ```laravel 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 找到符合需求的資料,一次刪除 ```laravel $delete = App\Flight::where('active',0)->delete(); ``` - Soft Delete 做資料標記。並不是真的刪除 a. 在 migration 出 softDelete 的欄位 ```laravel Schema::table('flights',function(Blueprint $table){ $table->softDeletes(); }) ``` b. modal ```laravel 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 ```laravel $flight = App\Flight::find(1); $flight->delete() ``` d. Querying Soft Deleted Models - withTrashed 連同被刪除的一起找 ```laravel $flights=App\Flight::withTrashed() ->where('account_id', 1) ->get() ``` - onlyTrashed 只找被刪除的資料 ```laravel $flights=App\Flight::onlyTrashed() ->where('account_id', 1) ->get() ``` - restore 回復刪除的資料 ```laravel App\Flight::withTrashed() ->where('airline_id', 1) ->restore() ``` - forceDelete ```laravel $flight->forceDelete(); ``` ### 17. Eloquent 取得常用資料 - Query Scopes - Local Scopes 只想選取固定範圍的資料可以使用,使用 Scopes ![](https://i.imgur.com/aYFBdRQ.png) 例如: 只想選取有 active 的資料 1. model 中先新增 Query Scopes,注意,query 名字一定要 scope 開頭 ```laravel @param\Illuminate\Database\Eloquent\Builder $query @return\Illuminate\Database\Eloquent\Builder public function scopeActive($query) { return $query->where('active',1) } ``` 2. 使用 Query ```laravel use App\Post $posts = Post::popular()->active()->get(); ``` - Dynamic Scopes 加入變數來控制 Scope ![](https://i.imgur.com/SPW6Bse.png) ```laravel @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 ```laravel $users = App\User::ofType('admin')->get(); ``` - Comparing Models 使用 is,如果這個東西是這個東西的話.... ```laravel if($post->is($anotherPost)){ \\ } ``` ### 18. Query Builder 建立 Query 的工具 a. 介紹 - Query Builder 可以提供給其他 class 使用 (Eloquent) - 可以與 php 完美融合 - 安全性佳 ```mysql SELECT*FROM `USERS` WHERE `name` = 'John' ``` ```laravel $user = DB::table('user')->where('name', 'John')->first(); echo $user->name ``` b. 工具介紹 [官網連結](https://laravel.com/docs/5.7/queries) ### 19. relationship 兩個 table 間的關係,例:小明和小明寫的文章的關係 ![](https://i.imgur.com/AkbG50l.png) #### a. 建立 relation 的順序 a. migration 建立 foreign key ```laravel 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 定義彼此的關連性 ```laravel namespace App; use Illuminate\Database\Eloquent\Model class User exrends Model { public function posts() { return $this->hasMany('App\Post'); } } ``` c. 實際使用 傳統上分兩次取用資料,一次找小明,另一次找文章 ```laravel 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 ```laravel use App\User $user = User::where('name', '小明')->first(); // 只要找到小明 foreach ($user->posts as $key => $post) { echo $post->title; } // 就可以找到小明下面的文章 ``` #### b. relation 的關係 ![](https://i.imgur.com/KJvhlij.png) - One To One 雙邊設定:每個使用者都有一個隱私權設定 ```laravel namespace App; use Illuminate\Database\Eloquent\Model class User extends Model { public function privacy() { return $this->hasOne('App\Privacy'); } } ``` 雙邊設定:隱私權是使用者的一部分 ```laravel namespace App; use Illuminate\Database\Eloquent\Model class Privacy extends Model { public function user() { return $this->belongTo('App\User'); } } ``` Use: 可以透過 user 去找到他的隱私權設定 ```laravel use App\User $use = User::find(1); echo $user->privacy->settings; ``` Use: 也可以透過隱私權去找到他的 user ```laravel use App\Privacy $privacy= Privacy::find(1); echo $privacy->user->name; ``` - One To Many ![](https://i.imgur.com/mkyP1gk.png) 雙邊設定: 每篇文章有很多留言 ```laravel namespace App; use Illuminate\Database\Eloquent\Model class Post exrends Model { public function comments() { return $this->hasMany('App\Comment'); } } ``` 雙邊設定: 留言是屬於這篇文章的 ```laravel 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 ![](https://i.imgur.com/TYHuv2e.png) 雙邊設定: 這篇 post 是屬於這些 tags 的 ```laravel namespace App; use Illuminate\Database\Eloquent\Model class Post exrends Model { public function tags() { return $this->belongsToMany('App\Tag'); } } ``` 雙邊設定: 這個 tag 是屬於這些 posts 的 ```laravel namespace App; use Illuminate\Database\Eloquent\Model class Tag exrends Model { public function posts() { return $this->belongsToMany('App\Post'); } } ``` Use ```laravel use App\Post $tags = Post::find(1)->tags()->get(); ``` 如果不使用預設名稱,可以透過下列方法更改 post_tag, post_id, post_tag 為 post_tag_refs, my_post_id, my_tag_id ```laravel 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 去做其他處理 ```laravel $suer = App\User::find(1); $user->posts()->where('active', 1)->get(); ``` #### b. Dynamic property: 無法接 query builder 去做其他處理 ```laravel $suer = App\User::find(1); foreach($user->post as $post) { // } ``` #### c. Querying Relationship Existence 如果你有 - 使用關聯去判斷: 找到有留言的文章 ```laravel $posts = App\Post::has('comments')->get(); ``` - 過濾條件: 找到有三篇留言以上的文章 ```laravel $posts = App\Post::has('comments', '>=', 3)->get(); ``` - 過濾條件: 內容有固定字串的留言才抓出來 ```laravel $posts = App\Post::whereHas('comments', function($query) { $query->where('content', 'like', 'foo%'); })->get(); // 找到 comment 裡面有含 foo% 的 post ``` - 多層判斷: 找到至少有一篇文章且有投票的 ```laravel $post = App\Post::has('comments.votes')->get(); ``` #### d. Querying Relationship Absence 如果你沒有 - 使用關聯去判斷: 找到沒有留言的文章 ```laravel $post = App\Post::doesntHave('comment')->get(); ``` - 過濾條件: 內容沒有固定字串的留言才抓出來 ```laravel $posts = App\Post::doesntHave('comments', function($query) { $query->where('content', 'like', 'foo%'); })->get(); // 找到 comment 裡面有含 foo% 的 post ``` #### e. Counting Related Models - 使用 withCount 會多一個 table名稱 + _count 的屬性 ```laravel $post = App\Post::withCount('comment')->get(); foreach ($posts as $post){ echo $post->comments_count; } ``` - 使用 withCount,並使用條件篩選 ```laravel $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 名稱 ```laravel $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 都得去跟資料庫要一次資料。這會太耗效能 ```laravel $book = App\Book::all(); foreach($books as $book) { echo $book->author->name; } ``` #### b. Eager Loading - 使用 Eager Loading 在第一次就去資料庫把需要的資料拿出來,這樣 foreach 就只會在 php 裡面跑,不用每次都跑回資料庫。 ```laravel $book = App\Book::with('author')->get(); foreach($books as $book) { echo $book->author->name; } ``` - 可以將資料放進陣列中,一次取多筆 ```laravel $book = App\Book::with('author', 'publisher')->get(); ``` - 可以讀取巢狀的資料,讀取作者的聯絡資訊 ```laravel $book = App\Book::with('author.contacts')->get(); ``` ## 八、 Controller route 可以指派 controller 內的 method 做特定行為,可以使 route 裡面比較乾淨 ### 1. 建立 Controller 指令 ```laravel php artisan make:controller PostController ``` #### a. Router 與 Controller 對應 - Router 去抓 postController 裡面的 show ```laravel Route::get('/posts', 'postController@show'); ``` - Controller ```laravel <?php namespace App\Http\Controllers; use Illuminate\Http\Request; class PostController extends Controller { public function show() { # code... } } ``` #### b. 在 Controller 內使用 model ```laravel <?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 資料就好 ```laravel <?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 ```laravel public function delete(Request $request, int $id) { User::destroy($id); // 回傳前端 HTTP 200 } ``` ### 2. 注入參數 (method injection) #### a. controller 使用 $request 全部接收 ```laravel 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 裡面 ```laravel Route::get('post/{id}', 'PostController@show') ``` 可以在 function 後寫 $id 接收參數 ```laravel 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 + 變數 ```laravel Route::get('post/{user}', 'PostController@destroy') ``` 可以在 function 後寫 model 接收參數 ```laravel 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 ```laravel php artisan make:controller UserController --resource ``` #### b. 建立加上 model 的 coontroller #### c. 自動加上 CRUD 的 method ```laravel php artisan make:controller UserModelController --model=user ``` ![](https://i.imgur.com/7UVeZz0.png) #### d. Routes 對應的設定 - 只需要生成以下行為 ```laravel Route::resource('photos', 'PhotoController')->only([ 'index', 'show' ]); ``` - 不需要以下行為 ```laravel Route::resource('photos', 'PhotoController')->except([ 'create', 'store', 'update', 'destroy' ]); ``` ### 4. MVC 總結 #### a. 只需回傳一個 view 回去 ![](https://i.imgur.com/pRkSHnP.png) #### b. 透過 controller 執行內容再透過 view 印出東西 ![](https://i.imgur.com/SIe4PCW.png) #### c. 透過 controller 使用 model 去資料庫拿東西 ![](https://i.imgur.com/3DgKerB.png) ## 九、實作 CURD ### 使用 http 來做 destory post - 做一個隱藏的 form,在 input 裡面使用 delete method - 記得 form 要加上 csrf token ```htmlmixed= <form id="delete-form" method="post" action="/posts/id"> @csrf <input type="hidden" name="_method" value="delete"> </form> ``` - 按下刪除按鈕時執行 js 替換掉 action 的路徑,然後再送出 ```javascript= 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 ```htmlmixed= <meta name="csrf-token" content="{{ csrf_token() }}"> ``` - 在 resource/app.js global 的環境,將 ajax 設定成帶 csrf 參數 ```javascript= $.ajaxSetup({ headers: { 'X-CSRF-Token': $('meta[name=csrf-token]').attr('content') } }) ``` - 按下刪除按鈕時執行 js,將 ajax 帶上 delete 的方法,之後 reload 頁面 ```javascript= 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 內的內容 ```laravel Route::get('profile', function() { // only authenticated user may enter... })->middleware('auth'); ``` - 如果整個 controller 的每個 method 都需要驗證可以寫在 controller 的 construct 裡面 ```laravel public function _construct() { $this->middleware('auth') } ``` - Auth:check() ```laravel use Illuminate\Support\Facades\Auth; if(Auth::check()){ // This user is logged in... } ``` #### b. 取得使用者資訊 ```laravel use Illuminates\Support\Facades\Auth $user = Auth::user(); $id = Auth::id(); ``` #### c. 未登入導向頁面 路徑: app/Http/Middleware/Authenticate.php ```laravel 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 ```laravel Route::get('/posts/admin', 'postController@admin')->middleware('auth'); ``` - group protect ```laravel 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 ```laravel 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](https://mailtrap.io/) release: [mailgun](https://www.mailgun.com/) ## 十二、Validation [laravel Validation](https://laravel.com/docs/7.x/validation#available-validation-rules) ### 1. 驗證機制 #### a. HTTP 流程 ![](https://i.imgur.com/NTyndFY.png) 驗證成功->存入資料庫 驗證失敗->產生錯誤訊息->將錯誤訊息帶回上一個 view - 在 method 執行前先做 validation 驗證。 ![](https://i.imgur.com/ud4Kmkh.png) - 出現錯誤返回上頁的錯誤訊息提示 ![](https://i.imgur.com/3r0v4Cr.png) #### b. AJAX 流程 - 回報 200 成功 - 回報 422 失敗 ### 2. 驗證設定 #### a. 撰寫驗證邏輯 - 產生錯誤訊息 如果沒有加上 bail 的話,required、unique、max 三個錯誤訊息都會產生 ![](https://i.imgur.com/agoP2XG.png) 加上 bail 只要有一個驗證沒通過,即會產生錯誤訊息,後面的動作就不會再去驗證 ![](https://i.imgur.com/5VQn7Zh.png) - 巢狀陣列的驗證 使用.來代替 array 的階層 ![](https://i.imgur.com/7TVOGrH.png) - trimStrings/ ConvertEmptyStringsToNull 如果是空字串,將會被清掉變成 null, 以下方為例,publish_at 是一個 Option 的設定,因為空字串會被轉為 null,因此要加上 nullable 的設定。免得產生額外錯誤。 ![](https://i.imgur.com/HAq7Fit.png) ### 3. 印出錯誤訊息 - first 取出 email 的第一個錯誤 ```laravel echo $errors->first('email'); ``` - get 將 email 的所有訊息印出來 ```laravel foreach($errors->get('email') as $message) { \\ } ``` 使用 * 將 attachment 裡面巢狀陣列的所有屬性的錯誤都取出來 ```laravel foreach($erroes->get(attachment.*) as $message) { \\ } ``` - has 辨識是否有錯誤 ```laravel if($errors->has('email')) { // } ``` - all 取出所有的錯誤 ```laravel foreach($errors->all() as $message) { \\ } ``` ### 4. 自訂錯誤訊息 將錯誤訊息定義好後,丟到 $validator 的 $message $rule => 錯誤訊息 ```laravel $message = [ 'required' => 'The :attribute field is required.', ]; $validator = Validator::make($input, $rules, $message); ``` ### 5. Form Request Validation 將驗證的表單分離出來,原本是寫在 method 裡面去做驗證 ![](https://i.imgur.com/qeCUbRW.png) 分離出來 StoreBlogPost,在 $request 前就做驗證了 ![](https://i.imgur.com/MD1rdsV.png) - 使用指令產生 Form Request Validation ``` php artisan make:request StoreBlogPost ``` 產生的檔案會放在 app/Http/Requests/StoreBlogPost. - rules 將原本驗證的資訊寫在 rules ![](https://i.imgur.com/JkF0Aqt.png) - Authorize 驗證使用這是否有權限操作下面的動作 如果使用者 id 和 comment 的 user_id 一樣的話,就回傳 true,表示 Authorize 通過,反之就是不通過。 ![](https://i.imgur.com/NgWupl0.png) - message 如果需要提示訊息,可另外新增一個 function 來帶入 ![](https://i.imgur.com/aWGxoxe.png) ### 6. 自訂驗證條件 #### a. Use Rule Object 指令 ``` php artisan make:rule Uppercase ``` 驗證文字是否為大寫 ![](https://i.imgur.com/AI3ADYV.png) 引用 Custom Validation Rules 需要用 array 方式把值傳入 ![](https://i.imgur.com/9tM2leQ.png) #### b. Use Closure