經歷了將近一個月的分享,筆者已完整地傳達了 Anser 的開發理念,因此在接下來的章節中將會向外擴張一些額外的知識。Anser 在開發的過程中並沒有依賴外部的軟體框架,因此它可以在單純的原生 PHP 環境下透過 Composer 安裝後直接使用。
在很多時候,單純的 PHP 環境是沒有辦法滿足我們的開發需求的。因此本章將示範如何在框架中引入 Anser 並保持架構的整潔。
本章,我們將以 CodeIgniter4 進行示範。
你可以在你喜歡的地方建立起一個資料夾比如:Anser-CodeIgniter4
,接著在底下建立起一個 docker-compose.yml
如下:
version: "3"
services:
app:
image: webdevops/php-nginx-dev:8.1
platform: linux/x86_64
networks:
- anser_project_network
ports:
- 8084:80
working_dir: /web
environment:
- WEB_DOCUMENT_ROOT=/web/ci4/public
- PHP_DISPLAY_ERRORS=1
volumes:
- './:/web'
anser_redis:
image: redis:latest
networks:
- anser_project_network
ports:
- 6379:6379
networks:
anser_project_network:
external: true
這個 docker-compose.yml
基於 Nginx 與 php8.1 ,它與範例微服務一樣使用著同一個 Docker Network。同時,它會將資料夾內的所有檔案掛載到容器內,Nginx 的網頁根目錄則是被鎖定在 /web/ci4/public
裡面。
此時,請在你的終端機中使用 docker compose up -d
啟動容器,再透過 docekr compose exec app bash
進到容器之中,大概像這個樣子:
接著我們就可以透過以下指令取得最新的 CodeIgniter4 框架:
composer create-project codeigniter4/appstarter ci4
上述指令會將 CodeIgniter4 安裝在一個名為 ci4
的資料夾下,在執行後你的資料夾結構與 Command Line 畫面應該會像是這個樣子:
最後,打開你的瀏覽器輸入 localhost:8083
應該可以看到以下畫面:
恭喜,你已經完成安裝 CodeIgniter4 了。
為了在接下來的實作中檢查是否有設定上的失誤,你需要複製 CodeIgniter4 專案根目錄的
env
檔案,並將新的檔案重新命名為.env
,在其中的17
行改為CI_ENVIRONMENT = development
。藉由這個設定,我們將開啟 CodeIgniter4 的錯誤顯示功能,
當你看到了歡迎畫面後,我們緊接著進行 Anser 的安裝設定。在 Command Line 介面中我們得先 cd ci4
進入到 CodeIgniter4 的專案根目錄內。為了確認一切是否正常,你可以先執行 php spark
如果一切正確,你應該能看到以下內容:
緊接著,讓我們來執行 Composer 安裝指令:
composer require sdpmlab/anser
當一切安裝結束後,你可以看看 vendor
中是否包含一個 sdpmlab
資料夾,
讓我們來將 Anser 給整合進 CodeIgniter4 的框架啟動週期內。首先,建立ci4/app/Config/Anser.php
,這支檔案應該長得像這個樣子:
<?php
use SDPMlab\Anser\Service\ServiceList;
use SDPMlab\Anser\Orchestration\Saga\Cache\CacheFactory;
use SDPMlab\Anser\Orchestration\Saga\Cache\Redis\Config;
ServiceList::addLocalService(
name: "ProductionService",
address: "production-service",
port: 8080,
isHttps: false
);
ServiceList::addLocalService(
name: "UserService",
address: "user-service",
port: 8080,
isHttps: false
);
ServiceList::addLocalService(
name: "OrderService",
address: "order-service",
port: 8080,
isHttps: false
);
CacheFactory::initCacheDriver(CacheFactory::CACHE_DRIVER_PREDIS, new Config(
host: "anser_redis",
port: 6379,
db: 1,
serverName: 'AnserCi4Service'
));
你應該能在上述檔案發現,我們去除了原先 Anser-Tutorial-Service/init.php
的 require_once './vendor/autoload.php';
這是因為在 CodeIgniter4 執行的生命週期內,它已經替我們處理了所有的類別自動載入規則,因此我們就不需要再引入任何外部 PHP 程式。
接著,讓我們移動到 ci4/app/Config/Autoload.php
中將我們剛才建立的 ci4/app/Config/Anser.php
給宣告進去,你可以在大約第 87
行的地方看到 $files
變數,並將它改變成這個樣子:
public $files = [
APPPATH . 'Config/Anser.php',
];
此時在 CodeIgniter4 的載入週期內,就已經自動包含了 Anser 的基本設定了。
建立起 ci4/app/Anser
資料夾後,你可以參考 Anser-Tutorial-Service
的設計建立起以下結構:
其中的 .gitkeep
為了是在版本控制系統中能夠保留空白的資料夾。
接著,你可以將前面的章節所練習的相關檔案給移動到這個 CodeIgniter4 的 Anser 資料夾結構中,就像這個樣子:
這邊需要注意的是 namespace
的問題,CodeIgniter4 與其他主流的 PHP 框架一樣採用了 PSR-4 的自動載入標準,因此我們要將這些類別檔案的命名空間給改成與資料夾結構符合的樣子。
比如本來的 Filters
前後的對比會是這樣子:
//Anser-Tutorial-Service
namespace Filters;
//Anser with CodeIgniter4
namespace App\Anser\Filters;
又或者是 :
//Anser-Tutorial-Service
namespace Orchestrators;
namespace Orchestrators\Sagas;
//Anser with CodeIgniter4
namespace App\Anser\Orchestrators;
namespace App\Anser\Orchestrators\Sagas;
當命名空間改變,use
關鍵字引用的類別宣告方式也會隨之改變,以下是已經修改完畢的 CreateOrderOrchestrator
作為示範:
<?php
namespace App\Anser\Orchestrators;
use SDPMlab\Anser\Orchestration\Orchestrator;
use App\Anser\Services\OrderService;
use App\Anser\Services\ProductionService;
use App\Anser\Services\UserService;
use App\Models\DataModel\OrderProductDetail;
use App\Anser\Orchestrators\Sagas\CreateOrderSaga;
class CreateOrderOrchestrator extends Orchestrator
{
//hide
另外,因為 CodeIgniter4 也提供了 log 的管理機制,因此我們需要改變一下 Anser/Filters/FailHandlerFilter.php
的寫法:
$action->failHandler(static function (
ActionException $e
) {
if($e->isClientError()){
$msg = $e->getAction()->getResponse()->getBody()->getContents();
log_message("error", $msg);
$error = json_decode($msg, true)['error'] ?? "unknow error";
$e->getAction()->setMeaningData([
"code" => $e->getAction()->getResponse()->getStatusCode(),
"msg" => $error,
"requestRawBody" => $msg
]);
}else if ($e->isServerError()){
$serverBody = $e->getAction()->getResponse()->getBody()->getContents();
log_message("error", $serverBody);
$e->getAction()->setMeaningData([
"code" => 500,
"msg" => "server error"
]);
}else if($e->isConnectError()){
log_message("error", $e->getMessage());
$e->getAction()->setMeaningData([
"code" => 500,
"msg" => $e->getMessage()
]);
}
});
將原先手動寫入的原生寫法:
file_put_contents(
LOG_PATH . "actionClientErrorlog.txt",
"[" . date("Y-m-d H:i:s") . "] " . $msg . PHP_EOL, FILE_APPEND
);
轉換成框架所提供的 log_message()
機制。
當完成了上述的前置設定後,我們就可以在 CodeIgniter4 中使用 Anser 協作器。讓我們先來建立一個 Controller 作為我們的 API 進入點:
以 Command Line 進入到 CodeIgniter4 根目錄後執行以下指令建立 Controller :
php spark make:controller User
你應該可以看到名為 User.php
的控制器隨著指令的生效被建立起來:
就像你熟悉的那樣,建立一個使用 Anser 協作器的方法:
<?php
namespace App\Controllers;
use App\Controllers\BaseController;
use App\Anser\Orchestrators\UserLoginOrchestrator;
class User extends BaseController
{
public function login()
{
$email = $this->request->getPost('email') ?? '';
$password = $this->request->getPost('password') ?? '';
$orchestrator = new UserLoginOrchestrator();
$result = $orchestrator->build($email, $password);
if($result['success'] == false){
return $this->response->setStatusCode(400)->setJSON($result);
}
return $this->response->setJSON($result);
}
}
在上述程式碼中,我們透過 CodeIgniter4 的 Request 物件取得了 Post 中的 email
與 password
,接著實體化一個 UserLoginOrchestrator
類別,將信箱與密碼傳入後執行這個協作器。透過協作器回傳的內容,判斷該響應何種 HTTP Status Code 給予 Client。
緊接著,讓我們來到 app/Config/Routes.php
增加以下路由設定:
$routes->post('/user/login', 'User::login');
接著,打開你的 Postman 直接進行 API 呼叫:
看起來一切順利,文章的最後讓我們試試看錯誤的帳號密碼:
完美!
本篇文章討論的是如何在 PHP 的 CodeIgniter4 框架中進行 Anser 程式庫的整合,從環境的搭建、安裝,到實際地在具體的專案中應用 Anser 並做到相應的組態設定。大多數的現代化 PHP 框架大同小異,因此你可以參考本篇文章的介紹,依樣畫葫蘆地將 Anser 整合到你的專案之中。