diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php
index af06931ac..4890e9f4a 100644
--- a/.phpstorm.meta.php
+++ b/.phpstorm.meta.php
@@ -1,97 +1,7 @@
AvailabilityDtoAssemblerInterface::class,
- TransactionDtoAssemblerInterface::class => TransactionDtoAssemblerInterface::class,
- TransferDtoAssemblerInterface::class => TransferDtoAssemblerInterface::class,
- TransferLazyDtoAssemblerInterface::class => TransferLazyDtoAssemblerInterface::class,
- // internal.query in assembler
- TransactionQueryAssemblerInterface::class => TransactionQueryAssemblerInterface::class,
- TransferQueryAssemblerInterface::class => TransferQueryAssemblerInterface::class,
-
- // internal.repositories
- TransactionRepositoryInterface::class => TransactionRepositoryInterface::class,
- TransferRepositoryInterface::class => TransferRepositoryInterface::class,
-
- // internal.service
- DatabaseServiceInterface::class => DatabaseServiceInterface::class,
- JsonServiceInterface::class => JsonServiceInterface::class,
- LockServiceInterface::class => LockServiceInterface::class,
- MathServiceInterface::class => MathServiceInterface::class,
- StorageServiceInterface::class => StorageServiceInterface::class,
- TranslatorServiceInterface::class => TranslatorServiceInterface::class,
- UuidFactoryServiceInterface::class => UuidFactoryServiceInterface::class,
-
- // internal.transform
- TransactionDtoTransformerInterface::class => TransactionDtoTransformerInterface::class,
- TransferDtoTransformerInterface::class => TransferDtoTransformerInterface::class,
-
- // legacy.models
- Wallet::class => Wallet::class,
- Transfer::class => Transfer::class,
- Transaction::class => Transaction::class,
-
- // legacy.objects
- Cart::class => Cart::class,
-
- // services
- AssistantServiceInterface::class => AssistantServiceInterface::class,
- AtmServiceInterface::class => AtmServiceInterface::class,
- AtomicServiceInterface::class => AtomicServiceInterface::class,
- BasketServiceInterface::class => BasketServiceInterface::class,
- BookkeeperServiceInterface::class => BookkeeperServiceInterface::class,
- CastServiceInterface::class => CastServiceInterface::class,
- ConsistencyServiceInterface::class => ConsistencyServiceInterface::class,
- DiscountServiceInterface::class => DiscountServiceInterface::class,
- ExchangeServiceInterface::class => ExchangeServiceInterface::class,
- PrepareServiceInterface::class => PrepareServiceInterface::class,
- PurchaseServiceInterface::class => PurchaseServiceInterface::class,
- TaxServiceInterface::class => TaxServiceInterface::class,
-
- // lagacy.services
- CommonServiceLegacy::class => CommonServiceLegacy::class,
- MetaServiceLegacy::class => MetaServiceLegacy::class,
+ '' => '@',
]));
-
}
diff --git a/README.md b/README.md
index 12b4e0c2d..c5981a8b8 100644
--- a/README.md
+++ b/README.md
@@ -25,16 +25,25 @@ laravel-wallet - Easy work with virtual wallet.
* **Laravel Version**: `9.x`
* **[Composer](https://getcomposer.org/):** `composer require bavix/laravel-wallet`
+
+### Support Policy
+
+| Version | Laravel | PHP | End of improvements | End of support |
+|------------|----------------|-------------|---------------------|----------------|
+| 7.x | ^6.0,^7.0,^8.0 | 7.4,8.0,8.1 | 1 Mar 2022 | 6 Sep 2022 |
+| 8.x | ^9.0 | 8.0,8.1 | 1 May 2022 | 1 Jun 2022 |
+| 9.x [LTS] | ^9.0 | 8.0,8.1 | 1 Feb 2023 | 6 Nov 2023 |
+
### Upgrade Guide
To perform the migration, you will be [helped by the instruction](https://bavix.github.io/laravel-wallet/#/upgrade-guide).
### Extensions
-| Extension | Description |
-| ----- | ----- |
-| [Swap](https://github.com/bavix/laravel-wallet-swap) | Addition to the laravel-wallet library for quick setting of exchange rates |
-| [Warm Up](https://github.com/bavix/laravel-wallet-warmup) | Addition to the laravel-wallet library for refresh balance wallets |
+| Extension | Description |
+|-----------------------------------------------------------|----------------------------------------------------------------------------|
+| [Swap](https://github.com/bavix/laravel-wallet-swap) | Addition to the laravel-wallet library for quick setting of exchange rates |
+| [Warm Up](https://github.com/bavix/laravel-wallet-warmup) | Addition to the laravel-wallet library for refresh balance wallets |
### Usage
Add the `HasWallet` trait and `Wallet` interface to model.
@@ -78,19 +87,52 @@ class User extends Model implements Customer
}
```
-Add the `HasWallet` trait and `Product` interface to `Item` model.
+Add the `HasWallet` trait and interface to `Item` model.
+
+Starting from version 9.x there are two product interfaces:
+- For an unlimited number of products (`ProductLimitedInterface`);
+- For a limited number of products (`ProductInterface`);
+
+An example with an unlimited number of products:
```php
use Bavix\Wallet\Traits\HasWallet;
-use Bavix\Wallet\Interfaces\Product;
use Bavix\Wallet\Interfaces\Customer;
+use Bavix\Wallet\Interfaces\ProductInterface;
-class Item extends Model implements Product
+class Item extends Model implements ProductInterface
+{
+ use HasWallet;
+
+ public function getAmountProduct(Customer $customer)
+ {
+ return 100;
+ }
+
+ public function getMetaProduct(): ?array
+ {
+ return [
+ 'title' => $this->title,
+ 'description' => 'Purchase of Product #' . $this->id,
+ ];
+ }
+}
+```
+
+Example with a limited number of products:
+```php
+use Bavix\Wallet\Traits\HasWallet;
+use Bavix\Wallet\Interfaces\Customer;
+use Bavix\Wallet\Interfaces\ProductLimitedInterface;
+
+class Item extends Model implements ProductLimitedInterface
{
use HasWallet;
public function canBuy(Customer $customer, int $quantity = 1, bool $force = false): bool
{
/**
+ * This is where you implement the constraint logic.
+ *
* If the service can be purchased once, then
* return !$customer->paid($this);
*/
@@ -112,6 +154,10 @@ class Item extends Model implements Product
}
```
+I do not recommend using the limited interface when working with a shopping cart.
+If you are working with a shopping cart, then you should override the `PurchaseServiceInterface` interface.
+With it, you can check the availability of all products with one request, there will be no N-queries in the database.
+
Proceed to purchase.
```php
@@ -123,7 +169,7 @@ $user->pay($item); // If you do not have enough money, throw an exception
var_dump($user->balance); // 0
if ($user->safePay($item)) {
- // try to buy again )
+ // try to buy again
}
var_dump((bool)$user->paid($item)); // bool(true)
@@ -135,7 +181,11 @@ var_dump((bool)$user->paid($item)); // bool(false)
### Eager Loading
```php
+// When working with one wallet
User::with('wallet');
+
+// When using the multi-wallet functionality
+User::with('wallets');
```
### How to work with fractional numbers?
diff --git a/changelog.md b/changelog.md
index d07d424e1..4f985ba33 100644
--- a/changelog.md
+++ b/changelog.md
@@ -6,6 +6,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [9.0.0] - 2022-05-02 #481
+### Added
+- ExtraDtoInterface #479
+- Product custom price #485
+
+### Changed
+- Changing the logic of funds transfers #483
+- Split Product interface #474
+- PHP 8+ Union types #482
+- Eager loading #480
+
+### Removed
+- method `Cart::addItems`
+- method `Cart::addItem`
+- method `Cart::setMeta`
+
+### Updated
+- Performance just got a little better
+- Public contracts have become stricter
+- Inside is now strongly typed
+
+### Deprecated
+- interface `Product`
+- method `CartPay::paid`
+
## [8.4.1] - 2022-04-26
### Deprecated
- Cart::getUniqueItems
@@ -843,7 +868,8 @@ The operation is now executed in the transaction and updates the new `refund` fi
- Exceptions: AmountInvalid, BalanceIsEmpty.
- Models: Transfer, Transaction.
-[Unreleased]: https://github.com/bavix/laravel-wallet/compare/8.4.1...develop
+[Unreleased]: https://github.com/bavix/laravel-wallet/compare/9.0.0...develop
+[9.0.0]: https://github.com/bavix/laravel-wallet/compare/8.4.1...9.0.0
[8.4.1]: https://github.com/bavix/laravel-wallet/compare/8.4.0...8.4.1
[8.4.0]: https://github.com/bavix/laravel-wallet/compare/8.3.0...8.4.0
[8.3.0]: https://github.com/bavix/laravel-wallet/compare/8.2.1...8.3.0
diff --git a/composer.json b/composer.json
index 4693270aa..f68a5e488 100644
--- a/composer.json
+++ b/composer.json
@@ -38,11 +38,11 @@
"infection/infection": "~0.26",
"laravel/cashier": "^13.8",
"nunomaduro/collision": "^6.2",
- "orchestra/testbench": "^7.3",
- "phpstan/phpstan": "^1.5",
+ "orchestra/testbench": "^7.4",
+ "phpstan/phpstan": "^1.6",
"phpunit/phpunit": "^9.5",
"rector/rector": "^0.12",
- "symplify/easy-coding-standard": "^10.1",
+ "symplify/easy-coding-standard": "^10.2",
"vimeo/psalm": "^4.22"
},
"suggest": {
diff --git a/config/config.php b/config/config.php
index 27aadf324..8ed1effac 100644
--- a/config/config.php
+++ b/config/config.php
@@ -4,6 +4,8 @@
use Bavix\Wallet\Internal\Assembler\AvailabilityDtoAssembler;
use Bavix\Wallet\Internal\Assembler\BalanceUpdatedEventAssembler;
+use Bavix\Wallet\Internal\Assembler\ExtraDtoAssembler;
+use Bavix\Wallet\Internal\Assembler\OptionDtoAssembler;
use Bavix\Wallet\Internal\Assembler\TransactionDtoAssembler;
use Bavix\Wallet\Internal\Assembler\TransactionQueryAssembler;
use Bavix\Wallet\Internal\Assembler\TransferDtoAssembler;
@@ -36,11 +38,14 @@
use Bavix\Wallet\Services\CastService;
use Bavix\Wallet\Services\ConsistencyService;
use Bavix\Wallet\Services\DiscountService;
+use Bavix\Wallet\Services\EagerLoaderService;
use Bavix\Wallet\Services\ExchangeService;
use Bavix\Wallet\Services\PrepareService;
use Bavix\Wallet\Services\PurchaseService;
use Bavix\Wallet\Services\RegulatorService;
use Bavix\Wallet\Services\TaxService;
+use Bavix\Wallet\Services\TransactionService;
+use Bavix\Wallet\Services\TransferService;
use Bavix\Wallet\Services\WalletService;
return [
@@ -96,10 +101,13 @@
'cast' => CastService::class,
'consistency' => ConsistencyService::class,
'discount' => DiscountService::class,
+ 'eager_loader' => EagerLoaderService::class,
'exchange' => ExchangeService::class,
'prepare' => PrepareService::class,
'purchase' => PurchaseService::class,
'tax' => TaxService::class,
+ 'transaction' => TransactionService::class,
+ 'transfer' => TransferService::class,
'wallet' => WalletService::class,
],
@@ -126,6 +134,8 @@
'assemblers' => [
'availability' => AvailabilityDtoAssembler::class,
'balance_updated_event' => BalanceUpdatedEventAssembler::class,
+ 'extra' => ExtraDtoAssembler::class,
+ 'option' => OptionDtoAssembler::class,
'transaction' => TransactionDtoAssembler::class,
'transfer_lazy' => TransferLazyDtoAssembler::class,
'transfer' => TransferDtoAssembler::class,
diff --git a/database/2018_11_06_222923_create_transactions_table.php b/database/2018_11_06_222923_create_transactions_table.php
index 3bef22cee..d24ecc0a9 100644
--- a/database/2018_11_06_222923_create_transactions_table.php
+++ b/database/2018_11_06_222923_create_transactions_table.php
@@ -7,8 +7,7 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
-class CreateTransactionsTable extends Migration
-{
+return new class() extends Migration {
public function up(): void
{
Schema::create($this->table(), function (Blueprint $table) {
@@ -42,4 +41,4 @@ private function table(): string
{
return (new Transaction())->getTable();
}
-}
+};
diff --git a/database/2018_11_07_192923_create_transfers_table.php b/database/2018_11_07_192923_create_transfers_table.php
index e0861bb2c..f003d941c 100644
--- a/database/2018_11_07_192923_create_transfers_table.php
+++ b/database/2018_11_07_192923_create_transfers_table.php
@@ -8,8 +8,7 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
-class CreateTransfersTable extends Migration
-{
+return new class() extends Migration {
public function up(): void
{
Schema::create($this->table(), function (Blueprint $table) {
@@ -70,4 +69,4 @@ private function transactionTable(): string
{
return (new Transaction())->getTable();
}
-}
+};
diff --git a/database/2018_11_15_124230_create_wallets_table.php b/database/2018_11_15_124230_create_wallets_table.php
index 2799b24f4..268ee8ae2 100644
--- a/database/2018_11_15_124230_create_wallets_table.php
+++ b/database/2018_11_15_124230_create_wallets_table.php
@@ -8,8 +8,7 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
-class CreateWalletsTable extends Migration
-{
+return new class() extends Migration {
public function up(): void
{
Schema::create($this->table(), function (Blueprint $table) {
@@ -63,4 +62,4 @@ private function transactionTable(): string
{
return (new Transaction())->getTable();
}
-}
+};
diff --git a/database/2021_11_02_202021_update_wallets_uuid_table.php b/database/2021_11_02_202021_update_wallets_uuid_table.php
index 5025bd988..754b99926 100644
--- a/database/2021_11_02_202021_update_wallets_uuid_table.php
+++ b/database/2021_11_02_202021_update_wallets_uuid_table.php
@@ -9,8 +9,7 @@
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Schema;
-class UpdateWalletsUuidTable extends Migration
-{
+return new class() extends Migration {
public function up(): void
{
if (Schema::hasColumn($this->table(), 'uuid')) {
@@ -18,7 +17,7 @@ public function up(): void
}
// upgrade from 6.x
- Schema::table($this->table(), function (Blueprint $table) {
+ Schema::table($this->table(), static function (Blueprint $table) {
$table->uuid('uuid')
->after('slug')
->nullable()
@@ -49,4 +48,4 @@ protected function table(): string
{
return (new Wallet())->getTable();
}
-}
+};
diff --git a/depfile.yaml b/depfile.yaml
deleted file mode 100644
index 797b6bc80..000000000
--- a/depfile.yaml
+++ /dev/null
@@ -1,249 +0,0 @@
-paths:
- - ./src
-
-layers:
- - name: Legacy
- collectors:
- - type: className
- regex: ^Bavix\\.*Legacy$
- - type: className
- regex: ^Bavix\\.*\\Objects\\Cart$
-
- - name: Contract
- collectors:
- - type: className
- regex: ^Bavix\\Wallet\\Interfaces\\.*
-
- - name: UI
- collectors:
- - type: className
- regex: ^Bavix\\.*\\Traits\\.*
-
- - name: UIException
- collectors:
- - type: className
- regex: ^Illuminate\\Database\\Eloquent\\ModelNotFoundException$
- - type: className
- regex: ^Bavix\\Wallet\\Exceptions\\.*
-
- # internal
- - name: InternalException
- collectors:
- - type: className
- regex: ^Bavix\\.*\\Internal\\Exceptions\\.*
-
- - name: Event
- collectors:
- - type: className
- regex: ^Bavix\\.*\\Internal\\Events\\.*Event$
-
- - name: EventInterface
- collectors:
- - type: className
- regex: ^Bavix\\.*\\Internal\\Events\\.*EventInterface$
-
- - name: Dto
- collectors:
- - type: className
- regex: ^Bavix\\.*\\Internal\\Dto\\.*Dto$
-
- - name: DtoInterface
- collectors:
- - type: className
- regex: ^Bavix\\.*\\Internal\\Dto\\.*DtoInterface$
-
- - name: AssemblerDto
- collectors:
- - type: className
- regex: ^Bavix\\.*\\Internal\\Assembler\\.*DtoAssembler$
-
- - name: AssemblerDtoInterface
- collectors:
- - type: className
- regex: ^Bavix\\.*\\Internal\\Assembler\\.*DtoAssemblerInterface$
-
- - name: QueryInterface
- collectors:
- - type: className
- regex: ^Bavix\\.*Internal\\Query\\.*QueryInterface$
-
- - name: Query
- collectors:
- - type: className
- regex: ^Bavix\\.*Internal\\Query\\.*Query$
-
- - name: RepositoryInterface
- collectors:
- - type: className
- regex: ^Bavix\\.*Internal\\Repository\\.*RepositoryInterface$
-
- - name: Repository
- collectors:
- - type: className
- regex: ^Bavix\\.*Internal\\Repository\\.*Repository$
-
- - name: Transform
- collectors:
- - type: className
- regex: ^Bavix\\.*Internal\\Transform\\.*DtoTransformer$
-
- - name: TransformInterface
- collectors:
- - type: className
- regex: ^Bavix\\.*Internal\\Transform\\.*DtoTransformerInterface$
-
- - name: Infra
- collectors:
- - type: className
- regex: ^Bavix\\.*Internal\\Service\\.*Service$
-
- # contracts
- - name: Model
- collectors:
- - type: className
- regex: ^Bavix\\.*Models\\Transaction$
- - type: className
- regex: ^Bavix\\.*Models\\Transfer$
- - type: className
- regex: ^Bavix\\.*Models\\Wallet$
-
- - name: Service
- collectors:
- - type: className
- regex: ^Bavix\\.*Services\\.*Service$
-
- - name: ServiceInterface
- collectors:
- - type: className
- regex: ^Bavix\\.*Service\\.*ServiceInterface$
- - type: className
- regex: ^Bavix\\.*Services\\.*ServiceInterface$
-
- # framework
- - name: EloquentModel
- collectors:
- - type: className
- regex: ^Illuminate\\Database\\Eloquent\\Model$
-
- - name: Config
- collectors:
- - type: className
- regex: ^Illuminate\\Config\\Repository$
-
- - name: Cache
- collectors:
- - type: className
- regex: ^Illuminate\\Contracts\\Cache\\Repository$
-
-ruleset:
- Contract:
- - InternalException
- - DtoInterface
- - UIException
- - Model
-
- UI:
- - AssemblerDtoInterface # delete as soon as possible
- - InternalException
- - ServiceInterface
- - UIException
- - Contract
- - Model
- - Legacy # delete as soon as possible
-
- UIException:
- - InternalException
-
- Infra:
- - InternalException
- - ServiceInterface
- - EventInterface
- - Config
- - Cache
-
- EventInterface:
- Event:
- - EventInterface
-
- DtoInterface:
- - Contract
- Dto:
- - DtoInterface
- - Contract
-
- Model:
- - AssemblerDtoInterface
- - InternalException
- - ServiceInterface
- - EloquentModel
- - UIException
- - Contract
- - Legacy
- - UI
-
- TransformInterface:
- - DtoInterface
- Transform:
- - TransformInterface
- - DtoInterface
-
- QueryInterface:
- Query:
- - QueryInterface
-
- RepositoryInterface:
- - InternalException
- - QueryInterface
- - DtoInterface
- - Model
- Repository:
- - RepositoryInterface
- - TransformInterface
- - InternalException
- - ServiceInterface # json service only
- - QueryInterface
- - DtoInterface
- - UIException
- - Model
-
- ServiceInterface:
- - InternalException
- - EventInterface
- - EloquentModel
- - DtoInterface
- - UIException
- - Contract
- - Model
- Service:
- - AssemblerDtoInterface
- - RepositoryInterface
- - InternalException
- - ServiceInterface
- - EloquentModel
- - DtoInterface
- - UIException
- - Contract
- - Model
-
- AssemblerDtoInterface:
- - EloquentModel
- - DtoInterface
- - Contract
- AssemblerDto:
- - AssemblerDtoInterface
- - QueryInterface
- - ServiceInterface # UUID
- - EloquentModel
- - DtoInterface
- - Contract
- - Dto
-
- Legacy:
- - AssemblerDtoInterface
- - InternalException
- - ServiceInterface
- - DtoInterface
- - UIException
- - Contract
- - Model
- - Dto # Cart from objects
diff --git a/deptrac.yaml b/deptrac.yaml
new file mode 100644
index 000000000..e549ce5f0
--- /dev/null
+++ b/deptrac.yaml
@@ -0,0 +1,247 @@
+parameters:
+ paths:
+ - ./src
+
+ layers:
+ - name: Legacy
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*\\Objects\\Cart$
+
+ - name: Contract
+ collectors:
+ - type: className
+ regex: ^Bavix\\Wallet\\Interfaces\\.*
+ - type: className
+ regex: ^Bavix\\Wallet\\External\\.*
+
+ - name: UI
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*\\Traits\\.*
+
+ - name: UIException
+ collectors:
+ - type: className
+ regex: ^Illuminate\\Database\\Eloquent\\ModelNotFoundException$
+ - type: className
+ regex: ^Bavix\\Wallet\\Exceptions\\.*
+
+ # internal
+ - name: InternalException
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*\\Internal\\Exceptions\\.*
+
+ - name: Event
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*\\Internal\\Events\\.*Event$
+
+ - name: EventInterface
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*\\Internal\\Events\\.*EventInterface$
+
+ - name: Dto
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*\\Internal\\Dto\\.*Dto$
+
+ - name: DtoInterface
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*\\Internal\\Dto\\.*DtoInterface$
+
+ - name: AssemblerDto
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*\\Internal\\Assembler\\.*DtoAssembler$
+
+ - name: AssemblerDtoInterface
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*\\Internal\\Assembler\\.*DtoAssemblerInterface$
+
+ - name: QueryInterface
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*Internal\\Query\\.*QueryInterface$
+
+ - name: Query
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*Internal\\Query\\.*Query$
+
+ - name: RepositoryInterface
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*Internal\\Repository\\.*RepositoryInterface$
+
+ - name: Repository
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*Internal\\Repository\\.*Repository$
+
+ - name: Transform
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*Internal\\Transform\\.*DtoTransformer$
+
+ - name: TransformInterface
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*Internal\\Transform\\.*DtoTransformerInterface$
+
+ - name: Infra
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*Internal\\Service\\.*Service$
+
+ # contracts
+ - name: Model
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*Models\\Transaction$
+ - type: className
+ regex: ^Bavix\\.*Models\\Transfer$
+ - type: className
+ regex: ^Bavix\\.*Models\\Wallet$
+
+ - name: Service
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*Services\\.*Service$
+
+ - name: ServiceInterface
+ collectors:
+ - type: className
+ regex: ^Bavix\\.*Service\\.*ServiceInterface$
+ - type: className
+ regex: ^Bavix\\.*Services\\.*ServiceInterface$
+
+ # framework
+ - name: EloquentModel
+ collectors:
+ - type: className
+ regex: ^Illuminate\\Database\\Eloquent\\Model$
+
+ - name: Config
+ collectors:
+ - type: className
+ regex: ^Illuminate\\Config\\Repository$
+
+ - name: Cache
+ collectors:
+ - type: className
+ regex: ^Illuminate\\Contracts\\Cache\\Repository$
+
+ ruleset:
+ Contract:
+ - InternalException
+ - DtoInterface
+ - UIException
+ - Model
+
+ UI:
+ - AssemblerDtoInterface # delete as soon as possible
+ - InternalException
+ - ServiceInterface
+ - UIException
+ - Contract
+ - Model
+ - Legacy # delete as soon as possible
+
+ UIException:
+ - InternalException
+
+ Infra:
+ - InternalException
+ - ServiceInterface
+ - EventInterface
+ - Config
+ - Cache
+
+ EventInterface:
+ Event:
+ - EventInterface
+
+ DtoInterface:
+ - Contract
+ Dto:
+ - DtoInterface
+ - Contract
+
+ Model:
+ - AssemblerDtoInterface
+ - InternalException
+ - ServiceInterface
+ - EloquentModel
+ - UIException
+ - Contract
+ - Legacy
+ - UI
+
+ TransformInterface:
+ - DtoInterface
+ Transform:
+ - TransformInterface
+ - DtoInterface
+
+ QueryInterface:
+ Query:
+ - QueryInterface
+
+ RepositoryInterface:
+ - InternalException
+ - QueryInterface
+ - DtoInterface
+ - Model
+ Repository:
+ - RepositoryInterface
+ - TransformInterface
+ - InternalException
+ - ServiceInterface # json service only
+ - QueryInterface
+ - DtoInterface
+ - UIException
+ - Model
+
+ ServiceInterface:
+ - InternalException
+ - EventInterface
+ - EloquentModel
+ - DtoInterface
+ - UIException
+ - Contract
+ - Model
+ Service:
+ - AssemblerDtoInterface
+ - RepositoryInterface
+ - InternalException
+ - ServiceInterface
+ - EloquentModel
+ - DtoInterface
+ - UIException
+ - Contract
+ - Model
+
+ AssemblerDtoInterface:
+ - EloquentModel
+ - DtoInterface
+ - Contract
+ AssemblerDto:
+ - AssemblerDtoInterface
+ - QueryInterface
+ - ServiceInterface # UUID
+ - EloquentModel
+ - DtoInterface
+ - Contract
+ - Dto
+
+ Legacy:
+ - InternalException
+ - ServiceInterface
+ - DtoInterface
+ - Contract
+ - Dto # Cart from objects
diff --git a/docs/basic-usage.md b/docs/basic-usage.md
index 8917f8fd9..0ee3d96f7 100644
--- a/docs/basic-usage.md
+++ b/docs/basic-usage.md
@@ -40,26 +40,59 @@ class User extends Model implements Customer
}
```
-Add the `HasWallet` trait and `Product` interface to `Item` model.
+Add the `HasWallet` trait and interface to `Item` model.
+
+Starting from version 9.x there are two product interfaces:
+- For an unlimited number of products (`ProductLimitedInterface`);
+- For a limited number of products (`ProductInterface`);
+
+An example with an unlimited number of products:
+```php
+use Bavix\Wallet\Traits\HasWallet;
+use Bavix\Wallet\Interfaces\Customer;
+use Bavix\Wallet\Interfaces\ProductInterface;
+
+class Item extends Model implements ProductInterface
+{
+ use HasWallet;
+
+ public function getAmountProduct(Customer $customer): int|string
+ {
+ return 100;
+ }
+
+ public function getMetaProduct(): ?array
+ {
+ return [
+ 'title' => $this->title,
+ 'description' => 'Purchase of Product #' . $this->id,
+ ];
+ }
+}
+```
+
+Example with a limited number of products:
```php
use Bavix\Wallet\Traits\HasWallet;
-use Bavix\Wallet\Interfaces\Product;
use Bavix\Wallet\Interfaces\Customer;
+use Bavix\Wallet\Interfaces\ProductLimitedInterface;
-class Item extends Model implements Product
+class Item extends Model implements ProductLimitedInterface
{
use HasWallet;
public function canBuy(Customer $customer, int $quantity = 1, bool $force = false): bool
{
/**
+ * This is where you implement the constraint logic.
+ *
* If the service can be purchased once, then
* return !$customer->paid($this);
*/
return true;
}
- public function getAmountProduct(Customer $customer)
+ public function getAmountProduct(Customer $customer): int|string
{
return 100;
}
@@ -74,6 +107,10 @@ class Item extends Model implements Product
}
```
+I do not recommend using the limited interface when working with a shopping cart.
+If you are working with a shopping cart, then you should override the `PurchaseServiceInterface` interface.
+With it, you can check the availability of all products with one request, there will be no N-queries in the database.
+
Proceed to purchase.
```php
diff --git a/docs/cart.md b/docs/cart.md
index 4f51263c6..8a783c96a 100644
--- a/docs/cart.md
+++ b/docs/cart.md
@@ -16,27 +16,59 @@ class User extends Model implements Customer
## Item Model
-Add the `HasWallet` trait and `Product` interface to Item model.
+Add the `HasWallet` trait and interface to `Item` model.
+Starting from version 9.x there are two product interfaces:
+- For an unlimited number of products (`ProductLimitedInterface`);
+- For a limited number of products (`ProductInterface`);
+
+An example with an unlimited number of products:
+```php
+use Bavix\Wallet\Traits\HasWallet;
+use Bavix\Wallet\Interfaces\Customer;
+use Bavix\Wallet\Interfaces\ProductInterface;
+
+class Item extends Model implements ProductInterface
+{
+ use HasWallet;
+
+ public function getAmountProduct(Customer $customer): int|string
+ {
+ return round($this->price * 100);
+ }
+
+ public function getMetaProduct(): ?array
+ {
+ return [
+ 'title' => $this->title,
+ 'description' => 'Purchase of Product #' . $this->id,
+ ];
+ }
+}
+```
+
+Example with a limited number of products:
```php
use Bavix\Wallet\Traits\HasWallet;
-use Bavix\Wallet\Interfaces\Product;
use Bavix\Wallet\Interfaces\Customer;
+use Bavix\Wallet\Interfaces\ProductLimitedInterface;
-class Item extends Model implements Product
+class Item extends Model implements ProductLimitedInterface
{
use HasWallet;
public function canBuy(Customer $customer, int $quantity = 1, bool $force = false): bool
{
/**
+ * This is where you implement the constraint logic.
+ *
* If the service can be purchased once, then
* return !$customer->paid($this);
*/
return true;
}
-
- public function getAmountProduct(Customer $customer)
+
+ public function getAmountProduct(Customer $customer): int|string
{
return round($this->price * 100);
}
@@ -45,12 +77,16 @@ class Item extends Model implements Product
{
return [
'title' => $this->title,
- 'description' => 'Purchase of Product #' . $this->getKey(),
+ 'description' => 'Purchase of Product #' . $this->id,
];
}
}
```
+I do not recommend using the limited interface when working with a shopping cart.
+If you are working with a shopping cart, then you should override the `PurchaseServiceInterface` interface.
+With it, you can check the availability of all products with one request, there will be no N-queries in the database.
+
## Fill the cart
Find the user and check the balance.
@@ -76,14 +112,19 @@ $products = Item::query()
$cart = app(Cart::class);
foreach ($products as $product) {
- // add product's
- $cart->addItem($product, $list[$product->slug]);
+ $cart = $cart->withItem($product, quantity: $list[$product->slug]);
}
-$user->deposit($cart->getTotal());
+$cartTotal = $cart->getTotal($user); // 15127
+$user->deposit($cartTotal);
$user->balanceInt; // 15127
$user->balanceFloat; // 151.27
+$cart = $cart->withItem(current($products), pricePerItem: 500); // 15127+500
+$user->deposit(500);
+$user->balanceInt; // 15627
+$user->balanceFloat; // 156.27
+
(bool)$user->payCart($cart); // true
$user->balanceFloat; // 0
```
diff --git a/docs/credit-limits.md b/docs/credit-limits.md
index 5360985df..f8e6ee8a6 100644
--- a/docs/credit-limits.md
+++ b/docs/credit-limits.md
@@ -11,7 +11,7 @@ An example of working with a credit limit:
/**
* @var \Bavix\Wallet\Interfaces\Customer $customer
* @var \Bavix\Wallet\Models\Wallet $wallet
- * @var \Bavix\Wallet\Interfaces\Product $product
+ * @var \Bavix\Wallet\Interfaces\ProductInterface $product
*/
$wallet = $customer->wallet; // get default wallet
$wallet->meta['credit'] = 10000; // credit limit
diff --git a/docs/dist/bundle.js b/docs/dist/bundle.js
index d812fc5d8..196c864d2 100644
--- a/docs/dist/bundle.js
+++ b/docs/dist/bundle.js
@@ -1,2 +1,2 @@
/*! For license information please see bundle.js.LICENSE.txt */
-(()=>{var e={669:(e,n,t)=>{e.exports=t(609)},448:(e,n,t)=>{"use strict";var r=t(867),i=t(26),o=t(372),a=t(327),u=t(97),c=t(109),f=t(985),s=t(61);e.exports=function(e){return new Promise((function(n,t){var l=e.data,d=e.headers,p=e.responseType;r.isFormData(l)&&delete d["Content-Type"];var h=new XMLHttpRequest;if(e.auth){var g=e.auth.username||"",m=e.auth.password?unescape(encodeURIComponent(e.auth.password)):"";d.Authorization="Basic "+btoa(g+":"+m)}var _=u(e.baseURL,e.url);function b(){if(h){var r="getAllResponseHeaders"in h?c(h.getAllResponseHeaders()):null,o={data:p&&"text"!==p&&"json"!==p?h.response:h.responseText,status:h.status,statusText:h.statusText,headers:r,config:e,request:h};i(n,t,o),h=null}}if(h.open(e.method.toUpperCase(),a(_,e.params,e.paramsSerializer),!0),h.timeout=e.timeout,"onloadend"in h?h.onloadend=b:h.onreadystatechange=function(){h&&4===h.readyState&&(0!==h.status||h.responseURL&&0===h.responseURL.indexOf("file:"))&&setTimeout(b)},h.onabort=function(){h&&(t(s("Request aborted",e,"ECONNABORTED",h)),h=null)},h.onerror=function(){t(s("Network Error",e,null,h)),h=null},h.ontimeout=function(){var n="timeout of "+e.timeout+"ms exceeded";e.timeoutErrorMessage&&(n=e.timeoutErrorMessage),t(s(n,e,e.transitional&&e.transitional.clarifyTimeoutError?"ETIMEDOUT":"ECONNABORTED",h)),h=null},r.isStandardBrowserEnv()){var v=(e.withCredentials||f(_))&&e.xsrfCookieName?o.read(e.xsrfCookieName):void 0;v&&(d[e.xsrfHeaderName]=v)}"setRequestHeader"in h&&r.forEach(d,(function(e,n){void 0===l&&"content-type"===n.toLowerCase()?delete d[n]:h.setRequestHeader(n,e)})),r.isUndefined(e.withCredentials)||(h.withCredentials=!!e.withCredentials),p&&"json"!==p&&(h.responseType=e.responseType),"function"==typeof e.onDownloadProgress&&h.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&h.upload&&h.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then((function(e){h&&(h.abort(),t(e),h=null)})),l||(l=null),h.send(l)}))}},609:(e,n,t)=>{"use strict";var r=t(867),i=t(849),o=t(321),a=t(185);function u(e){var n=new o(e),t=i(o.prototype.request,n);return r.extend(t,o.prototype,n),r.extend(t,n),t}var c=u(t(655));c.Axios=o,c.create=function(e){return u(a(c.defaults,e))},c.Cancel=t(263),c.CancelToken=t(972),c.isCancel=t(502),c.all=function(e){return Promise.all(e)},c.spread=t(713),c.isAxiosError=t(268),e.exports=c,e.exports.default=c},263:e=>{"use strict";function n(e){this.message=e}n.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},n.prototype.__CANCEL__=!0,e.exports=n},972:(e,n,t)=>{"use strict";var r=t(263);function i(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var n;this.promise=new Promise((function(e){n=e}));var t=this;e((function(e){t.reason||(t.reason=new r(e),n(t.reason))}))}i.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},i.source=function(){var e;return{token:new i((function(n){e=n})),cancel:e}},e.exports=i},502:e=>{"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},321:(e,n,t)=>{"use strict";var r=t(867),i=t(327),o=t(782),a=t(572),u=t(185),c=t(875),f=c.validators;function s(e){this.defaults=e,this.interceptors={request:new o,response:new o}}s.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{}).url=arguments[0]:e=e||{},(e=u(this.defaults,e)).method?e.method=e.method.toLowerCase():this.defaults.method?e.method=this.defaults.method.toLowerCase():e.method="get";var n=e.transitional;void 0!==n&&c.assertOptions(n,{silentJSONParsing:f.transitional(f.boolean,"1.0.0"),forcedJSONParsing:f.transitional(f.boolean,"1.0.0"),clarifyTimeoutError:f.transitional(f.boolean,"1.0.0")},!1);var t=[],r=!0;this.interceptors.request.forEach((function(n){"function"==typeof n.runWhen&&!1===n.runWhen(e)||(r=r&&n.synchronous,t.unshift(n.fulfilled,n.rejected))}));var i,o=[];if(this.interceptors.response.forEach((function(e){o.push(e.fulfilled,e.rejected)})),!r){var s=[a,void 0];for(Array.prototype.unshift.apply(s,t),s=s.concat(o),i=Promise.resolve(e);s.length;)i=i.then(s.shift(),s.shift());return i}for(var l=e;t.length;){var d=t.shift(),p=t.shift();try{l=d(l)}catch(e){p(e);break}}try{i=a(l)}catch(e){return Promise.reject(e)}for(;o.length;)i=i.then(o.shift(),o.shift());return i},s.prototype.getUri=function(e){return e=u(this.defaults,e),i(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},r.forEach(["delete","get","head","options"],(function(e){s.prototype[e]=function(n,t){return this.request(u(t||{},{method:e,url:n,data:(t||{}).data}))}})),r.forEach(["post","put","patch"],(function(e){s.prototype[e]=function(n,t,r){return this.request(u(r||{},{method:e,url:n,data:t}))}})),e.exports=s},782:(e,n,t)=>{"use strict";var r=t(867);function i(){this.handlers=[]}i.prototype.use=function(e,n,t){return this.handlers.push({fulfilled:e,rejected:n,synchronous:!!t&&t.synchronous,runWhen:t?t.runWhen:null}),this.handlers.length-1},i.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},i.prototype.forEach=function(e){r.forEach(this.handlers,(function(n){null!==n&&e(n)}))},e.exports=i},97:(e,n,t)=>{"use strict";var r=t(793),i=t(303);e.exports=function(e,n){return e&&!r(n)?i(e,n):n}},61:(e,n,t)=>{"use strict";var r=t(481);e.exports=function(e,n,t,i,o){var a=new Error(e);return r(a,n,t,i,o)}},572:(e,n,t)=>{"use strict";var r=t(867),i=t(527),o=t(502),a=t(655);function u(e){e.cancelToken&&e.cancelToken.throwIfRequested()}e.exports=function(e){return u(e),e.headers=e.headers||{},e.data=i.call(e,e.data,e.headers,e.transformRequest),e.headers=r.merge(e.headers.common||{},e.headers[e.method]||{},e.headers),r.forEach(["delete","get","head","post","put","patch","common"],(function(n){delete e.headers[n]})),(e.adapter||a.adapter)(e).then((function(n){return u(e),n.data=i.call(e,n.data,n.headers,e.transformResponse),n}),(function(n){return o(n)||(u(e),n&&n.response&&(n.response.data=i.call(e,n.response.data,n.response.headers,e.transformResponse))),Promise.reject(n)}))}},481:e=>{"use strict";e.exports=function(e,n,t,r,i){return e.config=n,t&&(e.code=t),e.request=r,e.response=i,e.isAxiosError=!0,e.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code}},e}},185:(e,n,t)=>{"use strict";var r=t(867);e.exports=function(e,n){n=n||{};var t={},i=["url","method","data"],o=["headers","auth","proxy","params"],a=["baseURL","transformRequest","transformResponse","paramsSerializer","timeout","timeoutMessage","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","decompress","maxContentLength","maxBodyLength","maxRedirects","transport","httpAgent","httpsAgent","cancelToken","socketPath","responseEncoding"],u=["validateStatus"];function c(e,n){return r.isPlainObject(e)&&r.isPlainObject(n)?r.merge(e,n):r.isPlainObject(n)?r.merge({},n):r.isArray(n)?n.slice():n}function f(i){r.isUndefined(n[i])?r.isUndefined(e[i])||(t[i]=c(void 0,e[i])):t[i]=c(e[i],n[i])}r.forEach(i,(function(e){r.isUndefined(n[e])||(t[e]=c(void 0,n[e]))})),r.forEach(o,f),r.forEach(a,(function(i){r.isUndefined(n[i])?r.isUndefined(e[i])||(t[i]=c(void 0,e[i])):t[i]=c(void 0,n[i])})),r.forEach(u,(function(r){r in n?t[r]=c(e[r],n[r]):r in e&&(t[r]=c(void 0,e[r]))}));var s=i.concat(o).concat(a).concat(u),l=Object.keys(e).concat(Object.keys(n)).filter((function(e){return-1===s.indexOf(e)}));return r.forEach(l,f),t}},26:(e,n,t)=>{"use strict";var r=t(61);e.exports=function(e,n,t){var i=t.config.validateStatus;t.status&&i&&!i(t.status)?n(r("Request failed with status code "+t.status,t.config,null,t.request,t)):e(t)}},527:(e,n,t)=>{"use strict";var r=t(867),i=t(655);e.exports=function(e,n,t){var o=this||i;return r.forEach(t,(function(t){e=t.call(o,e,n)})),e}},655:(e,n,t)=>{"use strict";var r=t(867),i=t(16),o=t(481),a={"Content-Type":"application/x-www-form-urlencoded"};function u(e,n){!r.isUndefined(e)&&r.isUndefined(e["Content-Type"])&&(e["Content-Type"]=n)}var c,f={transitional:{silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},adapter:(("undefined"!=typeof XMLHttpRequest||"undefined"!=typeof process&&"[object process]"===Object.prototype.toString.call(process))&&(c=t(448)),c),transformRequest:[function(e,n){return i(n,"Accept"),i(n,"Content-Type"),r.isFormData(e)||r.isArrayBuffer(e)||r.isBuffer(e)||r.isStream(e)||r.isFile(e)||r.isBlob(e)?e:r.isArrayBufferView(e)?e.buffer:r.isURLSearchParams(e)?(u(n,"application/x-www-form-urlencoded;charset=utf-8"),e.toString()):r.isObject(e)||n&&"application/json"===n["Content-Type"]?(u(n,"application/json"),function(e,n,t){if(r.isString(e))try{return(0,JSON.parse)(e),r.trim(e)}catch(e){if("SyntaxError"!==e.name)throw e}return(0,JSON.stringify)(e)}(e)):e}],transformResponse:[function(e){var n=this.transitional,t=n&&n.silentJSONParsing,i=n&&n.forcedJSONParsing,a=!t&&"json"===this.responseType;if(a||i&&r.isString(e)&&e.length)try{return JSON.parse(e)}catch(e){if(a){if("SyntaxError"===e.name)throw o(e,this,"E_JSON_PARSE");throw e}}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*"}}};r.forEach(["delete","get","head"],(function(e){f.headers[e]={}})),r.forEach(["post","put","patch"],(function(e){f.headers[e]=r.merge(a)})),e.exports=f},849:e=>{"use strict";e.exports=function(e,n){return function(){for(var t=new Array(arguments.length),r=0;r "']/),vn=/[&<>"']/g,yn=/[<>"']|&(?!#?\w+;)/,wn=/[<>"']|&(?!#?\w+;)/g,kn={"&":"&","<":"<",">":">",'"':""","'":"'"},xn=function(e){return kn[e]},Sn=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi;function An(e){return e.replace(Sn,(function(e,n){return"colon"===(n=n.toLowerCase())?":":"#"===n.charAt(0)?"x"===n.charAt(1)?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1)):""}))}var En=/(^|[^\[])\^/g,Tn=/[^\w:]/g,On=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i,Rn={},zn=/^[^:]+:\/*[^/]*$/,jn=/^([^:]+:)[\s\S]*$/,Cn=/^([^:]+:\/*[^/]*)[\s\S]*$/;function Ln(e,n,t){var r=e.length;if(0===r)return"";for(var i=0;i "+e+"
\n":"'+(t?e:at(e,!0))+"
\n"},e.prototype.blockquote=function(e){return""+(t?e:at(e,!0))+"\n"+e+"
\n"},e.prototype.html=function(e){return e},e.prototype.heading=function(e,n,t,r){return this.options.headerIds?"
\n":"
\n"},e.prototype.list=function(e,n,t){var r=n?"ol":"ul";return"<"+r+(n&&1!==t?' start="'+t+'"':"")+">\n"+e+""+r+">\n"},e.prototype.listitem=function(e){return"\n\n"+e+"\n"+n+"
\n"},e.prototype.tablerow=function(e){return"\n"+e+" \n"},e.prototype.tablecell=function(e,n){var t=n.header?"th":"td";return(n.align?"<"+t+' align="'+n.align+'">':"<"+t+">")+e+""+t+">\n"},e.prototype.strong=function(e){return""+e+""},e.prototype.em=function(e){return""+e+""},e.prototype.codespan=function(e){return""+e+""},e.prototype.br=function(){return this.options.xhtml?"
":"
"},e.prototype.del=function(e){return""+e+""},e.prototype.link=function(e,n,t){if(null===(e=ot(this.options.sanitize,this.options.baseUrl,e)))return t;var r='"+t+""},e.prototype.image=function(e,n,t){if(null===(e=ot(this.options.sanitize,this.options.baseUrl,e)))return t;var r='":">")},e.prototype.text=function(e){return e},e}(),ct=function(){function e(){}return e.prototype.strong=function(e){return e},e.prototype.em=function(e){return e},e.prototype.codespan=function(e){return e},e.prototype.del=function(e){return e},e.prototype.html=function(e){return e},e.prototype.text=function(e){return e},e.prototype.link=function(e,n,t){return""+t},e.prototype.image=function(e,n,t){return""+t},e.prototype.br=function(){return""},e}(),ft=function(){function e(){this.seen={}}return e.prototype.serialize=function(e){return e.toLowerCase().trim().replace(/<[!\/a-z].*?>/gi,"").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-")},e.prototype.getNextSafeSlug=function(e,n){var t=e,r=0;if(this.seen.hasOwnProperty(t)){r=this.seen[e];do{t=e+"-"+ ++r}while(this.seen.hasOwnProperty(t))}return n||(this.seen[e]=r,this.seen[t]=0),t},e.prototype.slug=function(e,n){void 0===n&&(n={});var t=this.serialize(e);return this.getNextSafeSlug(t,n.dryrun)},e}(),st=_n.defaults,lt=$n,dt=function(){function e(e){this.options=e||st,this.options.renderer=this.options.renderer||new ut,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new ct,this.slugger=new ft}return e.parse=function(n,t){return new e(t).parse(n)},e.parseInline=function(n,t){return new e(t).parseInline(n)},e.prototype.parse=function(e,n){void 0===n&&(n=!0);var t,r,i,o,a,u,c,f,s,l,d,p,h,g,m,_,b,v,y="",w=e.length;for(t=0;t
"+gt(e.message+"",!0)+"
";throw e}}vt.options=vt.setOptions=function(e){return pt(vt.defaults,e),_t(vt.defaults),vt},vt.getDefaults=mt,vt.defaults=bt,vt.use=function(e){var n=pt({},e);if(e.renderer){var t=vt.defaults.renderer||new ut,r=function(n){var r=t[n];t[n]=function(){for(var i=[],o=arguments.length;o--;)i[o]=arguments[o];var a=e.renderer[n].apply(t,i);return!1===a&&(a=r.apply(t,i)),a}};for(var i in e.renderer)r(i);n.renderer=t}if(e.tokenizer){var o=vt.defaults.tokenizer||new Hn,a=function(n){var t=o[u];o[u]=function(){for(var n=[],r=arguments.length;r--;)n[r]=arguments[r];var i=e.tokenizer[u].apply(o,n);return!1===i&&(i=t.apply(o,n)),i}};for(var u in e.tokenizer)a();n.tokenizer=o}if(e.walkTokens){var c=vt.defaults.walkTokens;n.walkTokens=function(n){e.walkTokens(n),c&&c(n)}}vt.setOptions(n)},vt.walkTokens=function(e,n){for(var t=0,r=e;t
"+gt(e.message+"",!0)+"";throw e}},vt.Parser=dt,vt.parser=dt.parse,vt.Renderer=ut,vt.TextRenderer=ct,vt.Lexer=rt,vt.lexer=rt.lex,vt.Tokenizer=Hn,vt.Slugger=ft,vt.parse=vt;var yt=vt;function wt(e,n){if(void 0===n&&(n=''),!e||!e.length)return"";var t="";return e.forEach((function(e){var r=e.title.replace(/(<([^>]+)>)/g,"");t+='
'+n.slice(5).trim()+"
"}function xt(e,n){var t=[],r={};return e.forEach((function(e){var i=e.level||1,o=i-1;i>n||(r[o]?r[o].children=(r[o].children||[]).concat(e):t.push(e),r[i]=e)})),t}var St={},At=/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g;function Et(e){return e.toLowerCase()}function Tt(e){if("string"!=typeof e)return"";var n=e.trim().replace(/[A-Z]+/g,Et).replace(/<[^>]+>/g,"").replace(At,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),t=St[n];return t=r.call(St,n)?t+1:0,St[n]=t,t&&(n=n+"-"+t),n}function Ot(e,n){return''+jt.highlight(e.replace(/@DOCSIFY_QM@/g,"`"),t,n)+""}}({renderer:e}),u.link=function(e){var n=e.router,t=e.linkTarget,r=e.linkRel,i=e.compilerClass;return e.renderer.link=function(e,o,a){void 0===o&&(o="");var u=[],c=Rt(o),f=c.str,s=c.config;return t=s.target||t,r="_blank"===t?i.config.externalLinkRel||"noopener":"",o=f,z(e)||i._matchNotCompileLink(e)||s.ignore?(z(e)||"./"!==e.slice(0,2)||(e=document.URL.replace(/\/(?!.*\/).*/,"/").replace("#/./","")+e),u.push(0===e.indexOf("mailto:")?"":'target="'+t+'"'),u.push(0===e.indexOf("mailto:")?"":""!==r?' rel="'+r+'"':"")):(e===i.config.homepage&&(e="README"),e=n.toURL(e,null,n.getCurrentPath())),s.crossorgin&&"_self"===t&&"history"===i.config.routerMode&&-1===i.config.crossOriginLinks.indexOf(e)&&i.config.crossOriginLinks.push(e),s.disabled&&(u.push("disabled"),e="javascript:void(0)"),s.class&&u.push('class="'+s.class+'"'),s.id&&u.push('id="'+s.id+'"'),o&&u.push('title="'+o+'"'),'"+a+""}}({renderer:e,router:i,linkTarget:t,linkRel:r,compilerClass:a}),u.paragraph=function(e){return e.renderer.paragraph=function(e){return/^!>/.test(e)?kt("tip",e):/^\?>/.test(e)?kt("warn",e):""+e+"
"}}({renderer:e}),u.image=function(e){var n=e.contentBase,t=e.router;return e.renderer.image=function(e,r,i){var o=e,a=[],u=Rt(r),c=u.str,f=u.config;if(r=c,f["no-zoom"]&&a.push("data-no-zoom"),r&&a.push('title="'+r+'"'),f.size){var s=f.size.split("x"),l=s[0],d=s[1];d?a.push('width="'+l+'" height="'+d+'"'):a.push('width="'+l+'"')}return f.class&&a.push('class="'+f.class+'"'),f.id&&a.push('id="'+f.id+'"'),z(e)||(o=I(n,C(t.getCurrentPath()),e)),a.length>0?'"+e.content+"
\n\n"})),i.classList.add("show"),o.classList.add("show"),i.innerHTML=d||''+f+"
",c.hideOtherSidebarContent&&(u&&u.classList.add("hide"),s&&s.classList.add("hide"))}function l(e){c=e}var d={placeholder:"Type to search",noData:"No Results!",paths:"auto",depth:2,maxAge:864e5,hideOtherSidebarContent:!1,namespace:void 0,pathNamespaces:void 0};$docsify.plugins=[].concat((function(e,n){var t=Docsify.util,r=n.config.search||d;Array.isArray(r)?d.paths=r:"object"==typeof r&&(d.paths=Array.isArray(r.paths)?r.paths:"auto",d.maxAge=t.isPrimitive(r.maxAge)?r.maxAge:d.maxAge,d.placeholder=r.placeholder||d.placeholder,d.noData=r.noData||d.noData,d.depth=r.depth||d.depth,d.hideOtherSidebarContent=r.hideOtherSidebarContent||d.hideOtherSidebarContent,d.namespace=r.namespace||d.namespace,d.pathNamespaces=r.pathNamespaces||d.pathNamespaces);var i="auto"===d.paths;e.mounted((function(e){(function(e,n){var t,r,i,o,a=n.router.parse().query.s;l(e),Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0 7px;\n line-height: 36px;\n font-size: 14px;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n.search .clear-button {\n cursor: pointer;\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"),function(e){void 0===e&&(e="");var n='"']/),vn=/[&<>"']/g,yn=/[<>"']|&(?!#?\w+;)/,wn=/[<>"']|&(?!#?\w+;)/g,kn={"&":"&","<":"<",">":">",'"':""","'":"'"},xn=function(e){return kn[e]},Sn=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi;function An(e){return e.replace(Sn,(function(e,n){return"colon"===(n=n.toLowerCase())?":":"#"===n.charAt(0)?"x"===n.charAt(1)?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1)):""}))}var En=/(^|[^\[])\^/g,On=/[^\w:]/g,Tn=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i,Rn={},zn=/^[^:]+:\/*[^/]*$/,Cn=/^([^:]+:)[\s\S]*$/,Ln=/^([^:]+:\/*[^/]*)[\s\S]*$/;function jn(e,n,t){var r=e.length;if(0===r)return"";for(var i=0;i "+e+"
\n":"'+(t?e:at(e,!0))+"
\n"},e.prototype.blockquote=function(e){return""+(t?e:at(e,!0))+"\n"+e+"
\n"},e.prototype.html=function(e){return e},e.prototype.heading=function(e,n,t,r){return this.options.headerIds?"
\n":"
\n"},e.prototype.list=function(e,n,t){var r=n?"ol":"ul";return"<"+r+(n&&1!==t?' start="'+t+'"':"")+">\n"+e+""+r+">\n"},e.prototype.listitem=function(e){return"\n\n"+e+"\n"+n+"
\n"},e.prototype.tablerow=function(e){return"\n"+e+" \n"},e.prototype.tablecell=function(e,n){var t=n.header?"th":"td";return(n.align?"<"+t+' align="'+n.align+'">':"<"+t+">")+e+""+t+">\n"},e.prototype.strong=function(e){return""+e+""},e.prototype.em=function(e){return""+e+""},e.prototype.codespan=function(e){return""+e+""},e.prototype.br=function(){return this.options.xhtml?"
":"
"},e.prototype.del=function(e){return""+e+""},e.prototype.link=function(e,n,t){if(null===(e=ot(this.options.sanitize,this.options.baseUrl,e)))return t;var r='"+t+""},e.prototype.image=function(e,n,t){if(null===(e=ot(this.options.sanitize,this.options.baseUrl,e)))return t;var r='":">")},e.prototype.text=function(e){return e},e}(),ct=function(){function e(){}return e.prototype.strong=function(e){return e},e.prototype.em=function(e){return e},e.prototype.codespan=function(e){return e},e.prototype.del=function(e){return e},e.prototype.html=function(e){return e},e.prototype.text=function(e){return e},e.prototype.link=function(e,n,t){return""+t},e.prototype.image=function(e,n,t){return""+t},e.prototype.br=function(){return""},e}(),ft=function(){function e(){this.seen={}}return e.prototype.serialize=function(e){return e.toLowerCase().trim().replace(/<[!\/a-z].*?>/gi,"").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-")},e.prototype.getNextSafeSlug=function(e,n){var t=e,r=0;if(this.seen.hasOwnProperty(t)){r=this.seen[e];do{t=e+"-"+ ++r}while(this.seen.hasOwnProperty(t))}return n||(this.seen[e]=r,this.seen[t]=0),t},e.prototype.slug=function(e,n){void 0===n&&(n={});var t=this.serialize(e);return this.getNextSafeSlug(t,n.dryrun)},e}(),st=mn.defaults,lt=Dn,dt=function(){function e(e){this.options=e||st,this.options.renderer=this.options.renderer||new ut,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new ct,this.slugger=new ft}return e.parse=function(n,t){return new e(t).parse(n)},e.parseInline=function(n,t){return new e(t).parseInline(n)},e.prototype.parse=function(e,n){void 0===n&&(n=!0);var t,r,i,o,a,u,c,f,s,l,d,p,h,g,_,m,b,v,y="",w=e.length;for(t=0;t
"+gt(e.message+"",!0)+"";throw e}}vt.options=vt.setOptions=function(e){return pt(vt.defaults,e),mt(vt.defaults),vt},vt.getDefaults=_t,vt.defaults=bt,vt.use=function(e){var n=pt({},e);if(e.renderer){var t=vt.defaults.renderer||new ut,r=function(n){var r=t[n];t[n]=function(){for(var i=[],o=arguments.length;o--;)i[o]=arguments[o];var a=e.renderer[n].apply(t,i);return!1===a&&(a=r.apply(t,i)),a}};for(var i in e.renderer)r(i);n.renderer=t}if(e.tokenizer){var o=vt.defaults.tokenizer||new Hn,a=function(n){var t=o[u];o[u]=function(){for(var n=[],r=arguments.length;r--;)n[r]=arguments[r];var i=e.tokenizer[u].apply(o,n);return!1===i&&(i=t.apply(o,n)),i}};for(var u in e.tokenizer)a();n.tokenizer=o}if(e.walkTokens){var c=vt.defaults.walkTokens;n.walkTokens=function(n){e.walkTokens(n),c&&c(n)}}vt.setOptions(n)},vt.walkTokens=function(e,n){for(var t=0,r=e;t
"+gt(e.message+"",!0)+"";throw e}},vt.Parser=dt,vt.parser=dt.parse,vt.Renderer=ut,vt.TextRenderer=ct,vt.Lexer=rt,vt.lexer=rt.lex,vt.Tokenizer=Hn,vt.Slugger=ft,vt.parse=vt;var yt=vt;function wt(e,n){if(void 0===n&&(n=''),!e||!e.length)return"";var t="";return e.forEach((function(e){var r=e.title.replace(/(<([^>]+)>)/g,"");t+='
'+n.slice(5).trim()+"
"}function xt(e,n){var t=[],r={};return e.forEach((function(e){var i=e.level||1,o=i-1;i>n||(r[o]?r[o].children=(r[o].children||[]).concat(e):t.push(e),r[i]=e)})),t}var St={},At=/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g;function Et(e){return e.toLowerCase()}function Ot(e){if("string"!=typeof e)return"";var n=e.trim().replace(/[A-Z]+/g,Et).replace(/<[^>]+>/g,"").replace(At,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),t=St[n];return t=r.call(St,n)?t+1:0,St[n]=t,t&&(n=n+"-"+t),n}function Tt(e,n){return''+Ct.highlight(e.replace(/@DOCSIFY_QM@/g,"`"),t,n)+""}}({renderer:e}),u.link=function(e){var n=e.router,t=e.linkTarget,r=e.linkRel,i=e.compilerClass;return e.renderer.link=function(e,o,a){void 0===o&&(o="");var u=[],c=Rt(o),f=c.str,s=c.config;return t=s.target||t,r="_blank"===t?i.config.externalLinkRel||"noopener":"",o=f,z(e)||i._matchNotCompileLink(e)||s.ignore?(z(e)||"./"!==e.slice(0,2)||(e=document.URL.replace(/\/(?!.*\/).*/,"/").replace("#/./","")+e),u.push(0===e.indexOf("mailto:")?"":'target="'+t+'"'),u.push(0===e.indexOf("mailto:")?"":""!==r?' rel="'+r+'"':"")):(e===i.config.homepage&&(e="README"),e=n.toURL(e,null,n.getCurrentPath())),s.crossorgin&&"_self"===t&&"history"===i.config.routerMode&&-1===i.config.crossOriginLinks.indexOf(e)&&i.config.crossOriginLinks.push(e),s.disabled&&(u.push("disabled"),e="javascript:void(0)"),s.class&&u.push('class="'+s.class+'"'),s.id&&u.push('id="'+s.id+'"'),o&&u.push('title="'+o+'"'),'"+a+""}}({renderer:e,router:i,linkTarget:t,linkRel:r,compilerClass:a}),u.paragraph=function(e){return e.renderer.paragraph=function(e){return/^!>/.test(e)?kt("tip",e):/^\?>/.test(e)?kt("warn",e):""+e+"
"}}({renderer:e}),u.image=function(e){var n=e.contentBase,t=e.router;return e.renderer.image=function(e,r,i){var o=e,a=[],u=Rt(r),c=u.str,f=u.config;if(r=c,f["no-zoom"]&&a.push("data-no-zoom"),r&&a.push('title="'+r+'"'),f.size){var s=f.size.split("x"),l=s[0],d=s[1];d?a.push('width="'+l+'" height="'+d+'"'):a.push('width="'+l+'"')}return f.class&&a.push('class="'+f.class+'"'),f.id&&a.push('id="'+f.id+'"'),z(e)||(o=$(n,L(t.getCurrentPath()),e)),a.length>0?'"+e.content+"
\n\n"})),i.classList.add("show"),o.classList.add("show"),i.innerHTML=d||''+f+"
",c.hideOtherSidebarContent&&(u&&u.classList.add("hide"),s&&s.classList.add("hide"))}function l(e){c=e}var d={placeholder:"Type to search",noData:"No Results!",paths:"auto",depth:2,maxAge:864e5,hideOtherSidebarContent:!1,namespace:void 0,pathNamespaces:void 0};$docsify.plugins=[].concat((function(e,n){var t=Docsify.util,r=n.config.search||d;Array.isArray(r)?d.paths=r:"object"==typeof r&&(d.paths=Array.isArray(r.paths)?r.paths:"auto",d.maxAge=t.isPrimitive(r.maxAge)?r.maxAge:d.maxAge,d.placeholder=r.placeholder||d.placeholder,d.noData=r.noData||d.noData,d.depth=r.depth||d.depth,d.hideOtherSidebarContent=r.hideOtherSidebarContent||d.hideOtherSidebarContent,d.namespace=r.namespace||d.namespace,d.pathNamespaces=r.pathNamespaces||d.pathNamespaces);var i="auto"===d.paths;e.mounted((function(e){(function(e,n){var t,r,i,o,a=n.router.parse().query.s;l(e),Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0 7px;\n line-height: 36px;\n font-size: 14px;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n.search .clear-button {\n cursor: pointer;\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"),function(e){void 0===e&&(e="");var n='