Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [9.6.0] - 2022-10-27
### Added
* Full support for standard transactions and laravel nova #589

## [9.5.0] - 2022-10-06
### Added
* Improved performance api handles #576
Expand Down Expand Up @@ -944,7 +948,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/9.5.0...master
[Unreleased]: https://github.com/bavix/laravel-wallet/compare/9.6.0...master
[9.6.0]: https://github.com/bavix/laravel-wallet/compare/9.5.0...9.6.0
[9.5.0]: https://github.com/bavix/laravel-wallet/compare/9.4.0...9.5.0
[9.4.0]: https://github.com/bavix/laravel-wallet/compare/9.3.0...9.4.0
[9.3.0]: https://github.com/bavix/laravel-wallet/compare/9.2.0...9.3.0
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"brick/math": "~0.8",
"doctrine/dbal": "^2.8|^3.0",
"illuminate/contracts": "^9.0",
"illuminate/database": "^9.0",
"illuminate/database": "^9.37",
"ramsey/uuid": "^4.0"
},
"require-dev": {
Expand Down
2 changes: 2 additions & 0 deletions config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Bavix\Wallet\Internal\Repository\TransferRepository;
use Bavix\Wallet\Internal\Repository\WalletRepository;
use Bavix\Wallet\Internal\Service\ClockService;
use Bavix\Wallet\Internal\Service\ConnectionService;
use Bavix\Wallet\Internal\Service\DatabaseService;
use Bavix\Wallet\Internal\Service\DispatcherService;
use Bavix\Wallet\Internal\Service\JsonService;
Expand Down Expand Up @@ -82,6 +83,7 @@
*/
'internal' => [
'clock' => ClockService::class,
'connection' => ConnectionService::class,
'database' => DatabaseService::class,
'dispatcher' => DispatcherService::class,
'json' => JsonService::class,
Expand Down
8 changes: 8 additions & 0 deletions deptrac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ parameters:
- type: className
regex: ^Bavix\\.*\\Internal\\Exceptions\\.*

- name: InternalListener
collectors:
- type: className
regex: ^Bavix\\.*\\Internal\\Listeners\\.*

- name: Event
collectors:
- type: className
Expand Down Expand Up @@ -250,6 +255,9 @@ parameters:
- UIException
- Model

InternalListener:
- ServiceInterface

ServiceInterface:
- InternalException
- EventInterface
Expand Down
2 changes: 2 additions & 0 deletions docs/nova-action.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Nova Action

> Use only if you have a package version below 9.6

As you know, the package works with internal state. You can read more [here](https://github.com/bavix/laravel-wallet/pull/412) and [here](https://github.com/bavix/laravel-wallet/issues/455).

The action runs inside a transaction, which means you need to reset the transaction manually.
Expand Down
23 changes: 23 additions & 0 deletions docs/transaction.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
## Transaction

> Starting from version 9.6 you can safely use standard framework transactions.

You definitely need to know the feature of transactions. The wallet is automatically blocked from the moment it is used until the end of the transaction. Therefore, it is necessary to use the wallet closer to the end of the transaction.

Very important! Almost all wallet transactions are blocking.

```php
use Illuminate\Support\Facades\DB;

DB::beginTransaction();
$wallet->balanceInt; // now the wallet is blocked
doingMagic(); // running for a long time.
DB::commit(); // here will unlock the wallet
```

The point is that you need to minimize operations within transactions as much as possible. The longer the transaction, the longer the wallet lock.
The maximum wallet blocking time is set in the configuration. The longer the transaction takes, the more likely it is to get a race for the wallet.

---

If you are using a version below 9.6, then the following is relevant for you:
--

> Since version 9.2 it is safer to use AtomicServiceInterface.

> Is it possible to use laravel's inline transactions? No, It is Immpossible. This limitation is due to the internal architecture of the package. To achieve the maximum speed of work, work with an internal state of balance was needed. Starting with version 8.2, a special error has appeared that will inform you about incorrect work with the `TransactionStartException` package.
Expand Down
5 changes: 1 addition & 4 deletions ecs.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
use Symplify\CodingStandard\Fixer\LineLength\DocBlockLineLengthFixer;
use Symplify\CodingStandard\Fixer\LineLength\LineLengthFixer;
use Symplify\EasyCodingStandard\Config\ECSConfig;
use Symplify\EasyCodingStandard\ValueObject\Option;
use Symplify\EasyCodingStandard\ValueObject\Set\SetList;

return static function (ECSConfig $containerConfigurator): void {
$containerConfigurator->parallel();
$services = $containerConfigurator->services();
$services->set(ArraySyntaxFixer::class)
->call('configure', [[
Expand All @@ -22,9 +22,6 @@
$services->set(DeclareStrictTypesFixer::class);
$services->set(LineLengthFixer::class);

$parameters = $containerConfigurator->parameters();
$parameters->set(Option::PARALLEL, true);

$containerConfigurator->paths([
__DIR__ . '/config',
__DIR__ . '/database',
Expand Down
5 changes: 5 additions & 0 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

use Rector\CodeQuality\Rector\PropertyFetch\ExplicitMethodCallOverMagicGetSetRector;
use Rector\Config\RectorConfig;
use Rector\Laravel\Rector\Assign\CallOnAppArrayAccessToStandaloneAssignRector;
use Rector\Laravel\Rector\ClassMethod\AddParentRegisterToEventServiceProviderRector;
use Rector\Laravel\Set\LaravelSetList;
use Rector\Php74\Rector\Property\TypedPropertyRector;
use Rector\PHPUnit\Set\PHPUnitSetList;
use Rector\Set\ValueObject\SetList;

return static function (RectorConfig $containerConfigurator): void {
$containerConfigurator->parallel();
$containerConfigurator->paths([
__DIR__ . '/src',
__DIR__ . '/tests',
Expand All @@ -29,4 +32,6 @@

// register a single rule
$services->set(TypedPropertyRector::class);
$services->set(CallOnAppArrayAccessToStandaloneAssignRector::class);
$services->set(AddParentRegisterToEventServiceProviderRector::class);
};
2 changes: 0 additions & 2 deletions src/Internal/Exceptions/ExceptionInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,4 @@ interface ExceptionInterface extends Throwable
public const TRANSACTION_FAILED = 1 << 10;

public const MODEL_NOT_FOUND = 1 << 11;

public const TRANSACTION_START = 1 << 12;
}
21 changes: 21 additions & 0 deletions src/Internal/Exceptions/TransactionRollbackException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Bavix\Wallet\Internal\Exceptions;

use InvalidArgumentException;

final class TransactionRollbackException extends InvalidArgumentException implements ExceptionInterface
{
public function __construct(
private mixed $result
) {
parent::__construct();
}

public function getResult(): mixed
{
return $this->result;
}
}
11 changes: 0 additions & 11 deletions src/Internal/Exceptions/TransactionStartException.php

This file was deleted.

18 changes: 18 additions & 0 deletions src/Internal/Listeners/TransactionBeginningListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Bavix\Wallet\Internal\Listeners;

use Bavix\Wallet\Internal\Service\ConnectionServiceInterface;
use Bavix\Wallet\Services\RegulatorServiceInterface;

final class TransactionBeginningListener
{
public function __invoke(): void
{
if (app(ConnectionServiceInterface::class)->get()->transactionLevel() === 1) {
app(RegulatorServiceInterface::class)->purge();
}
}
}
18 changes: 18 additions & 0 deletions src/Internal/Listeners/TransactionCommittedListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Bavix\Wallet\Internal\Listeners;

use Bavix\Wallet\Internal\Service\ConnectionServiceInterface;
use Bavix\Wallet\Services\RegulatorServiceInterface;

final class TransactionCommittedListener
{
public function __invoke(): void
{
if (app(ConnectionServiceInterface::class)->get()->transactionLevel() === 0) {
app(RegulatorServiceInterface::class)->committed();
}
}
}
22 changes: 22 additions & 0 deletions src/Internal/Listeners/TransactionCommittingListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Bavix\Wallet\Internal\Listeners;

use Bavix\Wallet\Internal\Service\ConnectionServiceInterface;
use Bavix\Wallet\Services\RegulatorServiceInterface;

final class TransactionCommittingListener
{
public function __invoke(): void
{
/**
* In fact, this if is not needed here.
* But in order to protect the code from changes in the framework, I added a check here.
*/
if (app(ConnectionServiceInterface::class)->get()->transactionLevel() === 1) {
app(RegulatorServiceInterface::class)->committing();
}
}
}
18 changes: 18 additions & 0 deletions src/Internal/Listeners/TransactionRolledBackListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Bavix\Wallet\Internal\Listeners;

use Bavix\Wallet\Internal\Service\ConnectionServiceInterface;
use Bavix\Wallet\Services\RegulatorServiceInterface;

final class TransactionRolledBackListener
{
public function __invoke(): void
{
if (app(ConnectionServiceInterface::class)->get()->transactionLevel() === 0) {
app(RegulatorServiceInterface::class)->purge();
}
}
}
9 changes: 9 additions & 0 deletions src/Internal/Repository/WalletRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ public function create(array $attributes): Wallet
*/
public function updateBalances(array $data): int
{
// One element gives x10 speedup, on some data
if (count($data) === 1) {
return $this->wallet->newQuery()
->whereKey(key($data))
->update([
'balance' => current($data),
]);
}

$cases = [];
foreach ($data as $walletId => $balance) {
$cases[] = 'WHEN id = ' . $walletId . ' THEN ' . $balance;
Expand Down
26 changes: 26 additions & 0 deletions src/Internal/Service/ConnectionService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Bavix\Wallet\Internal\Service;

use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\ConnectionResolverInterface;

/**
* @internal
*/
final class ConnectionService implements ConnectionServiceInterface
{
private ConnectionInterface $connection;

public function __construct(ConnectionResolverInterface $connectionResolver)
{
$this->connection = $connectionResolver->connection(config('wallet.database.connection'));
}

public function get(): ConnectionInterface
{
return $this->connection;
}
}
12 changes: 12 additions & 0 deletions src/Internal/Service/ConnectionServiceInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Bavix\Wallet\Internal\Service;

use Illuminate\Database\ConnectionInterface;

interface ConnectionServiceInterface
{
public function get(): ConnectionInterface;
}
Loading