PHP code example of sdpmlab / anser

1. Go to this page and download the library: Download sdpmlab/anser library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

sdpmlab / anser example snippets


namespace App\Anser\Config;

use SDPMlab\Anser\Service\ServiceList;

ServiceList::addLocalService("order_service","localhost",8080,false);
ServiceList::addLocalService("product_service","localhost",8081,false);
ServiceList::addLocalService("cart_service","localhost",8082,false);
ServiceList::addLocalService("payment_service","localhost",8083,false);

namespace App\Anser\Services;

use SDPMlab\Anser\Service\SimpleService;
use SDPMlab\Anser\Service\ActionInterface;
use SDPMlab\Anser\Exception\ActionException;
use Psr\Http\Message\ResponseInterface;

class OrderService extends SimpleService
{
    protected $serviceName = "order_service";
    protected $retry      = 1;
    protected $retryDelay = 1;
    protected $timeout    = 10.0;

    /**
     * Get order by order_key
     *
     * @param integer $u_key
     * @param string $order_key
     * @return ActionInterface
     */
    public function getOrder(
        int $u_key,
        string $order_key
    ): ActionInterface {
        $action = $this->getAction("GET", "/api/v2/order/{$order_key}")
            ->addOption("headers", [
                    "X-User-Key" => $u_key
                ])
            ->doneHandler(
                function (
                    ResponseInterface $response,
                    ActionInterface $action
                ) {
                    $resBody = $response->getBody()->getContents();
                    $data    = json_decode($resBody, true);
                    $action->setMeaningData($data["data"]);
                }
            )
            ->failHandler(
                function (
                    ActionException $e
                ) {
                    log_message("critical", $e->getMessage());
                    $e->getAction()->setMeaningData([
                        "message" => $e->getMessage()
                    ]);
                }
            );
        return $action;
    }

    /**
     * Create order
     *
     * @param integer $u_key
     * @param integer $p_key
     * @param integer $amount
     * @param integer $price
     * @param string $orch_key
     * @return ActionInterface
     */
    public function createOrder(
        int $u_key,
        int $p_key,
        int $amount,
        int $price,
        string $orch_key
    ): ActionInterface {
        $action = $this->getAction("POST", "/api/v2/order")
            ->addOption("json", [
                "p_key"  => $p_key,
                "price"  => $price,
                "amount" => $amount
            ])
            ->addOption("headers", [
                "X-User-Key" => $u_key,
                "Orch-Key"   => $orch_key
            ])
            ->doneHandler(
                function (
                    ResponseInterface $response,
                    ActionInterface $action
                ) {
                    $resBody = $response->getBody()->getContents();
                    $data    = json_decode($resBody, true);
                    $action->setMeaningData($data["orderID"]);
                }
            )
            ->failHandler(
                function (
                    ActionException $e
                ) {
                    log_message("critical", $e->getMessage());
                    $e->getAction()->setMeaningData([
                        "message" => $e->getMessage()
                    ]);
                }
            );
        return $action;
    }

    /**
     * Delete order
     *
     * @param string $order_key
     * @param string $u_key
     * @param string $orch_key
     * @return ActionInterface
     */
    public function deleteOrderByOrchKey(
        string $u_key,
        string $orch_key
    ): ActionInterface {
        $action = $this->getAction('DELETE', "/api/v2/order")
            ->addOption("headers", [
                "X-User-Key" => $u_key,
                "Orch-Key"   => $orch_key
            ])
            ->doneHandler(
                function (
                    ResponseInterface $response,
                    Action $action
                ) {
                    $resBody = $response->getBody()->getContents();
                    $data    = json_decode($resBody, true);
                    $action->setMeaningData($data["data"]);
                }
            )
            ->failHandler($this->getFailHandler());
        return $action;
    }

    /**
     * Fail handler
     * 
     * @return callable
     */
    protected function getFailHandler(): callable {
        return function (
            ActionException $e
        ) {
            log_message("critical", $e->getMessage());
            $e->getAction()->setMeaningData([
                "message" => $e->getMessage()
            ]);
        };
    }

}



namespace App\Anser\Orchestrators\V2;

use App\Anser\Sagas\V2\CreateOrderSaga;
use SDPMlab\Anser\Orchestration\Orchestrator;
use App\Anser\Services\V2\ProductService;
use App\Anser\Services\V2\PaymentService;
use App\Anser\Services\V2\OrderService;
use Exception;
use SDPMlab\Anser\Orchestration\OrchestratorInterface;
use SDPMlab\Anser\Orchestration\Saga\Cache\CacheFactory;

class CreateOrderOrchestrator extends Orchestrator
{
    /**
     * The service of product.
     *
     * @var ProductService
     */
    protected ProductService $productService;

    /**
     * The service of payment.
     *
     * @var PaymentService
     */
    protected PaymentService $paymentService;

    /**
     * The service of order.
     *
     * @var OrderService
     */
    protected OrderService $orderService;

    /**
     * The user key of this orchestrator.
     *
     * @var string
     */
    public $user_key = null;

    /**
     * The product information.
     *
     * @var array
     */
    public array $product_data = [];

    /**
     * The order key.
     *
     * @var integer
     */
    public $order_key;

    /**
     * The product key.
     *
     * @var integer
     */
    public $product_key;

    /**
     * The product price * amount.
     *
     * @var int
     */
    public $total = 0;

    /**
     * The product amount.
     *
     * @var integer
     */
    public $product_amount = 0;

    /**
     * The payment key.
     *
     * @var string|null
     */
    public $payment_key = null;

    public function __construct()
    {
        $this->productService = new ProductService();
        $this->paymentService = new PaymentService();
        $this->orderService   = new OrderService();
    }

    protected function definition(int $product_key = null, int $product_amount = null, int $user_key = null)
    {
        if (is_null($product_key) || is_null($user_key) || is_null($product_amount)) {
            throw new Exception("The parameters of product or user_key fail.");
        }

        $this->user_key       = $user_key;
        $this->product_amount = $product_amount;
        $this->product_key    = $product_key;

        $this->setServerName("Anser_Server_1");

        // Step 1. Check the product inventory balance.
        $step1 = $this->setStep()->addAction(
            "product_check",
            $this->productService->checkProductInventory($product_key, $product_amount)
        );

        // Step 2. Get product info.
        $step2 = $this->setStep()->addAction(
            "get_product_info",
            $this->productService->getProduct($product_key)
        );

        // Step 3. Check the user wallet balance.
        $step3 = $this->setStep()
        ->addAction(
            "wallet_check",
            // Define the closure of step3.
            static function (
                OrchestratorInterface $runtimeOrch
            ) use (
                $user_key,
                $product_amount
            ) {
                $product_data = $runtimeOrch->getStepAction("get_product_info")->getMeaningData();
                $total        = $product_data["price"] * $product_amount;

                $runtimeOrch->product_data = &$product_data;
                $runtimeOrch->total        = $total;

                $action = $runtimeOrch->paymentService->checkWalletBalance($user_key, $runtimeOrch->total);
                return $action;
            }
        );

        // Start the saga.
        $this->transStart(CreateOrderSaga::class);

        // Step 4. Create order.
        $step4 = $this->setStep()
        ->setCompensationMethod("orderCreateCompensation")
        // Define the closure of step4.
        ->addAction(
            "create_order",
            static function (
                OrchestratorInterface $runtimeOrch
            ) use (
                $user_key,
                $product_amount,
                $product_key
            ) {
                return $runtimeOrch->orderService->createOrder(
                    $user_key,
                    $product_key,
                    $product_amount,
                    $runtimeOrch->product_data["price"],
                    $runtimeOrch->getOrchestratorNumber()
                );
            }
        );

        // Step 5. Create payment.
        $step5 = $this->setStep()
        ->setCompensationMethod("paymentCreateCompensation")
        ->addAction(
            "create_payment",
            // Define the closure of step5.
            static function (
                OrchestratorInterface $runtimeOrch
            ) use (
                $user_key,
                $product_amount
            ) {
                $order_key = $runtimeOrch->getStepAction("create_order")->getMeaningData();

                $runtimeOrch->order_key = $order_key;

                $action = $runtimeOrch->paymentService->createPayment(
                    $user_key,
                    $runtimeOrch->order_key,
                    $product_amount,
                    $runtimeOrch->total,
                    $runtimeOrch->getOrchestratorNumber()
                );

                return $action;
            }
        );

        // Step 6. Reduce the product inventory amount.
        $step6 = $this->setStep()
        ->setCompensationMethod("productInventoryReduceCompensation")
        ->addAction(
            "reduce_product_amount",
            // Define the closure of Step 6.
            static function (
                OrchestratorInterface $runtimeOrch
            ) use ($product_key, $product_amount) {
                $payment_key = $runtimeOrch->getStepAction("create_payment")->getMeaningData();

                $runtimeOrch->payment_key = $payment_key;

                return $runtimeOrch->productService->reduceInventory(
                    $product_key,
                    $product_amount,
                    $runtimeOrch->getOrchestratorNumber()
                );
            }
        );

        // Step 7. Reduce the user wallet balance.
        $step7 = $this->setStep()
        ->setCompensationMethod("walletBalanceReduceCompensation")
        ->addAction(
            "reduce_wallet_balance",
            // Define the closure of step 7.
            static function (
                OrchestratorInterface $runtimeOrch
            ) use ($user_key) {
                return $runtimeOrch->paymentService->reduceWalletBalance(
                    $user_key,
                    $runtimeOrch->total,
                    $runtimeOrch->getOrchestratorNumber()
                );
            }
        );

        $this->transEnd();
    }

    protected function defineResult()
    {
        $data["data"] = [
            "status"    => $this->isSuccess(),
            "order_key" => $this->order_key,
            "product_data" => $this->product_data,
            "total"        => $this->total
        ];

        if (!$this->isSuccess()) {
            $data["data"]["isCompensationSuccess"] = $this->isCompensationSuccess();
        }

        return $data;
    }
}


namespace App\Anser\Sagas\V2;

use SDPMlab\Anser\Orchestration\Saga\SimpleSaga;
use App\Anser\Services\V2\OrderService;
use App\Anser\Services\V2\ProductService;
use App\Anser\Services\V2\PaymentService;

class CreateOrderSaga extends SimpleSaga
{
    /**
     * The Compensation function for order creating.
     *
     * @return void
     */
    public function orderCreateCompensation()
    {
        $orderService = new OrderService();

        $orchestratorNumber = $this->getOrchestrator()->getOrchestratorNumber();
        $user_key  = $this->getOrchestrator()->user_key;

        $orderService->deleteOrderByRuntimeOrch($user_key, $orchestratorNumber)->do();
    }

    /**
     * The Compensation function for product inventory reducing.
     *
     * @return void
     */
    public function productInventoryReduceCompensation()
    {
        $productService = new ProductService();

        $orchestratorNumber = $this->getOrchestrator()->getOrchestratorNumber();
        $product_amount     = $this->getOrchestrator()->product_amount;

        // It still need the error condition.
        // It will compensate the product inventory balance Only if the error code is 5XX error.

        $productService->addInventoryByRuntimeOrch($product_amount, $orchestratorNumber)->do();
    }

    /**
     * The Compensation function for user wallet balance reducing.
     *
     * @return void
     */
    public function walletBalanceReduceCompensation()
    {
        $paymentService = new PaymentService();

        $orchestratorNumber = $this->getOrchestrator()->getOrchestratorNumber();
        $user_key = $this->getOrchestrator()->user_key;
        $total    = $this->getOrchestrator()->total;

        // It still need the error condition.
        // It will compensate the wallet balance Only if the error code is 5XX error.

        $paymentService->increaseWalletBalance($user_key, $total, $orchestratorNumber)->do();
    }

    /**
     * The Compensation function for payment creating.
     *
     * @return void
     */
    public function paymentCreateCompensation()
    {
        $paymentService = new PaymentService();

        $orchestratorNumber = $this->getOrchestrator()->getOrchestratorNumber();
        $payment_key = $this->getOrchestrator()->payment_key;
        $user_key    = $this->getOrchestrator()->user_key;

        $paymentService->deletePaymentByRuntimeOrch($user_key, $orchestratorNumber)->do();
    }
}

use App\Anser\Orchestrators\V2\CreateOrderOrchestrator;

class CreateOrderController extends BaseController
{
    use ResponseTrait;

    public function createOrder()
    {
        $data = $this->request->getJSON(true);

        $product_key    = $data["product_key"];
        $product_amout  = $data["product_amout"];
        $user_key       = $this->request->getHeaderLine("X-User-Key");

        $userOrch = new CreateOrderOrchestrator();

        $result   = $userOrch->build($product_key, $product_amout, $user_key);

        return $this->respond($result);
    }
}