current position:Home>Swoft 2. X Foundation (HTTP, database, redis)
Swoft 2. X Foundation (HTTP, database, redis)
2022-04-29 08:15:59【hualaoshuan】
A summary of this article :
0. Frame installation ;
0.1 official Docker The image starts Swoft;
- Document address :https://www.swoft.org/documents/v2/quick-start/install/
- Docker Mirror address :https://hub.docker.com/r/swoft/swoft/
# Pull the mirror image
docker pull swoft/swoft:latest
# see Dockerfile Understand mirror construction
# https://hub.docker.com/r/swoft/swoft/dockerfile
# start-up
docker run -p 18306:18306 --name swoft --rm -d swoft/swoft:latest
# Verify startup
curl localhost:18306
# Enter the local working directory
cd /data/php
# Copy the working directory code in the container to the host
# The working directory in the container is /var/www/swoft
# final “.” Copy to the current directory
docker cp swoft:/var/www/swoft .
# After copying successfully , The current machine will show a swoft Catalog
cd swoft && ls
# Swoft Built in a reload mechanism , After modifying a file, it will md5 comparison
# Stop container , Do folder mapping after restart
docker stop swoft
docker run -p 18306:18306 --name swoft --rm -d \
-v /data/php/swoft/:/var/www/swoft \
swoft/swoft:latest
1. Http Server;
1.1 controller ;
- contain :RequestMapping notes 、 Request and response 、 return JSON Format 、 route path Parameters 、GET \ POST Parameters to obtain
- PhpStorm Installing a plug-in
- newly build
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;
/** * Commodity module * // @Controller() * @Controller(prefix="/product") * Use a middleware * @Middleware(ControllerMiddleware::class) */
class ProductController{
/** * 1.1.1 RequestMapping notes * * @RequestMapping(route="/product", method={RequestMethod::GET}) */
public function prod_list(){
// 1.1.2 Request and response
// Reference resources :https://www.swoft.org/documents/v2/core-components/http-server/#http-1
// Context object :Contexts
// Get request object
$req = Context::get()->getRequest();
// Get the response object
$res = Context::get()->getResponse();
// Return content
// return $res->withContent("abc");
// 1.1.3 return JSON Format
// return $res->withContentType("application/json")->withContent("abc");
// 1.2 Call global functions
// return $res->withContentType("application/json")
// ->withContent(json_encode([NewProduct(101, " Test product "), NewProduct(102, " Test product 2")]));
return $res->withContentType("application/json")
->withData([NewProduct(101, " Test product "), NewProduct(102, " Test product 2")]);
}
/** * //@RequestMapping(route="/product/{pid}", method={RequestMethod::GET}) * route path Parameter prefixes , Regular control routing parameters * Injection parameters Response, May or cause too many parameters * 1.1.4 route path Parameters * @RequestMapping(route="{pid}", params={"pid"="\d+"}, method={RequestMethod::GET, RequestMethod::POST}) */
public function prod_detail(int $pid, Response $response){
$p = NewProduct($pid, " Test product ");
//return \response()->withContentType("application/json")->withData([$p]);
// return $response->withContentType(ContentType::JSON)->withData([$p]);
// 1.3 The returned value here will call the middleware
// return [$p];
// 1.1.5 GET \ POST Parameters to obtain
// echo request()->get("type", "default type");
// if(request()->getMethod() == RequestMethod::GET){
// *1.5 JSON Parameter to entity object
/** @var $product ProductEntity */
// $product = jsonForObject(ProductEntity::class);
// var_dump($product);
if(isGet()){
return $p;
}else if(isPost()) {
$p->pname = " Product modification " . request()->post('title', 'none');
return $p;
}
// *1.4 call chaining
// $my = new MyRequest();
// $my->if(isGet())
// ->then(function() use ($p){
// return $p;
// })
// ->if(isPost())
// ->then(function() use ($p){
// $p->pname = " Product modification " . request()->post('title', 'none');
// return $p;
// })
// ->getResult();
}
}
- Example operation
# The terminal enters the project directory
cd /data/test/php/swoft/
# start-up Swoft
php ./bin/swoft http:start
# return
SERVER INFORMATION(v2.0.8)
********************************************************************************
* HTTP | Listen: 0.0.0.0:18306, Mode: Process, Worker: 6, Task worker: 12
********************************************************************************
HTTP Server Start Success!
# Browser access :http://localhost:18306/product
1.2 Global function ;
- modify
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::get()->getResponse()->withContentType($contentType);
}
return Swoft\Context\Context::get()->getResponse();
}
function request(){
return Swoft\Context\Context::get()->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{
return $req->server('remote_addr');
}
}
// Transaction encapsulation ( It can also be used. 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 middleware ;
- Reference resources :https://www.swoft.org/documents/v2/core-components/http-server/#heading13
- newly build
App/Http/Middleware/ControllerMiddleware.php
<?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 * Put it into the controller for corresponding , You can also set global middleware * * @return ResponseInterface */
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
/** @var $ret \Swoft\Http\Message\Response */
// response object
$ret = $handler->handle($request);
$data = $ret->getData();
//$p2 = NewProduct(10000, " Middleware test products ");
//$data[] = $p2;
if(is_object($data)){
return \response(ContentType::JSON)->withContent(json_encode($data));
}
return \response(ContentType::JSON)->withData($data);
}
}
*1.4 call chaining ;
- newly build
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 Parameter to entity object ;
- newly build
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;
}
}
- newly build
App/Helper/WebFunction.php
<?php
/** * @param $class * $class Not instantiated objects , It is function name * Reflection is required */
function jsonForObject($class=""){
$req = request();
try {
$contentType = $req->getHeader('content-type');
if (!$contentType || false === stripos($contentType[0], \Swoft\Http\Message\ContentType::JSON)) {
return false;
}
// Get the original body Content
$raw = $req->getBody()->getContents();
// Compare the obtained object with $class Make a comparison
$map = json_decode($raw, true); // kv Array
if($class == "") return $map; // Realization 2.0.2 Before version request()->json() Method
$class_obj = new ReflectionClass($class); // Reflection object
$class_instance = $class_obj->newInstance(); // Create instances from reflective objects
// Get all methods ( Public methods )
$methods = $class_obj->getMethods(ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method){
// Regular get set Method
if(preg_match("/set(\w+)/", $method->getName(), $matches)){
// echo $matches[1] . PHP_EOL; // Get the method name ProdId,ProdName,ProdPrice
invokeSetterMethod($matches[1], $class_obj, $map,$class_instance);
}
}
return $class_instance;
}catch (Exception $exception){
return false;
}
}
// Map an array to an entity ( One dimensional array )
function mapToModel(array $map, $class){
try {
$class_obj = new ReflectionClass($class);
$class_instance = $class_obj->newInstance(); // Create instances from reflective objects
// Get all methods ( Public methods )
$methods = $class_obj->getMethods(ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method){
// Regular get set Method
if(preg_match("/set(\w+)/", $method->getName(), $matches)){
// echo $matches[1] . PHP_EOL; // Get the method name ProdId,ProdName,ProdPrice
invokeSetterMethod($matches[1], $class_obj, $map,$class_instance);
}
}
return $class_instance;
} catch (Exception $exception){
return null;
}
}
// Two dimensional array
// $fill Fill in new fields
// $toarray=false Returns an array of entities
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){
// Array
$ret[] = $getObject->getModelAttributes();
}else{
// Entity object
$ret[] = $getObject;
}
}
}
return $ret;
}
function invokeSetterMethod($name, ReflectionClass $class_obj, $jsonMap, &$class_instance){
// hold ProdId Turn it into Prod_Id
$filter_name = strtolower(preg_replace("/(?<=[a-z])([A-Z])/", "_$1", $name));
// compatible swoft
// hold ProdId Turn it into prodId
$filter_name_ForSwoft = lcfirst($name);
$props = $class_obj->getProperties(ReflectionProperty::IS_PRIVATE);
foreach ($props as $prop) {
// There are corresponding private properties
if(strtolower($prop->getName()) == $filter_name || $prop->getName() == $filter_name_ForSwoft){
$method = $class_obj->getMethod("set" . $name);
$args = $method->getParameters(); // Take out the parameters
if(count($args) == 1 && isset($jsonMap[$filter_name])){
// Assign values to instance objects ( By quoting )
$method->invoke($class_instance, $jsonMap[$filter_name]);
}
}
}
}
- About Reflexive class and Instantiate the class The difference between :new class() It is the presentation of a class object after encapsulation , You don't need to know the private members and methods of the class , And the internal mechanism , You can use it directly through the open member methods and properties of the class
- and new ReflectionClass() The reflection class is the presentation of a class object after it is opened , It will the internal properties of the class 、 Including public or private properties and methods , Is it static , Interface 、 Inherit 、 Namespace information , Even all comments are made public , Can be reflected API Visit
- This shows the power of reflection class . But reflection is usually used to write more complex underlying logic of the business . The external function development still uses instantiation class encapsulation , It's also safer and more convenient
- The reflection mechanism itself is the means to release the closure , Its purpose mostly lies in the bottom or not external business development , For example, interface documents are automatically generated 、 Implement hook . Since the post reflection class is so open , Nature can be modified at will
2. database ;
2.1 Basic configuration ;
- Reference resources :https://www.swoft.org/documents/v2/mysql/config/
- Modify file :
App\bean.php
<?php
// The setting configuration file is as follows
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
]
],
- Database connection pool :https://www.swoft.org/documents/v2/mysql/config/#heading4
# The main function of connection pool : Protect the database
# The built-in control pool has its own connection
'db.pool' => [
'class' => Pool::class,
'database' => bean('db'),
'minActive' => 10,
'maxActive' => 20,
'minWait' => 0,
'maxWaitTime' => 0
],
- database 、 Data source switching :https://www.swoft.org/documents/v2/mysql/select-db/
# The actual development should use middleware , Not the method given by the framework
# To switch data sources, you need to create a new db and db.pool
2.2 Native operation 、 Query builder ;
- Reference resources :https://www.swoft.org/documents/v2/mysql/origin/
- newly build
App/Http/Controller/Product2Controller.php
<?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 Validator :https://www.swoft.org/documents/v2/core-components/validator/ * Annotations use * // @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 Native query
// $product = DB::selectOne("SELECT * FROM product WHERE id = ?", [$pid]);
// Switch database
// $product = DB::db('test')->selectOne("SELECT * FROM product WHERE id = :pid", ["pid" => $pid]);
// Switch data source
// $product = DB::query("db.pool")->getConnection()->selectOne("SELECT * FROM product WHERE id = :pid", ["pid" => $pid]);
// 2.2.2 The query constructor uses 、 Association table :https://www.swoft.org/documents/v2/mysql/query/
// Get all
// $product = DB::table("product")->get();
// Get one
// $product = DB::table("product")->where("id", "=", $pid)->select("id")->first();
// Association table
// $product = DB::table("product")
// ->join("product_class", "product.id", "product_class.pid")
// ->select("product.*", "product_class.pid")
// ->where("product.id", $pid)
// ->first();
// 2.4 Use of models
$product = Product::find($pid);
// Does not affect the interface display , Join the code , Speed up the value taking process
// Reference resources :https://www.swoft.org/documents/v2/basic-components/public-function/#heading1
if($product){
// Code block Scope
{
sgo(function () use ($product){
\Swoole\Coroutine::sleep(3);
// Specifies that the field is incremented 1
$product->increment("prod_click");
});
}
{
sgo(function () use ($pid){
\Swoole\Coroutine::sleep(5);
// Every time a product is visited , Product access logs will be added
// Method 1 : Insert in model mode
$product_view = ProductView::new();
{
$product_view->setProdId($pid);
$product_view->setViewIp(ip());
$product_view->setViewNum(1);
$product_view->save();
}
// Method 2 : Insert in array mode
$pviewData = [
"prod_id" => $pid,
"view_ip" => ip(),
"view_num" => 1
];
$product_view = ProductView::new($pviewData)->save();
// If one ip Visited every product on the same day , It only needs view_num +1 Just go
// ProductView::updateOrCreate() // Data needs to be retrieved during update and creation , Using this method
// Just add or insert Use the following methods , Every time 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()) {
// Non annotation use :https://www.swoft.org/documents/v2/core-components/validator/#heading15
\validate(jsonForObject(),"product");
$product['prod_name'] = " Product modification " . request()->post('prod_name', 'none');
$product['prod_price'] = " Revise the price " . request()->post('prod_price', 0);
return response(ContentType::JSON)->withData($product);
}
}
}
2.3 Validator ;
- Annotations use
- newly build
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;
/** * verification :https://www.swoft.org/documents/v2/core-components/validator/#heading4 * *** remaining problems : A null value judgment * @Validator(name="product") */
class ProductValidator{
/** * @IsString(message=" Commodity name cannot be empty ") * @Length(min=5,max=20,message=" The length of the product name is 5-20") * @var string */
protected $prod_name;
/** * @IsString(message=" Commodity short name cannot be empty ") * @Length(min=2,max=20,message=" The length of the product name is 2-20") * @var string */
protected $prod_sname;
/** * @IsFloat(message=" Commodity price cannot be empty ") * @Min(value=20,message=" Lowest price 20") * @Max(value=1000,message=" Highest price 1000") * @var float */
protected $prod_price;
}
- another : Custom validator :https://www.bookstack.cn/read/swoft-doc-v2.x/validator-user-validator.md
2.4 Use of models ;
- Reference resources :https://www.swoft.org/documents/v2/mysql/model/
- Generate basic model :
php ./bin/swoft entity:create --table=product,product_class --pool=db.pool --path=@app/Model
# Add new fields later : After defining private variables , PhpStorm use Alt + Ins New generation set and get Method
2.5 Scene practice ( data validation , Primary sub order receipt , Transaction control );
- Generating entities :
# Master order table
php ./bin/swoft entity:create --table=orders_main --pool=db.pool --path=@app/Model
# Sub order table
php ./bin/swoft entity:create --table=orders_detail --pool=db.pool --path=@app/Model
- modify
App\Model\OrdersMain.php
// Alt + ins Generate set and get Method
// Don't annotate the attributes you define
// This attribute is not available in the database , Just mapping to objects
private $orderItems;
/** * @return mixed */
public function getOrderItems()
{
return $this->orderItems;
}
/** * @param mixed $orderItems */
public function setOrderItems($orderItems): void
{
$this->orderItems = $orderItems;
}
- Main table verifier : newly build
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=" Order No. cannot be empty ")
// * @var string
// */
// protected $order_no;
/** * @IsInt(message=" user ID Can't be empty ") * @Min(value=1,message=" user id Incorrect ") * @var int */
protected $user_id;
/** * @IsInt(message=" Order status cannot be empty ") * @Min(value=0,message=" The state is not correct min") * @Max(value=5,message=" The state is not correct max") * @var int */
protected $order_status;
/** * @IsFloat(message=" Order amount cannot be blank ") * @Min(value=1,message=" The order amount is incorrect ") * @var int */
protected $order_money;
// Order details , Is an array , Several entities change orders
// Custom validator rules :https://www.swoft.org/documents/v2/core-components/validator/#heading7
/** * @IsArray(message=" Order details cannot be blank ") * @OrderDetail(message=" Incorrect order details ") * @var array */
protected $order_items;
}
- Secondary table verifier : newly build 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=" goods ID Can't be empty ") * @Min(value=1,message=" goods id Incorrect ") * @var int */
protected $prod_id;
/** * @IsString(message=" Commodity name cannot be empty ") * @var string */
protected $prod_name;
/** * @IsFloat(message=" Commodity price cannot be empty ") * @Min(value=0,message=" The commodity price is incorrect ") * @var int */
protected $prod_price;
/** * @IsInt(message=" Discount cannot be blank ") * @Min(value=1,message=" The discount is incorrect min") * @Max(value=10,message=" The discount is incorrect max") * @var int */
protected $discount;
/** * @IsInt(message=" Commodity quantity cannot be blank ") * @Min(value=1,message=" The quantity of goods is incorrect ") * @var int */
protected $prod_num;
}
- Custom validator , newly build 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;
}
}
- Custom validator , newly build 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 App\Http\MyValidator\MyRules\OrderDetail;
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 [];
}
// Register an authentication rule with the verifier
ValidatorRegister::registerValidatorItem($this->className, $this->propertyName, $annotationObject);
return [];
}
}
- Custom validator , newly build 4
App\Http\MyValidator\MyRules\OrderDetailRule.php
<?php
namespace App\Http\MyValidator\MyRules;
use App\Http\MyValidator\MyRules\UserPass;
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;
}
}
- newly build
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\Task\Task;
use Swoft\Validator\Annotation\Mapping\Validate;
/** * @Controller(prefix="/order") */
class OrderController{
/** * Create order * @Validate(validator="order") * @RequestMapping(route="new",method={RequestMethod::POST}) */
public function createOrder(){
// obtain POST Basic verification of data
/** @var OrdersMain $orderPost */
// Contains the data of main order and sub order
$orderPost = jsonForObject(OrdersMain::class);
// Chrysanthemum algorithm can also be used
$orderNo = date("YmdHis") . substr(implode(NULL, array_map('ord',str_split(substr(uniqid(), 7, 13), 1))),0,8);
// ApiExceptionHandler Intercept
// throw new ApiException("api exception");
// Get sub order data , Is an array
// Need to pass through ORM Way to insert , Arrays don't work
// You need a function to map it to ORM Entity
// Sub order data ([],[]), Array of model entities
// the last one true Parameter represents the return of an array , The fields are consistent with the database
$orderDetail_array = mapToModelsArray($orderPost->getOrderItems(), OrdersDetail::class, ["order_no" => $orderNo], true);
// var_dump($orderDetail_array);
// $orderPost->getModelAttributes() // Entity object to array
// Both order data are received
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")]);
// Join the business : 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(" Failed to create order ");
}, $result);
// 3.2.1 Asynchronous task delivery
// Task::async("orders", "putorderno", [$orderNo]);
// 3.2.2 Put in the delay queue
Task::async("orders", "putorderno_zset", [$orderNo, 5]);
return $result;
}
return OrderCreateFail();
}
}
- Throw exception related modification :
# file App\Exception\Handler\HttpExceptionHandler.php
# Revise the following sections , A generic exception is thrown here
if (!APP_DEBUG) {
//return $response->withStatus(500)->withContent($e->getMessage());
return $response->withStatus(500)->withData(["message" => $e->getMessage()]);
}
# file App\Exception\Handler\ApiExceptionHandler.php
# Code throw new ApiException("api exception"); Make the following output
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()
]);
}
- add to :
App/Helper/OrderFunctions.php
<?php
function OrderCreateSuccess($orderNo){
return ["status" => "success", "orderNo" => $orderNo];
}
function OrderCreateFail($msg="error"){
return ["status" => $msg, "orderNo" => ""];
}
function getInWhere($array) {
$where = "";
foreach ($array as $item){
if($where == ""){
$where .= "'" . $item . "'";
}else{
$where .= ",'" . $item . "'";
}
}
return $where;
}
3. Redis relevant ;
3.1 Redis Configuration and use ;
- Basic configuration :https://www.swoft.org/documents/v2/redis/config/
- modify :
App\bean.php
'redis' => [
'class' => RedisDb::class,
'host' => '127.0.0.1',
'port' => 6379,
'password' => 'asdf',
'database' => 0,
'option' => [
//'serializer' => Redis::SERIALIZER_PHP
'serializer' => 0
]
],
'redis.pool' => [
'class' => Swoft\Redis\Pool::class,
'redisDb' => bean('redis'),
'minActive' => 2,
'maxActive' => 5,
'maxWait' => 0,
'maxWaitTime' => 0,
'maxIdleTime' => 60,
],
- newly build
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}) * in addition : Connection pool injection :https://www.swoft.org/documents/v2/redis/usage/#heading1 */
public function test(){
$redis = Redis::connection("redis.pool");
$v = $redis->get("name");
return $v;
}
}
3.2 Scene practice ( The order is overdue );
3.2.1 Asynchronous task ;
- Connect , The receipt of primary and sub orders must be successful . If there are other steps , For example, insert the order number redis, There is no need for customers to wait for this part . As long as the primary sub order is inserted successfully , Return a response to the client immediately , Insert redis You can use concurrent or asynchronous tasks to complete
- Asynchronous tasks are used here :https://www.swoft.org/documents/v2/core-components/task/
- newly build
App/Task/OrderTask.php
<?php
namespace App\Tasks;
use Swoft\Redis\Redis;
use Swoft\Task\Annotation\Mapping\Task;
use Swoft\Task\Annotation\Mapping\TaskMapping;
/** * Class OrderTask * @Task(name="orders") */
class OrderTask{
/** * @TaskMapping(name="putorderno") */
public function putOrderNoToRedis($orderNo){
// Expiration time 5 second
Redis::setex("order" . $orderNo, 5, 1);
}
}
- key Expiration trigger event listening : This method can be used when the pressure is not particularly high , Use queues to complete... Under high pressure
- Redis Of Keyspace notice :https://redis.io/topics/notifications , After triggering some events, you can send a notification to the specified channel
- Relevant events need to be set , once key Be overdue , Will send one to the specified channel key name , Get this key after , We can do the corresponding treatment
# operation 1:
# modify Redis The configuration file redis.conf, hold notify-keyspace-events Ex Ahead “#” Get rid of , Save and exit , restart redis
# operation 2: Use Redis Subscription publishing function
subscribe [email protected]__:expired
# return :
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "[email protected]__:expired"
3) (integer) 1
# Generate order 10 Seconds later redis Be overdue , Send an event to the channel to trigger , The console returns
1) "message"
2) "[email protected]__:expired"
3) "order2020022614311652575653"
3.2.2 User process ;
- Code implementation , Need to use user processes :https://www.swoft.org/documents/v2/core-components/process/#heading2
- If the process brings something particularly complex , Especially consumes machine performance , This process needs to be done alone , Do not embed directly in Swoft Inside . If the process is simple ( Do order monitoring ), That can be embedded in HTTP Server Inside ( stay Swoft When it starts , Start a process at the same time , You can do some monitoring and other related matters in the process )
- newly build :
App/MyProcess/OrderProcess.php
<?php
namespace App\MyProcess;
use App\Model\OrdersMain;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Process\Process;
use Swoft\Process\UserProcess;
use Swoft\Redis\Redis;
/** * Class OrderProcess * @Bean() */
class OrderProcess extends UserProcess {
/** * Run * * @param Process $process */
public function run(Process $process): void
{
// Officials say we must add , In fact, it's OK not to add
// If not , Output abc after , End of process , Then restart the process output abc
// while(true){
// echo "abc\n";
// \Swoole\Coroutine::sleep(5);
// }
// The following code is similar to blocking , Unwanted while
// Order 10 Seconds expired , Set the order status to expired
Redis::subscribe(["[email protected]__:expired"], function ($redis, $chan, $msg){
// echo "Channel: $chan\n";
// echo "Payload: $msg\n";
$orderNo = str_replace("order", "", $msg);
OrdersMain::where("order_no", $orderNo)
->update(["order_status" => -1]);
echo " Order No " . $orderNo . " Your order is overdue " . PHP_EOL;
});
}
}
- modify :
App\bean.php
// httpServer Add the following to it
'process' => [
// 3.2.2
// 'orderp' => bean(\App\MyProcess\OrderProcess::class)
// 3.2.3
'orderp' => bean(\App\MyProcess\OrderProcessBySet::class)
]
3.2.3 Redis Delay queue ;
- Before using Redis The subscription and publishing function of . In actual development , In the case of large orders , If Auto trigger is used , Will affect performance , Therefore, the delay queue method will be used to complete , And use the client process to poll
- Use Redis Polling in an orderly collection of . Order placed successfully , be
zadd key score value
, among key It's a fixed value ( such as orderset),score Is the timestamp value (time+5),value It's the order number - In the process, you can read circularly , Once it exceeds the time, delete it
- modify
App/Task/OrderTask.php
// Insert the following code
/** * @TaskMapping(name="putorderno_zset") */
public function putOrderNoToRedisZset($orderNo, $delay){
Redis::zAdd("orderset", [$orderNo => time() + $delay] );
echo " Insert redis_zset success " . PHP_EOL;
}
- Redis The relevant operation
# View ordered queues
zrange orderset 0 -1 withscores
zrangebyscore orderset -inf +inf withscores
- newly build :
App/MyProcess/OrderProcessBySet.php
<?php
namespace App\MyProcess;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Db\DB;
use Swoft\Process\Process;
use Swoft\Process\UserProcess;
use Swoft\Redis\Redis;
/** * Class OrderProcessBySet * @Bean() */
class OrderProcessBySet extends UserProcess {
/** * Run * * @param Process $process */
public function run(Process $process): void
{
while (true){
$orders = Redis::zRangeByScore("orderset", '-inf', strval(time()), ["limit" => [0, 3]]);
if($orders && count($orders) > 0) {
DB::beginTransaction();
try {
// Batch execution
$rows = DB::update("UPDATE orders_main SET order_status = -1 WHERE order_no in (" . getInWhere($orders) . ")");
// Extender :"..."
// There is a problem with the logic here , Once triggered MySQL Roll back ,Redis No rollback , Data inconsistency
if(Redis::zRem("orderset", ...$orders) === $rows){
throw new \Exception("orderset remove error");
}
DB::commit();
}catch (\Exception $exception){
echo $exception->getMessage();
DB::rollBack();
}
usleep(1000*500); // 500 millisecond
}
}
}
}
copyright notice
author[hualaoshuan],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2022/119/202204290600599257.html
The sidebar is recommended
- How does HTML5 display the value obtained from the database in the specified area
- Problems related to nginx rewriting interface
- What happens when you open a CSS file in eclipse without responding
- React download local template can be downloaded locally, but the deployment online download is blank
- @Babel / traverse official example running error: typeerror: traverse is not a function
- The click event of jQuery has no effect
- How to learn from non professional background?
- Usage of API documented as @since 1.8+ less... (Ctrl+F1) Inspection info: This inspection finds all
- Configuration Vue of webpack
- Introduction to nginx (learning notes)
guess what you like
Is the financial management class of qiniu school true? Can you learn how to manage money in free lessons
How does Java randomly get elements from a list
Use of Vue on in Vue
Use of Vue router
Vue basic syntax
Use of webpack
Vue diff algorithm
CSS -- Text gradient from top to bottom
Routing: Vue router
Leetcode 658. Find K closest elements
Random recommended
- How to configure Vue in Vue project config. JS to solve cross domain problems
- Centos6 makes nginx-1.21.6-rpm package -- the way to build a dream
- [vue2-sgg v] vuex
- [vue2-sgg vi] route Vue router guard
- [vue2-sgg VII] Vue export and deploy to nginx --- UI component library (element UI...)
- Chapter 12 Ajax
- Clion remote debugging ubutun server, blood lessons
- The latest vue-i18n international plug-in realizes language switching (with source code)
- Vue monitors watch usage
- Vue encapsulates Axios to the full version of the calling interface (code source code)
- Watch data monitoring in Vue and detailed explanation of various attributes in watch
- Vue encapsulates Axios to call interface Full Version (code source code) latest recommendation (II)
- Vue encapsulates Axios to the full version of the calling interface (code source code)
- Ajax usage based on JQ
- Vue project optimization
- Vue - form generator form code generation
- Data acquisition in vuex is assigned to the local problem, and when is vuex data assigned to the local problem
- The modal box component is encapsulated in Vue, and the animation effect in Vue
- Swiper, the application of swiper in Vue, and various versions of swiper are applied in Vue projects
- Python——ReadTimeoutError: HTTPSConnectionPool(host=‘files.pythonhosted.org‘, port=443)
- Lesson 3 of ROS quick start - subscriber subscriber of ROS
- A lifeless face
- Mock in Vue JS preliminary simple use
- The Java Web servlet triggers the alert box on the front end
- CSS sets the color of the small vertical bar in front of the title
- Incomplete display of CSS background image
- [front end learning notes] save the front-end related codes
- Precautions for AWS serverless design dynamodb
- AWS serverless design - apigateway
- AWS serverless design lambda
- AWS serverless design - firewall WAF
- AWS serverless design-s3
- Python repeated element determination function program
- Nginx direction agent solves cross domain Problems-2
- The foundation of JavaScript
- DOM based on JavaScript
- Javascript based BOM
- Basic summary of JavaScript advanced
- Object oriented JavaScript
- JavaScript advanced threading mechanism and event mechanism