# Never 週活動紀錄-from20240403 ###### tags: `活動紀錄` <!-- 設定字體陰影--> <style> /* 多重陰影 */ h1 { text-shadow: 0 0 3px #FF0000, 0 0 5px #0000FF; } h2 { color: white; text-shadow: 1px 1px 2px black, 0 0 25px blue, 0 0 5px darkblue; } /* 環繞邊框 */ h3 { color: coral; text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; } </style> <!-- 設定字體陰影--> ## ==20240404== [準備資安考試-內容網址](https://hackmd.io/rcL-zuQCTiaDTJRBF4W3bQ) - ## 分散式阻斷服務攻擊...等... ## ==20240403== - ## Laravel 外鍵約束 - 使用 Laravel 進行外键约束的示例 假設我們有兩個模型:User和Post,並且Post模型有一個user_id欄位,該欄位是對User模型的外鍵。 User ```js= class User extends Model { protected $fillable = ['name']; public function posts() { return $this->hasMany(Post::class); } } ``` Post ```js= class Post extends Model { protected $fillable = ['title', 'content', 'user_id']; public function user() { return $this->belongsTo(User::class); } } ``` 在Migration檔案中,我們可以新增外鍵約束 ```js= class CreatePostsTable extends Migration { public function up() { Schema::create('posts', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('content'); $table->unsignedBigInteger('user_id'); // 新寫法 $table->foreignId('user_id')->constrained('users')->onDelete('cascade'); // 傳統方式寫法 // $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); $table->timestamps(); }); } ... } ``` 關於在Laravel Migration 檔案中建立外鍵約束 有二種寫法 二個有何不同? - ### 傳統的方式 $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - 使用foreign()方法建立外鍵約束, - 然後透過references()方法指定參考表和參考列(例如所有的id欄位,稱為列(row),因為id是欄位column), - 最後使用onDelete('cascade')指定外鍵的刪除行為。 - ### Laravel 7 及更高版本的新方式 $table->foreignId('user_id')->constrained('users')->onDelete('cascade'); - 使用foreignId()方法直接建立外鍵欄位, - 使用constrained()方法將外鍵約束與特定的表和列進行關聯, - 最後使用onDelete('cascade')指定外鍵的刪除行為。 兩種寫法<font color=red>實現的功能相同</font>,都會在資料庫中建立一個外鍵約束,並且指定了當參考表中的對應記錄被刪除時,與之關聯的記錄也會被級聯刪除。<font color=red>區別在於語法和寫法的簡潔性</font>,新的foreignId()和constrained()方法提供了更簡潔的方式來創建外鍵約束,使程式碼更易讀和易於維護。 - 比較表 | 傳統方式 | 新方式 | |-------------------------------|-------------------------------| | $table->foreign('user_id') | $table->foreignId('user_id') | | ->references('id') | ->constrained('users') | | ->on('users') | | | ->onDelete('cascade'); | ->onDelete('cascade'); | - 各個外鍵約束刪除onDelete(類型)操作類型: 1. **Cascade(級聯)**:當參考表中的相應記錄被刪除時,與該記錄有外鍵關係的當前表中的相應記錄也將被自動刪除。這可以確保數據的一致性,但要小心使用,以免誤刪數據。 2. **Restrict(限制)**:當參考表中的記錄被刪除時,如果在當前表中有相應的外鍵引用,則不允許刪除參考表中的記錄。這樣可以防止意外刪除數據,但需要手動處理相關的外鍵引用問題。 3. **Set Null(設置為空)**:當參考表中的記錄被刪除時,與該記錄有外鍵關係的當前表中的相應外鍵列將被設置為 NULL。這樣做可以避免刪除記錄時的完整性違規,但需要確保相應的欄位允許 NULL 值。 4. **No Action(不採取任何操作)**:當參考表中的記錄被刪除時,不對當前表中的相應外鍵列進行任何操作。這意味著如果存在外鍵關係,則刪除操作將被阻止,直到手動處理相關的外鍵問題。 5. **Set Default(設置默認值)**:當參考表中的記錄被刪除時,與該記錄有外鍵關係的當前表中的相應外鍵列將被設置為其默認值。這樣做可以確保數據的一致性,但需要確保外鍵列有合適的默認值。 這些外鍵約束操作類型提供了不同的行為選擇,可以根據實際應用場景來選擇最合適的操作類型。 - ## @input="$emit('update:modelValue', event.target.value)" ```js= // 定義 props defineProps({ modelValue: { type: [String, Number], required: true, }, }); // 定義 emits defineEmits(['update:modelValue']); ... ; </script> <template> <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" ref="input" /> </template> ``` - **DefineEmits()** <font color=red>會返回一個名為 emit 的函數</font>,用於觸發自定義事件。這個 emit 函數接受一個事件名和一個可選的參數列表,然後將事件派發給當前組件的事件監聽器。 若是 ```js= const x = defineEmits(['update:modelValue']); console.log(x) ``` 會看到 <font color=red>(event, ...args) => instance.emit(event, ...args)</font> 這便是 **emit函數** 如果無法取得 emit 還有一個方式 ```js= const instance = getCurrentInstance(); // 獲取组件實例(instance) const emit = (event, ...args) => instance?.emit(event, ...args); // 從组件實例中獲取 emit 函数 ``` instance 當前组件實例 的內容 ![image](https://hackmd.io/_uploads/HJ_9sAGyC.png) 黃色框框內便是 emit 函數 - 在Vue模板(template)中,$emit是用來觸發自訂事件的方法,而emit是在Vue3 中setup函數中的一個函數參數,用於觸發自訂事件。通常情況下,只需要<font color=red>在模板中使用$emit來觸發自訂事件</font>。 - 對於事件對象,Vue模板(template)中的$event是一個特殊的變量,用於表示目前觸發的事件對象。因此, 模板(template)中,可以使用$event來存取事件物件的屬性和方法,例如$event.target.value用於取得輸入框的值。 - <font color=red>在 Vue.js 的模板中,$event 只能在事件處理器內部使用</font>。換句話說,當處理一個事件時(比如 @click、@input 等),可以使用 $event 來訪問觸發該事件時相關的數據。 - 在Vue的setup函數中,事件物件通常透過函數參數傳遞,因此你可以給參數取任意的名字,通常使用event或其他你喜歡的名稱。所以,在setup函數中,你可以使用event或其他的名字來存取事件物件。 - 在Vue 的setup函數中 這句話 釋疑 如果是使用了 <script setup>,就不再需要明確地聲明 setup 函數,因為 <script setup> <font color=red>會隱式地創建 setup 函數並將它的返回值作為組件的配置對象</font>。在 <script setup> 中,事件處理器會直接接收到事件物件,而不需要額外地在 setup 函數中聲明。 - 所以將 "$emit('update:modelValue', $event.target.value)" 寫成函式的方法 TextInputTry 元件 ```js= <script setup> .... defineProps({ modelValue: { ... }); const emit = defineEmits(['update:modelValue']); ... // 定義 handleInput 函式,用於處理輸入事件 const handleInput = (event) => { const newValue = event.target.value; // 通過 emit 函式觸發 update:modelValue 事件,並將新值作為參數傳遞給該事件 emit('update:modelValue', newValue); }; </script> <template> <input :value="modelValue" @input="handleInput" ref="input" /> </template> ``` 使用 TextInputTry 元件 ```js= ... const x = ref(''); ... <div> <text-input-try :model-value="x" @update:model-value="updateData" /> <p>Parent value: {{ x }}</p> </div> ``` 會發現還是<font color=red>@input="$emit('update:modelValue', $event.target.value)"</font>的寫法簡潔。 - ## watch 與 addEventListener: watch 和 addEventListener 都是用於監聽事件的機制,但它們在使用情境和功能上有所不同: 實際上來說應該說 <font color=red>watch(是監看數據)->數據是看得見的。 listener(是監聽事件)->事件是觸發,觸發是行為,是動作。 很明顯 聽 與 看 就是不同的事情,所以不需要一起比較,應該分開來各自討論。 </font> | 特點 | watch | |------------|-----------------------| | 用於 | <font color=red>監聽 Vue 實例中的數據變化</font>| | 語法 | Vue.js 中的 API | | 使用方式 | 在 Vue 組件的函數中使用 | | 監聽對象 | Vue 實例中的數據變化、computed 屬性| | 回調函數參數 | 新值、舊值 | | 觸發時機 | 監聽的數據發生變化時 | | 主要用於 | Vue 組件中的數據監聽 | | 處理的事件類型| Vue 實例中的數據變化事件| | 特點 | addEventListener | |------------|-----------------------------------| | 用於 | <font color=red>監聽 DOM 元素上的事件</font>| | 語法 | JavaScript 原生方法 | | 使用方式 | 在 JavaScript 中使用 | | 監聽對象 | DOM 元素上的事件(如 click、 input等)| | 回調函數參數 | 事件對象(event object) | | 觸發時機 | 監聽的事件發生時 | | 主要用於 | DOM 事件監聽 | | 處理的事件類型| DOM 元素上的各種事件(如 click、input、mouseover 等) | - ## Instance(實例、實體) 在程式設計中,"實例"(instance)通常指的是根據類別(class)建立的物件。在物件導向程式設計中,類別是一種抽象的概念,描述了具有相同屬性和方法的一組物件的特徵。透過類別可以建立多個物件(object),這些物件被稱為類別的實例。 在Vue.js 中,"實例"(instance)通常指的是Vue 元件的實例。當你建立一個Vue 元件時,你實際上建立了一個Vue 實例,該實例繼承了Vue.js 框架提供的一系列功能,包括資料響應式、生命週期鉤子函數、計算屬性、方法等。這個實例將包含元件的所有邏輯和狀態,並且可以在你的應用程式中被實例化和重複使用。 例如,在Vue 元件中,你可以透過new Vue()或Vue.js 提供的其他方法來建立一個Vue 實例,然後在該實例中定義元件的各種屬性和方法。這個實例會被掛載到你的應用程式中,並負責管理該元件的所有行為和狀態。 總而言之,在程式設計中,"實例"通常指的是根據類別或建構子建立的物件(object),而在Vue.js 中,"實例"通常指的是Vue 元件的實例,它包含了元件的所有邏輯和狀態。 - ## Middleware 權限控制 假設有一組路由需要經由 ```js= Route::prefix('order-service')->middleware(['auth', 'verified', 'role.weight:1|2|3'])->controller(OrderServiceController::class)->group(function () { ... }); ``` 此路由需要透由一個名為 'role.weight' 的 middleware,並傳遞 '1|2|3'(權限為1或2或3)的參數,此處的'role.weight'其實是一個別名,我們建立一個的是一個名為RroleWeight的middleware。 - 1. ### 自建middleware 首先需要先另外建立middleware **$ php artisan make:middleware RoleWeight** 將在 app/Http/Middleware 目錄下建立一個名為 RoleWeight 的中介軟體類別。 - 2. ### 註冊中介軟體 $middleware 及 中介軟體群組 $middlewareGroups - 請確保在 $middleware 中僅註冊<font color=red>不需要參數的<font size=4>**全域**</font>中介軟體</font>,而將<font color=red>需要參數的中介軟體註冊到 middlewareGroups 中</font>。 - 註冊中介軟體$middleware: 在 Laravel 中,$middleware 屬性是用於註冊全域</font>中介軟體的,這些中介軟體將應用於每個 HTTP 請求,<font color=red>而不是在特定路由群組中</font>。如果您在 $middleware 中註冊了中介軟體,並且這些中介軟體<font color=red>需要額外的參數(如您的 RoleWeight 中介軟體),**將會導致錯誤**</font>,因為全域中介軟體不接受參數。 在 **app/Http/Kernel.php** 檔案的 $middleware 陣列中註冊中介軟體。 ```js= protected $middleware = [ // 其他中介軟體... 'role' => \App\Http\Middleware\RoleWeight::class, ]; ``` - 中介軟體群組 $middlewareGroups 如果您想要在路由群組中使用中介軟體<font color=red>並且該中介軟體需要參數</font>,則應該將它註冊到 middlewareGroups 中,而不是 $middleware。 在 **app/Http/Kernel.php** 檔案的 $middlewareGroups 陣列中註冊中介軟體。 ```js= protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, ... ], 'api' => [ 'throttle:api', ... ], // 自定義中介軟體組 'custom' => [ \App\Http\Middleware\RoleWeight::class, // 其他自定義中介軟體... ], ] ``` - 3. ### 註冊中介軟體別名 $middlewareAliases $middlewareAliases 屬性用於註冊中介軟體別名,因此可以認為在這裡添加別名也是一種註冊行為。這些別名允許在路由定義中使用更簡潔的方式來引用中介軟體。 當在 $middlewareAliases 中註冊了中介軟體的別名後,可以在路由定義中使用這些別名,而不必每次都寫完整的中介軟體類名。這樣可以使代碼更加簡潔、易讀且易於維護。 ```js= protected $routeMiddleware = [ 'auth' => \Illuminate\Auth\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, ... // 權限中介軟體別名... 'role.weight' => \App\Http\Middleware\RoleWeight::class, ]; ``` 注意到 是用小寫並且用 '.' 隔開兩個單字 如果想命名 'RoleWeight' 則以 'role.weight' 當別名,可以參看上方原本就有的 'auth.basic' 並不一定要與原來命名 'AuthenticateWithBasicAuth' 都相同。 - 4. ### $middleware 傳參數 此路由透由一個別名為 'role.weight' 的 middleware 傳遞 '1|2|3'(權限為1或2或3)的參數。 **...->middleware(['role.weight<font color=red>:1|2|3'</font>])->...** - 5. ### middleware 接收權限參數及處理權限 要讓中介軟體接收參數,您可以在定義中介軟體時,在 handle 方法中添加額外的參數。然後,在路由定義中將這些參數傳遞給中介軟體。 範例: - 剛建立middleware時 ```js= class RoleWeight { public function handle($request, Closure $next) { return $next($request); } } ``` - <font color=red>**自己添加參數 $Roles**</font> ```js= class RoleWeight { public function handle($request, Closure $next, $roles) { // 在這裡檢查使用者的角色是否符合要求 // 您可以使用 $role 變數來檢查特定角色 // 如果使用者不符合要求,您可以返回相應的響應或重定向 return $next($request); } } ``` - <font color=red>**自己視需求添加處理過程**</font> - <font color=red>**需要切割**輸入參數為一陣列</font> ```js= class RoleWeight { public function handle($request, Closure $next, $roles) { $userRole = request->user()?->role(); // 假設您有一個方法可以取得使用者的角色 $allowedRoles = explode('|', $roles); //切割所有允許角色成陣列 if (in_array($userRole, $allowedRole)) { return $next($request); } return redirect()->route('login'); // 或者根據您的應用程式需求返回其他響應 } } ``` - <font color=red>**不需要切割**輸入參數為一陣列</font> 此處需要將傳入的字串參數切割成一陣列,所以不方便,還可以進一步,修改成不需要切割的方式。 修改使用 PHP 5.6 以後引入的<font color=red>可變長度引數 (...$roles)</font>。這允許您在路由中直接指定多個角色,而無需在中介軟體中進行字串分割。 當您使用可變長度引數時,請確保在函數調用時<font color=red>使用逗號將每個參數分隔開</font>,以便 PHP 正確地將它們解析<font color=red>並收集到陣列中</font>。 ```js= class RoleWeight { public function handle($request, Closure $next, ...$roles) { $userRole = $request->user()?->role(); // 假設您有一個方法可以取得使用者的角色 if (in_array($userRole, $roles)) { return $next($request); } return redirect()->route('login'); // 或者根據您的應用程式需求返回其他響應 } } ``` 參數可直接傳入1,2,3;其中參數以,隔開 ```laravel= Route::group(['prefix' => 'admin','middleware' => ['role.weight:1,2,3']], function() { ... }); ``` - ## PHP 可變長度引數(variable-length argument)的語法 可以在函數或方法的參數列表中使用 ... 來接受可變數量的參數,這些參數將被收集到一個陣列中。 使用可變長度引數 (...$args) 的情況,需要在函數調用時使用逗號將參數分隔開。這樣 PHP 才能將這些參數正確地收集到陣列中 - 範例 Laravel 中 ```laravel= function myFunction(...$args) { var_dump($args); } ``` 呼叫並傳遞任意數量的參數: myFunction('apple', 'banana', 'orange'); - ## $validator 在Laravel 的表單驗證過程中,$validator物件在驗證失敗時<font color=red>會停止請求繼續執行</font>,而在驗證成功時不會對請求產生影響,也不會修改請求資料。 因此,即使沒有使用$validated,您仍然可以直接使用 $request->all() 或 $request->input() 來獲取請求資料 或者使用 $validator->validated() 也可以。 一般 $validator 範例 ```laravel= use Illuminate\Http\Request; use Illuminate\Support\Facades\Validator; class UserController extends Controller { public function store(Request $request) { // 定義驗證規則 $validator = Validator::make($request->all(), [ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'email', 'max:255', 'unique:users'], 'password' => ['required', 'string', 'min:8', 'confirmed'], ]); // 執行驗證 if ($validator->fails()) { return redirect()->back() ->withErrors($validator) ->withInput(); } // 如果驗證通過,直接在這裡使用 $request->all() 或 $request->input() // 或者使用 $validator->validated() 也可以 // 獲取通過驗證的 'name' 欄位的值 // $name = $validated['name']; // 獲取通過驗證的 'email' 欄位的值 // $email = $validated['email']; // 獲取通過驗證的 'password' 欄位的值 // $password = $validated['password']; $data = $request->all(); // 在這裡使用請求數據進行其他操作,例如創建用戶 return redirect('/')->with('success', '用戶創建成功!'); } } ``` - ### 使用Laravel 完整的 $validator 驗證器來驗證請求資料 validator = Validator::make($input, $rules, $messages, $naem); Validator::make():這是Laravel 提供的用於建立驗證器實例的靜態方法。 它接受四個參數: - 請求資料($input): 是要驗證的數 。 - 驗證規則($rules): 是驗證規則。 - 自訂錯誤訊息($messages): 是自訂錯誤訊息,用於覆蓋預設的驗證錯誤訊息。 - 屬性名稱($attributes): 是屬性名稱。 範例 ```laravel= $validator = Validator::make( //第一個參數 請求資料 $request->all(), [ 'name' => ['required', 'string', 'max:255'], 'user_address' => ['string', 'max:255'], 'user_phone' => ['string', 'max:255'], 'user_bank' => ['string', 'max:255'], ], [ 'required' => ':attribute 不能留空', 'max' => ':attribute 不能超過255個字', ], [ 'name' => '名稱', 'user_address' => '通訊地址', 'user_phone' => '電話', 'user_bank' => '轉帳資料', ] ); ``` - 第一個參數 請求資料 $input: 是要驗證的數 若使用 $request->all():這個方法傳回請求中的所有數據,以關聯數組的形式。 - 第二個參數是驗證規則,它指定了要對請求資料進行的驗證。 在這個例子中,'name'、'user_address'、'user_phone' 和'user_bank' 欄位都分別具有一組驗證規則。 - 第三個參數是自訂錯誤訊息。在這個陣列中,可以為每個驗證規則指定自訂的錯誤訊息。例如,'required' 規則的自訂錯誤訊息是':attribute 不能留空',其中':attribute' 是一個佔位符,會被實際的欄位名稱取代。 - 第四個參數是屬性名稱數組,它用於指定每個欄位的自訂名稱。這樣做的目的是為了在錯誤訊息中使用更友善的欄位名稱。