# Laravel筆記 ## 套件管理 Composer or Laravel Installer 1. 使用composer -> 到官網下載:https://getcomposer.org/ ,然後執行並安裝,到某一步驟時將add to PATH 選項勾選用以自動加入環境變數。 2. 使用Laravel Installer -> 到terminal中鍵入指令 ```terminal composer global require "laravel/installer" ``` ## 建立新專案: ```php= composer create-project --prefer-dist laravel/laravel (專案名稱) laravel new (專案名稱) ``` ## 運行網頁(啟用網頁伺服器) ```terminal php artisan server ``` ## 專案結構 ### (一)設定檔config資料夾 設定整個專案的 Web APP 1. database.php -> 設定與資料庫連線,有各種DB引擎adapter 2. mail.php -> 寄信用 (等同於PHP Mailer) ### (二)database資料夾 1. 放置與資料庫相關之目錄與文件 2. 底下的migrations資料夾很重要,尤其是多人開發,會記錄資料庫變化 ### (三)public資料夾 1. 放置一些公開的東西 2. htaceess 設定權限 ### (四)resources資料夾 1. 網站開發所需資源存放處 2. views資料夾存放頁面 ### (五)routes資料夾 1. 控制各個網站進入點 2. web.php 控制主要進入點 3. api.php 可以寫api的地方(想提供的api) ### (六)vendor資料夾 1. 放置套件的地方,跟著整個專案一起佈署 2. ### (七).env檔 環境設定檔,設定一些較機密的資料,如資料庫密碼、AWS存取金鑰、PUSHER_APP推播用的東西、smtp等等 ### (八).gitignore檔 裡面的東西不會被push上去gitghub上,如有東西不要丟上去git可以放裡面 ### (九)composer.json檔 放置整個專案所有套件的版本資訊 ### (十)package.json檔 前端套件版本資訊 ## 虛擬環境 1. Laravel Homestead 2. Docker ## 設定路由、視圖函數 = Python-Django -> url.py、views.py 專案底下的routes資料夾下web.php ## 目標網頁 = Python-Django->template資料夾 專案底下的resources資料夾的views資料夾的任一檔案 ## 當目標檔名相同,優先使用public資料夾的東西,public資料夾 > views資料夾 > 其他資料夾 ## 套用佈景(layouts "須自己建") @extends('layout.app') -> 套用resources資料夾底下的layout資料夾底下的app.blade.php ## Models資料夾 -> (projectname/app/Models) ## Views資料夾 -> (projectname/resources/views) ## Controllers資料夾 -> (projectname/app/Http/Controllers/) ## 路徑說明 ### 1. 一般情況不帶參數傳遞時 ```php= // 雙冒號後面是填寫適當的http請求動作 // get、post Route::get('/', function () { // 控制進入路徑,可控制使用者使用甚麼網址進入網頁 return view('welcome'); // 回傳甚麼畫面給使用者看 }); ``` ### 2. 帶參數傳遞時,如id(可帶多個參數) ```php= // 小括號放參數存放的變數以供function解析 Route::get('/cats/{id}',function($id){ // 路徑內部須帶大括號,以供參數傳遞 return "我是第" . $id . "個人"; }); ``` ### 3. 連結指定Controller ```php= // 逗點後接陣列,[Controllername::class, 'functionname'] Route::get('/about', [WelcomeController::class ,'about']); ``` ### 4. 開放部分網址(URL入口管理) ```php= // 只開放index、show Route::resource('posts', PostController::class) -> only(['index', 'show']); // 除了index、show以外都開放 Route::resource('posts', PostController::class) -> except(['index', 'show']); ``` ## Controller寫法 ```php= <?php namespace App\Http\Controllers; use Illuminate\Http\Request; class WelcomeController extends Controller { // public function about(){ return view('about'); //return view('pages/about') // 當有第二層資料夾時(view/pages/about) //return view('pages.about') // 效果同上,如果使用這種寫法,避免再路徑上的資料夾加上'.',否則容易出錯 } } ``` ## HTTP動詞(設定路由時可以用到) 1. get 索取一or多筆資料 2. post 新增一or多筆資料 3. patch(部分)/put(全部) 修改資料 4. delete 刪除一or多筆資料 5. any 所有動作我都處理 6. match 符合相應動作的才處理網頁請求(可用陣列儲存你允許的動作) ## 查看 php artisan 底下有什麼功能~~ ### EX:查看artisan有什麼功能 ```terminal php artisan ``` ### EX:啟用網頁伺服器 ```terminal php artisan serve ``` ### EX:網站維護模式與取消 ```terminal= 1. 開啟維護模式-開後門 # 開啟維護模式,後面參數可彈性使用,在這邊是開一個秘密端口給工程師使用 # 可觀察cookie,會發現,多一個通行證 php artisan down --secret=hello 2. 開啟維護模式-指定路徑顯示維護中畫面(需創建一個notok.blade.php的檔案) php artisan down --render='notok' 3. 關閉維護模式 php artisan up # 關閉維護模式 ``` ### EX:新增Controller #### (後面如果加 - -resource會自動加入一些常用function框架) ```terminal php artisan make:controller NameController --resource ``` ### EX:新增Model #### (後面如果加上 - - migration 或是 -m 有順便加上migration動作的意思) #### (這樣方便也比較不會有model跟table勾稽上有問題) ```terminal php artisan make:model modName --migration ``` ### EX:查看有什麼Route ```terminal php artisan route:list ``` ### EX:建立資料庫描述檔(Laravel也是採用ORM)=>Eloquent ORM #### create_posts_table(檔名) - -create="posts"(新增一個叫做posts的資料表) ```terminal php artisan make:migration create_posts_table --create="posts" ``` ### EX:遷移(同步)資料庫or退回前版本 #### migrate是負責資料表結構而不是內容 **執行有風險,如果已有資料存在,rollback後資料會連帶資料表一起消失** ```terminal= # 執行遷移動作 php artisan migrate # 執行退回前一版本動作 php artisan migrate:rollback # 查看所有migrate狀態 php artisan migrate:status ``` ### EX:透過migrate工具生成描述檔處理指定資料表(以處理上面posts表為例) ##### 可以在描述檔名上加一些關鍵字眼(add_...to_tableName),Laravel會自動判別你想操作哪個資料表 ```terminal php artisan make:migration update_posts_table --table="posts" ``` ### EX:進入tinker對model進行操作 ```terminal php artisan tinker ``` ## tinker #### 簡單來說,前面通常是寫查詢(挑選條件),後面接你指定的動作,即可完成CRUD ### tinker 操作(C,create) 1. 單欄寫入 ![image](https://hackmd.io/_uploads/Hk3STC8TT.png) 2. 多欄寫入(要先進去model覆寫) ```php= # 對Book的model進行覆寫,類似白名單的用途 <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Book extends Model { use HasFactory; protected $fillable = [ // 開放可多欄寫入的欄位 'title', 'description', 'price', 'available' ]; } ``` ![image](https://hackmd.io/_uploads/rk_oTxDaT.png) ### tinker操作(R,read) 1. 簡單查詢 ![image](https://hackmd.io/_uploads/rJQVNbwpT.png) 2. 進階查詢 尋找特定字串 ![image](https://hackmd.io/_uploads/rklDSZwT6.png) 指定排序方式 ![image](https://hackmd.io/_uploads/By2QUbDTT.png) ### tinker操作(U,update & D,delete) 1. 更新 (方法一)改值 ![image](https://hackmd.io/_uploads/HywvxoKaT.png) (方法二)重新填值 ![image](https://hackmd.io/_uploads/HkrCejFap.png) (方法三)單次大量修改欄位值 ![image](https://hackmd.io/_uploads/SJunZoYT6.png) 2. 刪除 (方法一)利用id找到該筆資料並全部刪除 ![image](https://hackmd.io/_uploads/By_i4oFpa.png) (方法二)同上 ![image](https://hackmd.io/_uploads/rJM_BoYp6.png) ### 操作model ```terminal= 1. 多欄寫入 $data=['fieldName'='value',...] Book::create($data) 2. 單欄寫入 $b = new Book $b -> 'fieldName1'='value1'; $b -> 'fieldName2'='value2'; $b -> 'fieldName3'='value3'; $b -> save(); 3. 計算該model內有幾筆資料 Book::count() 4. 利用id尋找特定筆資料 Book::find(1) // 尋找id=1的資料 Book::findOrFail(1) // 跟上面一樣,但如果查不到該筆資料會顯示錯誤訊息,而不是null 5. 利用資料欄位值尋找特定資料(如果加上->get()表示不繼續串接query,進行查詢) Book::where('fieldName','value')->get() 6. 上一個的進階查詢(包含特定字串,%的用法同SQL,放的位置不同,效果也不同) Book::where('fieldName','like','value%')->get() 7. orderBy(),以大到小為例 Book::where('fieldName','like','value%')->orderBy('id', 'sortMode')->get() 8. 其他操作,如:加法,平均,最大、最小值 Book::sum('price') => 300 Book::avg('price') => 150.0 Book::max('price') => 200 Book::min('price') => 100 ``` (方法三)軟刪除,程式碼下方有圖示,id=2而不是等於1 ```php= // model use Illuminate\Database\Eloquent\SoftDeletes; use Softdeletes; // migration <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('books', function (Blueprint $table) { // $table->softDeletes(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('books', function (Blueprint $table) { // $table->dropSoftDeletes(); }); } }; ``` ![image](https://hackmd.io/_uploads/Sk3AG05pa.png) (補充)查找被刪除的資料 ```terminal Book::withTrashed()->find(1) ``` ![image](https://hackmd.io/_uploads/H1iDQ0qTT.png) ### 資料表關聯 1. 一對一 ```php= hasOne() <=> belongsTo() ``` 2. 一對多 ```php= hasMany() <=> belongsTo() ``` 3. 多對多 需額外再建置一張資料表用來表示關聯 ```php= belongsToMany() <=> belongsToMany() ``` ### Factory 工廠模式 #### Step1. 創建工廠 ```terminal php artisan make:factory factoryName --model=modelName ``` #### Step2. 設定工廠 ```php= <?php namespace Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory; /** * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Book> */ class BookFactory extends Factory { /** * Define the model's default state. * * @return array<string, mixed> */ public function definition() { return [ // 'title' => $this->faker->name, 'description' => $this->faker->text, 'price' => $this->faker->numberBetween(10, 300), ]; } } ``` #### Step3. 利用faker製作假資料 ```terminal // 要先進入tinker Book::factory()->count(3)->make() // 製作三筆假資料 Book::factory()->make(['title'=>'你好']) // 指定做出來的假資料title欄位值都是'你好' ``` #### Step4. 利用faker產生假資料(進資料庫) ```terminal Book::factory()->count(3)->create() ``` #### 關聯性:一對多 (以商店的角度) ```terminal # 一間店有很多本書,所以我創建一間商店時請他幫我新增三本書 # 前面用hsaMany(),這邊用has Store::factory()->has(Book::factory()->count(3))->create() Store::factory()->hasBooks(3)->create() # 效果同上,但命名要遵循CoC才可以這樣做 ``` (以書的角度) ```terminal # 見一本書,也順便來商店也一起建 # 前面用belongsTo(),這邊用for Book::factory()->for(Store::factory())->create(['title'=>'為自己學']) Book::factory()->forStore()->create(['title'=>'為自己學']) # 效果同上,但命名要遵循CoC才可以這樣做 ``` ### 種子資料 seeder(Database Seeding) (跑預設種子資料)database\seeders\DatabaseSeeder.php ```terminal php artisan db:seed ``` (跑指定種子資料) ```terminal php artisan db:seed --class=seederName ``` (創建種子資料) ```terminal php artisan make:seeder seederName ``` ### 更多migration相關操作參考官方文件:https://laravel.tw/docs/5.2/migrations ## Model(ORM > Active Record > Model) 1. 幫我們與資料庫交互的抽象層 2. 不等於資料庫本身 3. 將資料表物件化,其內容包含欄位,基本操作(新、修、刪、更)、商業邏輯 4. Active Record設計模式的下產物,第三點就是其模式的運作 ## ORM的解釋與模式 from ChatGPT 在軟體工程中,ORM(物件關聯對映)是一種編程技術,用於在關聯型資料庫和面向物件導向語言之間建立映射,從而允許使用面向物件的方式來操作資料庫。 根據使用的方法和模式,ORM 可以分為幾種不同的類型或模式。以下是一些常見的 ORM 模式: 1. **Active Record 模式**: - Active Record 是一種最簡單和最常見的 ORM 模式。在這種模式中,每個資料表對應於一個類(Active Record 類),每個實例(物件)表示資料庫表中的一行資料。 - Active Record 物件負責處理自己的資料持久化和業務邏輯,通常會封裝 CRUD(建立、讀取、更新、刪除)操作。 2. **Data Mapper 模式**: - Data Mapper 是一種更為分離的 ORM 模式。在這種模式中,資料物件(Domain Object)和資料庫操作之間有一個單獨的映射層(Mapper)。 - Data Mapper 負責將資料物件和資料庫表之間進行映射,但資料物件本身不包含任何持久化的邏輯。 3. **Table Data Gateway 模式**: - Table Data Gateway 是一種將資料庫表映射到單個類中的模式。在這種模式中,每個表都有一個對應的 Gateway 類,負責提供對該表的資料操作方法。 - 該模式通常會將資料庫表的每一行資料表示為物件,而每個 Gateway 類封裝了對應表的資料訪問邏輯。 4. **Unit of Work 模式**: - Unit of Work 是一種用於管理事務和資料變更的模式。在 ORM 中,Unit of Work 負責追蹤物件的變更,並在事務提交時將這些變更保存到資料庫中。 這些模式可以單獨使用,也可以結合使用,具體取決於應用程式的需求和開發人員的偏好。不同的 ORM 框架可能會支持不同的模式,或者在其實現中結合多種模式來提供更靈活和強大的功能。 ### CoC(Convention over Configuration)慣例大於設定 #### Model 與 Table的範例(左M右T) Post -> posts Student -> students MyBooK -> my_books #### 可複寫系統慣例,範例如下 情況:M=Post,T=user_posts(正常是要posts) ```php= # 透過更改指向,改變對應table(user_posts表) Protected $table = "user_posts"; ``` #### 可複寫預設主鍵,範例如下(小心使用) ```php= # 將主鍵更改為(post_id) Protected $primaryKey = "post_id"; ``` ## MVC 架構 ![image](https://hackmd.io/_uploads/HJtvacbaT.png) **M,Model資料交互    V,View畫面呈現    C,Controller流程控制** ### 簡單解釋:透過路由(Route->web.php)去選擇C,並透過C去與M、DB索要資料回傳給C,最後再由V呈現 ## Laravel 預設使用'Blade'樣板引擎 ### 詳細參考官方文件: https://laravel.tw/docs/5.3/blade 1. 如果有需要在撰寫的網頁檔使用相關語法,需使用固定格式檔名,格式:'filename.blade.php' 2. 該規則是參考 ASP.NET Razor 3. 盡量使用提供的樣板語法,可避免程式碼混亂情形發生 ## Blade範例 1. {{ $variableName }} ```php= // 後端 $js = '<script>alert("hi");</script>'; // 前端 {{ $js }} // 經過處理,輸出字串 {{!! $js !!}} // 不處理直接執行裡面的程式碼,會有跳窗跑出來 ``` 2. @if...@endif,其他敘述同理如:for、unless(ifnot的意思) ```php= @if(true) 123 @endif ``` 3. for 與 foreach ```php= // foreach @if(count($people) > 0) @foreach($people as $item) <li>{{ $item }}</li> @endforeach @endif // for @if(count($people) > 0) @for($i = 0; $i < count($people); $i++) <li>{{ $people[$i] }}</li> @endfor @endif ``` 4. $loop的使用(first第一筆,last最後一筆,even第偶數筆,odd第奇數筆) ```php= @if(count($people) > 0) @foreach($people as $item) @if($loop -> first || $loop -> last) <li><strong>{{ $item }}</strong></li> @elseif($loop -> even) <li>*{{ $item }}*</li> @elseif($loop -> odd) <li>^{{ $item }}^</li> @else <li>{{ $item }}</li> @endif @endforeach @endif ``` 5. 套用layout 佈局檔 1. @yield('locName') -> 挖空處 ```php= <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>My blog - @yield('title')</title> </head> <body> <main> @yield('main') </main> <footer> @yield('footer') </footer> </body> </html> ``` 目標檔 1. @extends('layouts/layoutname') 選擇哪個佈局檔 2. @section('locName','contents') 填入位置(單行寫法)=>*title*常用 3. @section('locName')...@endsection 多行寫法 ```php= @extends('layouts/application') @section('title', 'blade practice') @section('main') <h1>Hi</h1> <h2>{{ $name }}</h2> {{ $js }} <!--{{!! $js !!}}--> @if(true) 123 @endif @if(count($people) > 0) @foreach($people as $item) <li>{{ $item }}</li> @endforeach @endif <br> <br> @if(count($people) > 0) @for($i = 0; $i < count($people); $i++) <li>{{ $people[$i] }}</li> @endfor @endif <br> <br> @if(count($people) > 0) @foreach($people as $item) @if($loop -> first || $loop -> last) <li><strong>{{ $item }}</strong></li> @elseif($loop -> even) <li>*{{ $item }}*</li> @elseif($loop -> odd) <li>^{{ $item }}^</li> @else <li>{{ $item }}</li> @endif @endforeach @endif @endsection ``` ## Debug ### 找不到目標畫面 1. 檢查web.php有沒有打錯字 2. 檢查檔案位置(抓取網頁檔順序問題public > views > others ) 3. 參數設置問題 4. 檢查Controller裡function回傳的路徑 ### 資料庫問題 1. 檢查.env檔案 2. 使用php artisan migrate工具進行同步or退回前一版本