Make More Things the Same

前幾天在網上看到一種設計方式,闡述了一種讓代碼看起來跟擴展性高的一種思維方式,作者透過實際例子描述如何從一開始的設計演化到最後的概念,這樣子的設計概念其實充滿在 Laravel 當中,我認為是相當值得學習的一個作法與思維,以下為實際演示和相關我理解的註解: 首先作者提出了一種原有的代碼設計思路,這邊可以看到示例代碼,用來處理相關 throw 和 report 的機制: class Flaky { protected $throw = true; public function reportFailures() { $this->throw = false; } public function throwFailures() { $this->throw = true; } protected function handle(Exception $e) { $this->throw ? throw $e : report($e); } } 這邊是一個很基礎的設計,可以看到透過呼叫 method 來改變 throw property 來改變最後 handle 的動作。然而當有新的方法,例如 logFailures,就必須調整兩個部分: 新增 logFailures 方法 調整 handle 邏輯 這樣會導致會大幅度去調整原有邏輯跟方法,而沒有一個通用的方法,而且新增的邏輯是有機會跟原本方法不一樣,因此作者透過步驟去分析跟設計,而這樣子的作法也是 Laravel 代碼庫中的實踐方法: class Flaky { protected $handleFailure; public function __construct() { $this->throwFailures(); } public function handleFailures($callback) { // 這邊透過 callback 的行為來增加代碼彈性,使其作用域在內部 $this->handleFailure = $callback; } public function reportFailures() { $this->handleFailures(function($e) { report($e); }); } public function throwFailures() { $this->handleFailures(function($e) { throw $e; }); } // new method public function logFailures() { $this->handleFailures(function($e) { logger($e) }); } protected function handle(Exception $e) { call_user_func($this->handleFailure, $e); } } 透過 handle 去 call handleFailure,除了內部提供的 reportFailures, throwFailures, logFailures,也可以直接呼叫 handleFailure 進行自定義方法:...

March 28, 2023 · Yish

Pest

Pest 對我來說提供了更便捷的測試方式和直譯式的寫法,類似 JS 相關的測試框架,同時又保留了 Laravel 和 PHP 龐大的輔助函數和功能。 official laracasts 主結構 $ composer require pestphp/pest --dev --with-all-dependencies $ ./vendor/bin/pest --init $ ./vendor/bin/pest folders: ├── 📂 tests │ ├── 📂 Unit │ │ └── ExampleTest.php │ └── 📂 Feature │ │ └── ExampleTest.php │ └── TestCase.php │ └── Pest.php ├── phpunit.xml 簡單示例 這邊可以自己添加 phpunit.xml 對應路徑,可以看到 Pest.php 實現細節: 這邊表示注入 TestCase 到 Feature 底下,可以使用 TestCase 裡面所提供的方法,當然也可以注入對應的方法到指定的 folder 底下: uses( Tests\TestCase::class, // Illuminate\Foundation\Testing\RefreshDatabase::class, )->in('Feature'); SumTest...

March 21, 2023 · Yish

Laravel Eloquent Mutators

新版本的 Laravel 提供了一種更便捷的作法來定義 accessor 和 mutator,下面將會比較新舊版本之間的差異,原有方式在新版本當中還是有作兼容,但新的寫法是相對來說更加清晰好懂。 原做法 // 假定 users 有 name: // accessor protected function getNameAttribute($value) { return Str::mask($value, '*', 2); } // mutator protected function setNameAttribute($value) { $this->attributes['name'] = 'Mr.'.$value; } // 要偽裝一個不存在的欄位 accessor protected function getFirstNameAttribute($value) { return ucfirst($this->name); } 新做法 doc,將原有 accessor 和 mutator 綜合為同一個方法進行操作。 // 假定 users 有 name: protected function name(): Attribute { return Attribute::make( get: fn (string $value) => Str::mask($value, '*', 2), set: fn(string $value) => 'Mr....

March 17, 2023 · Yish

Laravel Sail

這個工具是 Laravel 官方所提供使用 docker 作為本地開發環境的 image,這邊紀錄一下關於相關配置和安裝設定。我本地端還是有配置 Laravel Valet,所以很顯然的是我必須調整 port 和相關兼容避免 port aleady in use。 先決配置 Docker 困難修復 開啟 Docker desktop/Resources/WSL integration/Ubuntu (我這邊 WSL 配置 Ubuntu) 進入 Ubuntu 內配置 $ sudo vi /etc/resolv.conf 配置 google nameserver: nameserver 8.8.8.8 安裝步驟 從遠端取得對應 shell script,並且執行 $ curl -s "https://laravel.build/<你的專案名稱>" | bash # curl -s "https://laravel.build/example-app" | bash 執行完畢後 cd 進去 開始配置對應 port -> .env,相關對應變數名稱可以參考 docker-compose.yml: APP_PORT=8088 # http port FORWARD_DB_PORT=33062 # db port FORWARD_MAILPIT_PORT=1026 # mailpit FORWARD_MAILPIT_DASHBOARD_PORT=8026 # mailpit dashboard FORWARD_REDIS_PORT=6380 # redis 拜訪 http://localhost:8088 測試相關對應數據庫連線,查看對應 ....

March 15, 2023 · Yish

自訂 Laravel Query Builder:使用 newEloquentBuilder

Query Builder 是一個強大的工具,用於構建和執行資料庫查詢。有時候我們可能需要更進一步地自訂 Query Builder,以滿足特定的需求。在這篇文章中,我們將深入探討 Laravel 的 Query Builder,並介紹如何使用 newEloquentBuilder 來自訂 Query Builder,以提供更彈性和強大的資料庫查詢功能。 創建一個自訂的ProductBuilder 類別,繼承 Illuminate\Database\Eloquent\Builder use Illuminate\Database\Eloquent\Builder; class ProductBuilder extends Builder { public function available() { return $this->where('status', 'available'); } } 接下來,我們需要在相應的 Product 模型中指定使用自訂的 ProductBuilder,而不是默認的 Builder: use Illuminate\Database\Eloquent\Model; class Product extends Model { /** * Get a new query builder instance for the model. * * @param \Illuminate\Database\Query\Builder $query * @return \App\Builders\ProductBuilder */ public function newEloquentBuilder($query) { return new ProductBuilder($query); } } 我們可以在使用 Product 模型進行查詢時,使用 available() 方法來篩選已上架的產品:...

May 22, 2021 · Yish

遞迴處理多維陣列的 Laravel Collection 方法

在 Laravel 開發中,我們經常需要處理複雜的多維陣列數據。遍歷和操作這些多維陣列可能變得困難且冗長。幸運的是,Laravel 提供了一個強大的 Collection 類,並且我們可以通過自定義方法擴展 Collection 的功能。 新增 recursive 方法到 Laravel Collection 首先,我們需要將 recursive 方法添加到 Laravel 的 Collection 中。這可以通過使用 Collection::macro 方法來實現。這個方法允許我們自定義 Collection 的新方法。以下是 recursive 方法的程式碼: use Illuminate\Support\Collection; Collection::macro('recursive', function () { return $this->map(function ($value) { if (is_array($value) || is_object($value)) { return collect($value)->recursive(); } return $value; }); }); 使用 recursive 方法遞迴處理多維陣列 現在我們已經將 recursive 方法添加到 Laravel Collection 中,我們可以在我們的項目中使用它來遞迴處理多維陣列了。以下是一個示例: $collection = collect([ 'name' => 'John', 'age' => 30, 'address' => [ 'street' => '123 Main St', 'city' => 'New York', 'country' => 'USA', ], ]); $result = $collection->recursive(); $result->dd(); 在上面的示例中,我們創建了一個包含多維陣列數據的 $data 變數。然後,我們將它轉換為 Laravel Collection 並存儲在 $collection 中。接下來,我們使用我們剛剛添加的 recursive 方法對 $collection 進行遞迴處理。最後,我們使用 dd() 函式來輸出處理後的結果。通過新增 recursive 方法到 Laravel Collection,我們可以輕鬆地處理多維陣列數據。遞迴處理多維陣列能夠幫助我們在 Laravel 項目中更有效地遍歷和操作數據。

May 22, 2019 · Yish