Completed
Pull Request — master (#40)
by Бабичев
06:05
created

HasWallet::addBalance()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 2
dl 0
loc 11
ccs 5
cts 5
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Bavix\Wallet\Traits;
4
5
use Bavix\Wallet\Exceptions\BalanceIsEmpty;
6
use Bavix\Wallet\Exceptions\InsufficientFunds;
7
use Bavix\Wallet\Interfaces\Wallet;
8
use Bavix\Wallet\Models\Transaction;
9
use Bavix\Wallet\Models\Transfer;
10
use Bavix\Wallet\Models\Wallet as WalletModel;
11
use Bavix\Wallet\Objects\Bring;
12
use Bavix\Wallet\Objects\Operation;
13
use Bavix\Wallet\Services\CommonService;
14
use Bavix\Wallet\Services\ProxyService;
15
use Bavix\Wallet\Services\WalletService;
16
use Illuminate\Database\Eloquent\Relations\MorphMany;
17
use Illuminate\Database\Eloquent\Relations\MorphOne;
18
use Illuminate\Support\Collection;
19
use Illuminate\Support\Facades\DB;
20
use Throwable;
21
use function app;
22
use function config;
23
use function current;
24
25
/**
26
 * Trait HasWallet
27
 *
28
 * @package Bavix\Wallet\Traits
29
 *
30
 * @property-read WalletModel $wallet
31
 * @property-read Collection|WalletModel[] $wallets
32
 * @property-read int $balance
33
 */
34
trait HasWallet
35
{
36
37
    /**
38
     * The input means in the system
39
     *
40
     * @param int $amount
41
     * @param array|null $meta
42
     * @param bool $confirmed
43
     *
44
     * @return Transaction
45
     */
46 31
    public function deposit(int $amount, ?array $meta = null, bool $confirmed = true): Transaction
47
    {
48
        return DB::transaction(function () use ($amount, $meta, $confirmed) {
49 31
            return app(CommonService::class)
50 31
                ->deposit($this, $amount, $meta, $confirmed);
0 ignored issues
show
Bug introduced by
$this of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $wallet of Bavix\Wallet\Services\CommonService::deposit(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

50
                ->deposit(/** @scrutinizer ignore-type */ $this, $amount, $meta, $confirmed);
Loading history...
51 31
        });
52
    }
53
54
    /**
55
     * Magic laravel framework method, makes it
56
     *  possible to call property balance
57
     *
58
     * Example:
59
     *  $user1 = User::first()->load('wallet');
60
     *  $user2 = User::first()->load('wallet');
61
     *
62
     * Without static:
63
     *  var_dump($user1->balance, $user2->balance); // 100 100
64
     *  $user1->deposit(100);
65
     *  $user2->deposit(100);
66
     *  var_dump($user1->balance, $user2->balance); // 200 200
67
     *
68
     * With static:
69
     *  var_dump($user1->balance, $user2->balance); // 100 100
70
     *  $user1->deposit(100);
71
     *  var_dump($user1->balance); // 200
72
     *  $user2->deposit(100);
73
     *  var_dump($user2->balance); // 300
74
     *
75
     * @return int
76
     * @throws
77
     */
78 40
    public function getBalanceAttribute(): int
79
    {
80 40
        if ($this instanceof WalletModel) {
81 40
            $this->exists or $this->save();
82 40
            $proxy = app(ProxyService::class);
83 40
            if (!$proxy->has($this->getKey())) {
0 ignored issues
show
Bug introduced by
It seems like $this->getKey() can also be of type boolean and null; however, parameter $key of Bavix\Wallet\Services\ProxyService::has() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

83
            if (!$proxy->has(/** @scrutinizer ignore-type */ $this->getKey())) {
Loading history...
84 40
                $proxy->set($this->getKey(), (int)($this->attributes['balance'] ?? 0));
0 ignored issues
show
Bug introduced by
It seems like $this->getKey() can also be of type boolean and null; however, parameter $key of Bavix\Wallet\Services\ProxyService::set() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

84
                $proxy->set(/** @scrutinizer ignore-type */ $this->getKey(), (int)($this->attributes['balance'] ?? 0));
Loading history...
Bug Best Practice introduced by
The property $attributes is declared protected in Illuminate\Database\Eloquent\Model. Since you implement __get, consider adding a @property or @property-read.
Loading history...
85
            }
86
87 40
            return $proxy[$this->getKey()];
88
        }
89
90 39
        return $this->wallet->balance;
91
    }
92
93
    /**
94
     * all user actions on wallets will be in this method
95
     *
96
     * @return MorphMany
97
     */
98 30
    public function transactions(): MorphMany
99
    {
100 30
        return ($this instanceof WalletModel ? $this->holder : $this)
101 30
            ->morphMany(config('wallet.transaction.model'), 'payable');
0 ignored issues
show
Bug introduced by
It seems like morphMany() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

101
            ->/** @scrutinizer ignore-call */ morphMany(config('wallet.transaction.model'), 'payable');
Loading history...
102
    }
103
104
    /**
105
     * This method ignores errors that occur when transferring funds
106
     *
107
     * @param Wallet $wallet
108
     * @param int $amount
109
     * @param array|null $meta
110
     * @return null|Transfer
111
     */
112 3
    public function safeTransfer(Wallet $wallet, int $amount, ?array $meta = null): ?Transfer
113
    {
114
        try {
115 3
            return $this->transfer($wallet, $amount, $meta);
116 3
        } catch (Throwable $throwable) {
117 3
            return null;
118
        }
119
    }
120
121
    /**
122
     * A method that transfers funds from host to host
123
     *
124
     * @param Wallet $wallet
125
     * @param int $amount
126
     * @param array|null $meta
127
     * @return Transfer
128
     * @throws
129
     */
130 6
    public function transfer(Wallet $wallet, int $amount, ?array $meta = null): Transfer
131
    {
132 6
        app(CommonService::class)->verifyWithdraw($this, $amount);
0 ignored issues
show
Bug introduced by
$this of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $wallet of Bavix\Wallet\Services\Co...rvice::verifyWithdraw(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

132
        app(CommonService::class)->verifyWithdraw(/** @scrutinizer ignore-type */ $this, $amount);
Loading history...
133 6
        return $this->forceTransfer($wallet, $amount, $meta);
134
    }
135
136
    /**
137
     * Withdrawals from the system
138
     *
139
     * @param int $amount
140
     * @param array|null $meta
141
     * @param bool $confirmed
142
     *
143
     * @return Transaction
144
     */
145 26
    public function withdraw(int $amount, ?array $meta = null, bool $confirmed = true): Transaction
146
    {
147 26
        app(CommonService::class)->verifyWithdraw($this, $amount);
0 ignored issues
show
Bug introduced by
$this of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $wallet of Bavix\Wallet\Services\Co...rvice::verifyWithdraw(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

147
        app(CommonService::class)->verifyWithdraw(/** @scrutinizer ignore-type */ $this, $amount);
Loading history...
148 19
        return $this->forceWithdraw($amount, $meta, $confirmed);
149
    }
150
151
    /**
152
     * Checks if you can withdraw funds
153
     *
154
     * @param int $amount
155
     * @return bool
156
     */
157 28
    public function canWithdraw(int $amount): bool
158
    {
159 28
        return $this->balance >= $amount;
160
    }
161
162
    /**
163
     * Forced to withdraw funds from system
164
     *
165
     * @param int $amount
166
     * @param array|null $meta
167
     * @param bool $confirmed
168
     *
169
     * @return Transaction
170
     */
171 19
    public function forceWithdraw(int $amount, ?array $meta = null, bool $confirmed = true): Transaction
172
    {
173
        return DB::transaction(function () use ($amount, $meta, $confirmed) {
174 19
            return app(CommonService::class)
175 19
                ->forceWithdraw($this, $amount, $meta, $confirmed);
0 ignored issues
show
Bug introduced by
$this of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $wallet of Bavix\Wallet\Services\Co...ervice::forceWithdraw(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

175
                ->forceWithdraw(/** @scrutinizer ignore-type */ $this, $amount, $meta, $confirmed);
Loading history...
176
177 19
        });
178
    }
179
180
    /**
181
     * the forced transfer is needed when the user does not have the money and we drive it.
182
     * Sometimes you do. Depends on business logic.
183
     *
184
     * @param Wallet $wallet
185
     * @param int $amount
186
     * @param array|null $meta
187
     * @return Transfer
188
     */
189 6
    public function forceTransfer(Wallet $wallet, int $amount, ?array $meta = null): Transfer
190
    {
191
        return DB::transaction(function () use ($amount, $wallet, $meta) {
192 6
            return app(CommonService::class)
193 6
                ->forceTransfer($this, $wallet, $amount, $meta);
0 ignored issues
show
Bug introduced by
$this of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $from of Bavix\Wallet\Services\Co...ervice::forceTransfer(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

193
                ->forceTransfer(/** @scrutinizer ignore-type */ $this, $wallet, $amount, $meta);
Loading history...
194 6
        });
195
    }
196
197
    /**
198
     * the transfer table is used to confirm the payment
199
     * this method receives all transfers
200
     *
201
     * @return MorphMany
202
     */
203 11
    public function transfers(): MorphMany
204
    {
205 11
        return app(WalletService::class)
206 11
            ->getWallet($this)
0 ignored issues
show
Bug introduced by
$this of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $object of Bavix\Wallet\Services\WalletService::getWallet(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

206
            ->getWallet(/** @scrutinizer ignore-type */ $this)
Loading history...
207 11
            ->morphMany(config('wallet.transfer.model'), 'from');
208
    }
209
210
    /**
211
     * Get default Wallet
212
     * this method is used for Eager Loading
213
     *
214
     * @return MorphOne|WalletModel
215
     */
216 40
    public function wallet(): MorphOne
217
    {
218 40
        return ($this instanceof WalletModel ? $this->holder : $this)
219 40
            ->morphOne(config('wallet.wallet.model'), 'holder')
0 ignored issues
show
Bug introduced by
It seems like morphOne() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

219
            ->/** @scrutinizer ignore-call */ morphOne(config('wallet.wallet.model'), 'holder')
Loading history...
220 40
            ->where('slug', config('wallet.wallet.default.slug'))
221 40
            ->withDefault([
222 40
                'name' => config('wallet.wallet.default.name'),
223 40
                'slug' => config('wallet.wallet.default.slug'),
224 40
                'balance' => 0,
225
            ]);
226
    }
227
228
}
229