Passed
Push — refactor/improve-static-analys... ( efcf20...37f12c )
by Bas
03:04
created

ManagesTransactions::beginTransaction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace LaravelFreelancerNL\Aranguent\Concerns;
6
7
use Closure;
8
use LaravelFreelancerNL\Aranguent\Exceptions\NoArangoClientException;
9
use Throwable;
10
11
trait ManagesTransactions
12
{
13
    /**
14
     * Execute a Closure within a transaction.
15
     *
16
     * @param \Closure $callback
17
     * @param  int  $attempts
18
     * @param  array<mixed> $collections
19
     *
20
     * @return mixed
21
     *
22
     * @throws \Throwable
23
     */
24 3
    public function transaction(Closure $callback, $attempts = 1, array $collections = [])
25
    {
26 3
        for ($currentAttempt = 1; $currentAttempt <= $attempts; $currentAttempt++) {
27 3
            $this->beginTransaction($collections);
28
29
            // We'll simply execute the given callback within a try / catch block and if we
30
            // catch any exception we can rollback this transaction so that none of this
31
            // gets actually persisted to a database or stored in a permanent fashion.
32
            try {
33 3
                $callbackResult = $callback($this);
34
            } catch (Throwable $e) {
35
                // If we catch an exception we'll rollback this transaction and try again if we
36
                // are not out of attempts. If we are out of attempts we will just throw the
37
                // exception back out and let the developer handle an uncaught exceptions.
38
39
                $this->handleTransactionException(
0 ignored issues
show
Bug introduced by
It seems like handleTransactionException() 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

39
                $this->/** @scrutinizer ignore-call */ 
40
                       handleTransactionException(
Loading history...
40
                    $e,
41
                    $currentAttempt,
42
                    $attempts
43
                );
44
45
                continue;
46
            }
47
48
            try {
49 3
                if ($this->transactions == 1) {
50 1
                    $this->commit();
51
                }
52
53 3
                $this->transactions = max(0, $this->transactions - 1);
0 ignored issues
show
Bug Best Practice introduced by
The property transactions does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
54
            } catch (Throwable $e) {
55
                $this->handleCommitTransactionException(
0 ignored issues
show
Bug introduced by
It seems like handleCommitTransactionException() 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

55
                $this->/** @scrutinizer ignore-call */ 
56
                       handleCommitTransactionException(
Loading history...
56
                    $e,
57
                    $currentAttempt,
58
                    $attempts
59
                );
60
61
                continue;
62
            }
63
64 3
            $this->fireConnectionEvent('committed');
0 ignored issues
show
Bug introduced by
It seems like fireConnectionEvent() 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

64
            $this->/** @scrutinizer ignore-call */ 
65
                   fireConnectionEvent('committed');
Loading history...
65
66 3
            return $callbackResult;
67
        }
68
    }
69
70
    /**
71
     * Start a new database transaction.
72
     *
73
     * @param  array<string, array<string>>  $collections
74
     *
75
     * @throws Throwable
76
     */
77 111
    public function beginTransaction(array $collections = []): void
78
    {
79 111
        $this->createTransaction($collections);
80
81 111
        $this->transactions++;
82
83 111
        $this->fireConnectionEvent('beganTransaction');
84
    }
85
86
    /**
87
     * Create a transaction within the database.
88
     *
89
     * @param  array<string, array<string>>  $collections
90
     *
91
     * @throws Throwable
92
     */
93 111
    protected function createTransaction(array $collections = []): void
94
    {
95 111
        if ($this->transactions == 0) {
96 111
            $this->reconnectIfMissingConnection();
0 ignored issues
show
Bug introduced by
It seems like reconnectIfMissingConnection() 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

96
            $this->/** @scrutinizer ignore-call */ 
97
                   reconnectIfMissingConnection();
Loading history...
97
98
            try {
99 111
                if ($this->arangoClient === null) {
100
                    throw new NoArangoClientException();
101
                }
102 111
                $this->arangoClient->begin($collections);
103
            } catch (Throwable $e) {
104
                $this->handleBeginTransactionException($e);
0 ignored issues
show
Bug introduced by
It seems like handleBeginTransactionException() 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

104
                $this->/** @scrutinizer ignore-call */ 
105
                       handleBeginTransactionException($e);
Loading history...
105
            }
106
        }
107
    }
108
109
    /**
110
     * Commit the active database transaction.
111
     *
112
     * @return void
113
     *
114
     * @throws Throwable
115
     */
116 3
    public function commit()
117
    {
118 3
        if ($this->arangoClient === null) {
119
            throw new NoArangoClientException();
120
        }
121
122 3
        if ($this->transactions == 1) {
123 3
            $this->arangoClient->commit();
124
        }
125
126 3
        $this->transactions = max(0, $this->transactions - 1);
0 ignored issues
show
Bug Best Practice introduced by
The property transactions does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
127
128 3
        $this->fireConnectionEvent('committed');
129
    }
130
131
    /**
132
     * Rollback the active database transaction.
133
     *
134
     * @param  int|null  $toLevel
135
     * @return void
136
     *
137
     * @throws \Throwable
138
     */
139 108
    public function rollBack($toLevel = null)
140
    {
141
        // We allow developers to rollback to a certain transaction level. We will verify
142
        // that this given transaction level is valid before attempting to rollback to
143
        // that level. If it's not we will just return out and not attempt anything.
144 108
        $toLevel = is_null($toLevel)
145 108
            ? $this->transactions - 1
146
            : $toLevel;
147
148 108
        if ($toLevel < 0 || $toLevel >= $this->transactions) {
149
            return;
150
        }
151
152
        // Next, we will actually perform this rollback within this database and fire the
153
        // rollback event. We will also set the current transaction level to the given
154
        // level that was passed into this method so it will be right from here out.
155
        try {
156 108
            $this->performRollBack($toLevel);
157
        } catch (Throwable $e) {
158
            $this->handleRollBackException($e);
0 ignored issues
show
Bug introduced by
It seems like handleRollBackException() 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

158
            $this->/** @scrutinizer ignore-call */ 
159
                   handleRollBackException($e);
Loading history...
159
        }
160
161 108
        $this->transactions = $toLevel;
0 ignored issues
show
Bug Best Practice introduced by
The property transactions does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
162
163 108
        $this->fireConnectionEvent('rollingBack');
164
    }
165
166
    /**
167
     * Perform a rollback within the database.
168
     *
169
     * @param  int  $toLevel
170
     * @return void
171
     *
172
     * @throws Throwable
173
     */
174 108
    protected function performRollBack($toLevel)
175
    {
176 108
        if ($this->arangoClient === null) {
177
            throw new NoArangoClientException();
178
        }
179
180 108
        if ($toLevel == 0) {
181 108
            $this->arangoClient->abort();
182
        }
183
    }
184
}
185