Swoft 2.x 基礎

1. Http Server;

1.1 控制器;

  • 包含:RequestMapping 註釋、請求與響應、返回 JSON 格式、路由 path 參數、GET \ POST 參數獲取
  • PhpStorm 安裝插件
    在這裏插入圖片描述
  • 新建 App/Http/Controller/ProductController.php
<?php

namespace App\Http\Controller;

use App\lib\MyRequest;
use function foo\func;
use Swoft\Context\Context;
use Swoft\Http\Message\ContentType;
use Swoft\Http\Message\Response;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;
use Swoft\Http\Server\Annotation\Mapping\Middleware;
use App\Http\Middleware\ControllerMiddleware;

/**
 * 商品模塊
 * // @Controller()
 * @Controller(prefix="/product")
 * 使用一箇中間件
 * @Middleware(ControllerMiddleware::class)
 */
class ProductController{

    /**
     * 1.1.1 RequestMapping 註釋
     *
     * @RequestMapping(route="/product", method={RequestMethod::GET})
     */
    public function prod_list(){
        // 1.1.2 請求與響應
        // 參考:https://www.swoft.org/documents/v2/core-components/http-server/#http-1
        // 上下文對象:Contexts
        // 獲取請求對象
        $req = Context::mustGet()->getRequest();
        // 獲取響應對象
        $res = Context::mustGet()->getResponse();
        // 返回內容
        // return $res->withContent("abc");
        // 1.1.3 返回 JSON 格式
        // return $res->withContentType("application/json")->withContent("abc");

        // 1.2 調用全局函數
        // return $res->withContentType("application/json")
        //    ->withContent(json_encode([NewProduct(101, "測試商品"), NewProduct(102, "測試商品2")]));
        return $res->withContentType("application/json")
            ->withData([NewProduct(101, "測試商品"), NewProduct(102, "測試商品2")]);
    }


    /**
     * //@RequestMapping(route="/product/{pid}", method={RequestMethod::GET})
     * 路由 path 參數前綴,正則控制路由參數
     * 注入參數 Response,可能或造成參數過多
     * 1.1.4 路由 path 參數
     * @RequestMapping(route="{pid}", params={"pid"="\d+"}, method={RequestMethod::GET, RequestMethod::POST})
     */
    public function prod_detail(int $pid, Response $response){
        $p = NewProduct($pid, "測試商品");
        //return \response()->withContentType("application/json")->withData([$p]);
        // return $response->withContentType(ContentType::JSON)->withData([$p]);
        // 1.3 此處返回值會調用中間件
        // return [$p];

        // 1.1.5 GET \ POST 參數獲取
        // echo request()->get("type", "default type");
        // if(request()->getMethod() == RequestMethod::GET){

        // *1.5 JSON 參數轉實體對象
        /** @var  $product  ProductEntity */
        // $product = jsonForObject(ProductEntity::class);
        // var_dump($product);

        if(isGet()){
            return $p;
        }else if(isPost()) {
            $p->pname = "修改產品" . request()->post('title', 'none');
            return $p;

        }

        // *1.4 鏈式調用
//        $my = new MyRequest();
//        $my->if(isGet())
//            ->then(function() use ($p){
//                return $p;
//            })
//            ->if(isPost())
//            ->then(function() use ($p){
//                $p->pname = "修改產品" . request()->post('title', 'none');
//                return $p;
//            })
//            ->getResult();
    }

}
  • 實例操作
# 終端進入項目目錄
cd /data/test/php/swoft/
# 啓動 Swoft
php ./bin/swoft http:start
# 返回
 SERVER INFORMATION(v2.0.8)
  ********************************************************************************
  * HTTP     | Listen: 0.0.0.0:18306, Mode: Process, Worker: 6, Task worker: 12
  ********************************************************************************

HTTP Server Start Success!

# 瀏覽器訪問:http://localhost:18306/product

1.2 全局函數;

  • 修改 App/Helper/Functions.php
<?php

use Swoft\Http\Server\Annotation\Mapping\RequestMethod;

require_once (__DIR__ . "/WebFunctions.php");
require_once (__DIR__ . "/OrderFunctions.php");

/**
 * This file is part of Swoft.
 *
 * @link     https://swoft.org
 * @document https://swoft.org/docs
 * @contact  [email protected]
 * @license  https://github.com/swoft-cloud/swoft/blob/master/LICENSE
 */

function user_func(): string
{
    return 'hello';
}


function NewProduct(int $pid, string $pname){
    $p = new stdClass();
    $p->pid = $pid;
    $p->pname = $pname . $p->pid;

    return $p;
}


function response($contentType=false){
    if($contentType){
        return Swoft\Context\Context::mustGet()->getResponse()->withContentType($contentType);
    }
    return Swoft\Context\Context::mustGet()->getResponse();
}


function request(){
    return Swoft\Context\Context::mustGet()->getRequest();
}


function isGet(){
    return request()->getMethod() == RequestMethod::GET;
}


function isPost(){
    return request()->getMethod() == RequestMethod::POST;
}


function ip(){
    $req = request();
    if($req->server('http_x_forwarded_for')){
        return $req->server('http_x_forwarded_for');
    }else if($req->server('http_client_ip')){
        return $req->server('http_client_ip');
    }else{
        $req->server('remote_addr');
    }
}


// 事務封裝(也可以用 AOP)
function tx(callable $func, &$result=null){
    \Swoft\Db\DB::beginTransaction();
    try{
        $result = $func();
        \Swoft\Db\DB::commit();
    }catch(Exception $exception){
        $result = OrderCreateFail($exception->getMessage());
        \Swoft\Db\DB::rollBack();
    }
}

1.3 中間件;

<?php

namespace App\Http\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Http\Message\Request;
use Swoft\Http\Message\Response;
use Swoft\Http\Server\Contract\MiddlewareInterface;
use Swoft\Http\Message\ContentType;

/**
 * @Bean()
 */
class ControllerMiddleware implements MiddlewareInterface
{

    /**
     * Process an incoming server request.
     *
     * @param ServerRequestInterface|Request $request
     * @param RequestHandlerInterface|Response $handler
     * 放到控制器裏面進行對應,也可以設置全局的中間件
     *
     * @return ResponseInterface
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        /** @var  $ret  \Swoft\Http\Message\Response */
        // response 對象
        $ret = $handler->handle($request);
        $data = $ret->getData();
        //$p2 = NewProduct(10000, "中間件測試商品");
        //$data[] = $p2;

        if(is_object($data)){
            return \response(ContentType::JSON)->withContent(json_encode($data));
        }

        return \response(ContentType::JSON)->withData($data);
    }
}

*1.4 鏈式調用;

  • 新建 App\lib\MyRequest.php
<?php

namespace App\lib;

class MyRequest{

    private $result;

    private $do = false;

    function if($bool){
        $this->do = $bool;
        return clone $this;
    }

    function then(callable $func){
        if($this->do){
            $this->result = $func();
            $this->do = !$this->do;
        }
        return clone $this;
    }

    function getResult(){
        return $this->result;
    }

}

*1.5 JSON 參數轉實體對象;

  • 新建 App\lib\ProductEntity.php
<?php

namespace App\lib;

class ProductEntity{

    private $prod_id;

    private $prod_name;

    private $prod_price;

    /**
     * @return mixed
     */
    public function getProdId(){
        return $this->prod_id;
    }

    /**
     * @param mixed $prod_id
     */
    public function setProdId($prod_id): void{
        $this->prod_id = $prod_id;
    }

    /**
     * @return mixed
     */
    public function getProdName(){
        return $this->prod_name;
    }

    /**
     * @param mixed $prod_name
     */
    public function setProdName($prod_name): void{
        $this->prod_name = $prod_name;
    }

    /**
     * @return mixed
     */
    public function getProdPrice(){
        return $this->prod_price;
    }

    /**
     * @param mixed $prod_price
     */
    public function setProdPrice($prod_price): void{
        $this->prod_price = $prod_price;
    }
}
  • 新建 App/Helper/WebFunction.php
<?php

/**
 * @param $class
 * $class 不是實例化對象,而是 function 名稱
 * 需要用到反射
 */
function jsonForObject($class=""){
    $req = request();
    try {
        $contentType = $req->getHeader('content-type');
        if (!$contentType || false === stripos($contentType[0], \Swoft\Http\Message\ContentType::JSON)) {
            return false;
        }

        // 獲取原始的 body 內容
        $raw = $req->getBody()->getContents();
        // 把得到的對象和 $class 做比對
        $map = json_decode($raw, true); // kv 數組
        if($class == "") return $map;   // 實現 2.0.2 版本前的 request()->json() 方法

        $class_obj = new ReflectionClass($class);   // 反射對象
        $class_instance = $class_obj->newInstance(); // 根據反射對象創建實例
        // 獲取所有方法(公共方法)
        $methods = $class_obj->getMethods(ReflectionMethod::IS_PUBLIC);
        foreach ($methods as $method){
            // 正則獲取 set 方法
            if(preg_match("/set(\w+)/", $method->getName(), $matches)){
                // echo $matches[1] . PHP_EOL; // 得到方法名 ProdId,ProdName,ProdPrice
                invokeSetterMethod($matches[1], $class_obj, $map,$class_instance);
            }
        }

        return $class_instance;
    }catch (Exception $exception){
        return false;

    }
}

// 把數組映射成實體(一維數組)
function mapToModel(array $map, $class){
    try {
        $class_obj = new ReflectionClass($class);
        $class_instance = $class_obj->newInstance(); // 根據反射對象創建實例
        // 獲取所有方法(公共方法)
        $methods = $class_obj->getMethods(ReflectionMethod::IS_PUBLIC);
        foreach ($methods as $method){
            // 正則獲取 set 方法
            if(preg_match("/set(\w+)/", $method->getName(), $matches)){
                // echo $matches[1] . PHP_EOL; // 得到方法名 ProdId,ProdName,ProdPrice
                invokeSetterMethod($matches[1], $class_obj, $map,$class_instance);
            }
        }
        return $class_instance;

    } catch (Exception $exception){
        return null;
    }
}


// 二維數組
// $fill 填充新字段
// $toarray=false 返回實體數組
function mapToModelsArray(array $maps, $class, $fill=[], $toarray=false){
    $ret = [];
    foreach ($maps as $map){
        $getObject = mapToModel($map, $class);
        if($getObject){
           if($fill && count($fill) > 0){
               $getObject->fill($fill);
           }
           if($toarray){
               // 數組
               $ret[] = $getObject->getModelAttributes();
           }else{
               // 實體對象
               $ret[] = $getObject;
           }
        }
    }
    return $ret;
}


function invokeSetterMethod($name, ReflectionClass $class_obj, $jsonMap, &$class_instance){
    // 把 ProdId 轉化成 Prod_Id
    $filter_name = strtolower(preg_replace("/(?<=[a-z])([A-Z])/", "_$1", $name));
    // 兼容 swoft
    // 把 ProdId 轉化成 prodId
    $filter_name_ForSwoft = lcfirst($name);
    $props = $class_obj->getProperties(ReflectionProperty::IS_PRIVATE);
    foreach ($props as $prop) {
        // 存在對應的私有屬性
        if(strtolower($prop->getName()) == $filter_name || $prop->getName() == $filter_name_ForSwoft){
            $method = $class_obj->getMethod("set" . $name);
            $args = $method->getParameters(); // 取出參數
            if(count($args) == 1 && isset($jsonMap[$filter_name])){
                // 對實例對象進行賦值(通過引用)
                $method->invoke($class_instance, $jsonMap[$filter_name]);
            }
        }
    }
}

2. 數據庫;

2.1 基本配置;

<?php
// 設置配置文件如下
db' => [
    'class'    => Database::class,
    'dsn'      => 'mysql:dbname=test;host=127.0.0.1',
    'username' => 'root',
    'password' => 'asdf',
    'charset' => 'utf8mb4',
    'config'   => [
        'collation' => 'utf8mb4_unicode_ci',
        'strict'    => true,
        'timezone'  => '+8:00',
        'modes'     => 'NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES',
        'fetchMode' => PDO::FETCH_ASSOC
    ]
],
# 連接池主要作用:保護數據庫
# 有內置連接池需要自己調控
 'db.pool' => [
    'class'    => Pool::class,
    'database' => bean('db'),
    'minActive' => 10,
    'maxActive' => 20,
    'minWait' => 0,
    'maxWaitTime' => 0
],
# 實際開發應該使用中間件,而非框架給的方法
# 切換數據源需要創建新的 db 和 db.pool

2.2 原生操作、查詢構造器;

<?php

namespace App\Http\Controller;

use App\lib\ProductEntity;
use App\Model\Product;
use Swoft\Db\DB;
use Swoft\Http\Message\ContentType;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;
use Swoft\Validator\Annotation\Mapping\Validate;

/**
 * @Controller(prefix="/product2")
 */
class Product2Controller{

    /**
     * 2.3 驗證器:https://www.swoft.org/documents/v2/core-components/validator/
     * 註解使用
     * // @Validate(validator="product") // type="get"
     * @RequestMapping(route="{pid}", params={"pid"="\d+"}, method={RequestMethod::GET, RequestMethod::POST})
     */
    public function prod_detail(int $pid){
        // 2.2.1 原生查詢
        // $product = DB::selectOne("SELECT * FROM product WHERE id = ?", [$pid]);
        // 切換數據庫
        // $product = DB::db('test')->selectOne("SELECT * FROM product WHERE id = :pid", ["pid" => $pid]);
        // 切換數據源
        // $product = DB::query("db.pool")->getConnection()->selectOne("SELECT * FROM product WHERE id = :pid", ["pid" => $pid]);

        // 2.2.2 查詢構造器使用、關聯表:https://www.swoft.org/documents/v2/mysql/query/
        // 獲取所有
        // $product = DB::table("product")->get();
        // 獲取一條
        // $product = DB::table("product")->where("id", "=", $pid)->select("id")->first();
        // 關聯表
//        $product = DB::table("product")
//            ->join("product_class", "product.id", "product_class.pid")
//            ->select("product.*", "product_class.pid")
//            ->where("product.id", $pid)
//            ->first();

        // 2.4 模型的使用
        $product = Product::find($pid);
        // 不影響界面展示,代碼加入協程,加快取值過程
        // 參考:https://www.swoft.org/documents/v2/basic-components/public-function/#heading1
        if($product){
            // 代碼塊 作用域
            {
                sgo(function () use ($product){
                    \Swoole\Coroutine::sleep(3);
                    // 指定字段遞增1
                    $product->increment("prod_click");
                });
            }
            {
                sgo(function () use ($pid){
                    \Swoole\Coroutine::sleep(5);
                    // 每次商品被訪問,都會增加商品訪問日誌

                    // 方法 1 : 模型方式插入
                    $product_view = ProductView::new();
                    {
                        $product_view->setProdId($pid);
                        $product_view->setViewIp(ip());
                        $product_view->setViewNum(1);
                        $product_view->save();
                    }

                    // 方法 2 : 數組方式插入
                    $pviewData = [
                        "prod_id" => $pid,
                        "view_ip" => ip(),
                        "view_num" => 1
                    ];
                    $product_view = ProductView::new($pviewData)->save();

                    // 如果一個 ip 在當天訪問過每個商品,只需要 view_num +1 就行
                    // ProductView::updateOrCreate()    // 更新和創建過程中需要取數據,使用這個方法
                    // 僅僅執行新增或者插入 用以下方法,每次 view_num +1
                    ProductView::updateOrInsert(
                        [
                            "prod_id" => $pid,
                            "view_ip" => ip()
                        ],
                        [
                            "view_num" => DB::raw("view_num+1")
                        ]
                    );
                });
            }
        }

        /** //@var  $product  ProductEntity */
        //$product = jsonForObject(ProductEntity::class);
        //var_dump($product);
        if(isGet()){
            return response(ContentType::JSON)->withData($product);
        }else if(isPost()) {
            // 非註解使用:https://www.swoft.org/documents/v2/core-components/validator/#heading15
            \validate(jsonForObject(),"product");
            $product['prod_name'] = "修改產品" . request()->post('prod_name', 'none');
            $product['prod_price'] = "修改價格" . request()->post('prod_price', 0);
           return response(ContentType::JSON)->withData($product);

        }

    }

}

2.3 驗證器;

  • 註解使用
  • 新建 App\Http\MyValidator\ProductValidator.php
<?php

namespace App\Http\MyValidator;

use Swoft\Validator\Annotation\Mapping\IsFloat;
use Swoft\Validator\Annotation\Mapping\IsString;
use Swoft\Validator\Annotation\Mapping\Length;
use Swoft\Validator\Annotation\Mapping\Min;
use Swoft\Validator\Annotation\Mapping\Max;
use Swoft\Validator\Annotation\Mapping\Validator;

/**
 * 驗證:https://www.swoft.org/documents/v2/core-components/validator/#heading4
 * *** 遺留問題:空值判斷
 * @Validator(name="product")
 */
class ProductValidator{

    /**
     * @IsString(message="商品名稱不能爲空")
     * @Length(min=5,max=20,message="商品名稱長度爲5-20")
     * @var string
     */
    protected $prod_name;

    /**
     * @IsString(message="商品短名稱不能爲空")
     * @Length(min=2,max=20,message="商品名稱長度爲2-20")
     * @var string
     */
    protected $prod_sname;

    /**
     * @IsFloat(message="商品價格不能爲空")
     * @Min(value=20,message="價格最低20")
     * @Max(value=1000,message="價格最高1000")
     * @var float
     */
    protected $prod_price;

}

2.4 模型的使用;

php ./bin/swoft entity:create --table=product,product_class --pool=db.pool --path=@app/Model

# 後期新加字段:定義私有變量後, PhpStorm 用 Alt + Ins 新生成 set 和 get 方法

2.5. 場景練習(數據驗證,主子訂單入庫,事務控制);

在這裏插入圖片描述
在這裏插入圖片描述

  • 生成實體:
# 主訂單表
php ./bin/swoft entity:create --table=orders_main --pool=db.pool --path=@app/Model

# 子訂單表
php ./bin/swoft entity:create --table=orders_detail --pool=db.pool --path=@app/Model
  • 修改 App\Model\OrdersMain.php
	// Alt + ins 生成 set 和 get 方法
    // 自己定義的屬性不要打註解
    // 這個屬性在數據庫裏是沒有的,僅僅是映射成對象
    private $orderItems;

    /**
     * @return mixed
     */
    public function getOrderItems()
    {
        return $this->orderItems;
    }

    /**
     * @param mixed $orderItems
     */
    public function setOrderItems($orderItems): void
    {
        $this->orderItems = $orderItems;
    }
  • 主表驗證器:新建 App\Http\MyValidator\OrderValidator.php
<?php

namespace App\Http\MyValidator;

use App\Http\MyValidator\MyRules\OrderDetail;
use Swoft\Validator\Annotation\Mapping\IsArray;
use Swoft\Validator\Annotation\Mapping\IsFloat;
use Swoft\Validator\Annotation\Mapping\IsInt;
use Swoft\Validator\Annotation\Mapping\Min;
use Swoft\Validator\Annotation\Mapping\Max;
use Swoft\Validator\Annotation\Mapping\Validator;

/**
 * @Validator(name="order")
 */
class OrderValidator{

//    /**
//     * @IsString(message="訂單號不能爲空")
//     * @var string
//     */
//    protected $order_no;

    /**
     * @IsInt(message="用戶ID不能爲空")
     * @Min(value=1,message="用戶id不正確")
     * @var int
     */
    protected $user_id;

    /**
     * @IsInt(message="訂單狀態不能爲空")
     * @Min(value=0,message="狀態不正確min")
     * @Max(value=5,message="狀態不正確max")
     * @var int
     */
    protected $order_status;

    /**
     * @IsFloat(message="訂單金額不能爲空")
     * @Min(value=1,message="訂單金額不正確")
     * @var int
     */
    protected $order_money;

    // 訂單明細數據,是一個數組,包換若干子訂單實體
    // 自定義驗證器規則:https://www.swoft.org/documents/v2/core-components/validator/#heading7
    /**
     * @IsArray(message="訂單明細不能爲空")
     * @OrderDetail(message="訂單明細不正確")
     * @var array
     */
    protected $order_items;

}
  • 副表驗證器:新建 1 App\Http\MyValidator\OrderDetailValidator.php
<?php

namespace App\Http\MyValidator;

use Swoft\Validator\Annotation\Mapping\IsFloat;
use Swoft\Validator\Annotation\Mapping\IsInt;
use Swoft\Validator\Annotation\Mapping\IsString;
use Swoft\Validator\Annotation\Mapping\Min;
use Swoft\Validator\Annotation\Mapping\Max;
use Swoft\Validator\Annotation\Mapping\Validator;

/**
 * @Validator(name="order_detail")
 */
class OrderDetailValidator{

    /**
     * @IsInt(message="商品ID不能爲空")
     * @Min(value=1,message="商品id不正確")
     * @var int
     */
    protected $prod_id;

    /**
     * @IsString(message="商品名稱不能爲空")
     * @var string
     */
    protected $prod_name;

    /**
     * @IsFloat(message="商品價格不能爲空")
     * @Min(value=0,message="商品價格不正確")
     * @var int
     */
    protected $prod_price;

    /**
     * @IsInt(message="折扣不能爲空")
     * @Min(value=1,message="折扣不正確min")
     * @Max(value=10,message="折扣不正確max")
     * @var int
     */
    protected $discount;

    /**
     * @IsInt(message="商品數量不能爲空")
     * @Min(value=1,message="商品數量不正確")
     * @var int
     */
    protected $prod_num;

}
  • 新建 2 App\Http\MyValidator\MyRules\OrderDetail.php
<?php

namespace App\Http\MyValidator\MyRules;

/**
 * @Annotation
 * @Attributes({
 *     @Attribute("message",type="string")
 * })
 */
class OrderDetail{

    /**
     * @var string
     */
    private $message = '';

    /**
     * @var string
     */
    private $name = '';

    /**
     * StringType constructor.
     *
     * @param array $values
     */
    public function __construct(array $values)
    {
        if (isset($values['value'])) {
            $this->message = $values['value'];
        }
        if (isset($values['message'])) {
            $this->message = $values['message'];
        }
        if (isset($values['name'])) {
            $this->name = $values['name'];
        }
    }

    /**
     * @return string
     */
    public function getMessage(): string
    {
        return $this->message;
    }

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

}
  • 新建 3 App\Http\MyValidator\MyRules\OrderDetailParser.php
<?php

namespace App\Http\MyValidator\MyRules;

use Swoft\Annotation\Annotation\Mapping\AnnotationParser;
use Swoft\Annotation\Annotation\Parser\Parser;
use Swoft\Validator\ValidatorRegister;

/**
 * Class OrderDetailParser
 * @AnnotationParser(annotation=OrderDetail::class)
 */
class OrderDetailParser extends Parser{

    /**
     * Parse object
     *
     * @param int $type Class or Method or Property
     * @param object $annotationObject Annotation object
     *
     * @return array
     * Return empty array is nothing to do!
     * When class type return [$beanName, $className, $scope, $alias] is to inject bean
     * When property type return [$propertyValue, $isRef] is to reference value
     */
    public function parse(int $type, $annotationObject): array
    {
        if ($type != self::TYPE_PROPERTY) {
            return [];
        }
        //向驗證器註冊一個驗證規則
        ValidatorRegister::registerValidatorItem($this->className, $this->propertyName, $annotationObject);
        return [];
    }
}

  • 新建 4 App\Http\MyValidator\MyRules\OrderDetailRule.php
<?php

namespace App\Http\MyValidator\MyRules;

use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Validator\Contract\RuleInterface;
use Swoft\Validator\Exception\ValidatorException;

/**
 * Class OrderDetailRule
 * @Bean(OrderDetail::class)
 */
class OrderDetailRule implements RuleInterface {

    /**
     * @param array $data
     * @param string $propertyName
     * @param object $item
     * @param mixed $default
     *
     * @return array
     */
    public function validate(array $data, string $propertyName, $item, $default = null, $strict = false): array
    {
        $getData = $data[$propertyName];
        if(!$getData || !is_array($getData) || count($getData) == 0){
            throw new ValidatorException($item->getMessage());
        }

        foreach ($getData as $data) {
           validate($data, "order_detail");
        }

        return $data;
    }
}
  • 新建 App/Http/Controller/OrderController.php
<?php

namespace App\Http\Controller;

use App\Exception\ApiException;
use App\Model\OrdersDetail;
use App\Model\OrdersMain;
use Swoft\Db\DB;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;
use Swoft\Validator\Annotation\Mapping\Validate;

/**
 * @Controller(prefix="/order")
 */
class OrderController{

    /**
     * 創建訂單
     * @Validate(validator="order")
     * @RequestMapping(route="new",method={RequestMethod::POST})
     */
    public function createOrder(){

        // 獲取 POST 數據做基本驗證
        /** @var OrdersMain $orderPost */
        // 包含了主訂單和子訂單數據
        $orderPost = jsonForObject(OrdersMain::class);
        // 也可以使用菊花算法等
        $orderNo = date("YmdHis") . substr(implode(NULL, array_map('ord',str_split(substr(uniqid(), 7, 13), 1))),0,8);

        // ApiExceptionHandler 攔截
        // throw new ApiException("api exception");

        // 獲取子訂單數據,是一個數組
        // 需要通過ORM方式插入,數組是不行的
        // 需要一個函數把它映射成 ORM 實體
        // 子訂單數據([],[]),模型實體的數組
        // 最後一個 true 參數代表返回一個數組,字段和數據庫一致
        $orderDetail_array = mapToModelsArray($orderPost->getOrderItems(), OrdersDetail::class, ["order_no" => $orderNo], true);
        // var_dump($orderDetail_array);
        // $orderPost->getModelAttributes() // 實體對象轉數組

        // 訂單數據雙雙入庫
        if($orderPost){
            // $orderPost->setOrderNo($orderNo);
            // $orderPost->setCreateTime(date("Y-m-d H:i:s"));
            $orderPost->fill(["order_no" => $orderNo, "create_time" => date("Y-m-d H:i:s")]);
            // 加入事務: https://www.swoft.org/documents/v2/mysql/transaction/
//            DB::beginTransaction();
//            if($orderPost->save() && OrdersDetail::insert($orderDetail_array) ){
//                DB::commit();
//                return OrderCreateSuccess($orderNo);
//            }else{
//                DB::rollBack();
//            }

            tx(function () use ($orderPost, $orderDetail_array, $orderNo){
                if($orderPost->save() && OrdersDetail::insert($orderDetail_array) ){
                    return OrderCreateSuccess($orderNo);
                }
                throw new \Exception("創建訂單失敗");
            }, $result);

            return $result;
        }

        return OrderCreateFail();
    }

}
  • 拋異常相關修改:
# 文件 App\Exception\Handler\HttpExceptionHandler.php
# 修改以下部分,通用異常在此拋出
if (!APP_DEBUG) {
   //return $response->withStatus(500)->withContent($e->getMessage());
    return $response->withStatus(500)->withData(["message" => $e->getMessage()]);
}

# 文件 App\Exception\Handler\ApiExceptionHandler.php
# 代碼 throw new ApiException("api exception"); 進行以下輸出
public function handle(Throwable $except, Response $response): Response
    {
//        $data = [
//            'code'  => $except->getCode(),
//            'error' => sprintf('(%s) %s', get_class($except), $except->getMessage()),
//            'file'  => sprintf('At %s line %d', $except->getFile(), $except->getLine()),
//            'trace' => $except->getTraceAsString(),
//        ];
//
//        return $response->withData($data);

        return $response->withStatus(500)->withData([
            "apimessage" => $except->getMessage()
        ]);
    }
  • 添加: App/Helper/OrderFunctions.php
<?php

function OrderCreateSuccess($orderNo){
    return ["status" => "success", "orderNo" => $orderNo];
}


function OrderCreateFail($msg="error"){
    return ["status" => $msg, "orderNo" => ""];
}

3. Redis 相關;

3.1 Redis 配置使用;

  'redis'             => [
        'class'    => RedisDb::class,
        'host'     => '127.0.0.1',
        'port'     => 6379,
        'password' => 'asdf',
        'database' => 0,
//        'option'   => [
//            'prefix' => 'swoft:'
//        ]
    ],
    'redis.pool' => [
        'class'       => Swoft\Redis\Pool::class,
        'redisDb'     => bean('redis'),
        'minActive'   => 2,
        'maxActive'   => 5,
        'maxWait'     => 0,
        'maxWaitTime' => 0,
        'maxIdleTime' => 60,
    ],
  • 新建 App/Http/Controller/testController.php
<?php

namespace App\Http\Controller;

use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;
use Swoft\Redis\Redis;

/**
 * Class testController
 * @Controller(prefix="/test")
 */
class TestController{

    /**
     * @RequestMapping(route="/test",method={RequestMethod::GET})
     * 另外:連接池注入:https://www.swoft.org/documents/v2/redis/usage/#heading1
     */
    public function test(){
        $redis = Redis::connection("redis.pool");
        $v = $redis->get("name");
        return $v;
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章