@@ -19,43 +19,64 @@ trait ManagesTransactions
19
19
*/
20
20
public function transaction (Closure $ callback , $ attempts = 1 )
21
21
{
22
- for ($ a = 1 ; $ a <= $ attempts ; $ a ++) {
22
+ for ($ currentAttempt = 1 ; $ currentAttempt <= $ attempts ; $ currentAttempt ++) {
23
23
$ this ->beginTransaction ();
24
24
25
25
// We'll simply execute the given callback within a try / catch block
26
26
// and if we catch any exception we can rollback the transaction
27
27
// so that none of the changes are persisted to the database.
28
28
try {
29
- $ result = $ callback ($ this );
30
-
31
- $ this -> commit ( );
29
+ return tap ( $ callback ($ this ), function ( $ result ) {
30
+ $ this -> commit ();
31
+ } );
32
32
}
33
33
34
34
// If we catch an exception, we will roll back so nothing gets messed
35
35
// up in the database. Then we'll re-throw the exception so it can
36
36
// be handled how the developer sees fit for their applications.
37
37
catch (Exception $ e ) {
38
- if ($ this ->causedByDeadlock ($ e ) && $ this ->transactions > 1 ) {
39
- --$ this ->transactions ;
40
-
41
- throw $ e ;
42
- }
43
-
44
- $ this ->rollBack ();
45
-
46
- if ($ this ->causedByDeadlock ($ e ) && $ a < $ attempts ) {
47
- continue ;
48
- }
49
-
50
- throw $ e ;
38
+ $ this ->handleTransactionException (
39
+ $ e , $ currentAttempt , $ attempts
40
+ );
51
41
} catch (Throwable $ e ) {
52
42
$ this ->rollBack ();
53
43
54
44
throw $ e ;
55
45
}
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Handle an exception encountered when running a transacted statement.
51
+ *
52
+ * @param \Exception $e
53
+ * @param int $currentAttempt
54
+ * @param int $maxAttempts
55
+ * @return void
56
+ */
57
+ protected function handleTransactionException ($ e , $ currentAttempt , $ maxAttempts )
58
+ {
59
+ // On a deadlock, MySQL rolls back the entire transaction so we can't just
60
+ // retry the query. We have to throw this exception all the way out and
61
+ // let the developer handle it in another way. We will decrement too.
62
+ if ($ this ->causedByDeadlock ($ e ) &&
63
+ $ this ->transactions > 1 ) {
64
+ --$ this ->transactions ;
65
+
66
+ throw $ e ;
67
+ }
56
68
57
- return $ result ;
69
+ // If there was an exception we will rollback this transaction and then we
70
+ // can check if we have exceeded the maximum attempt count for this and
71
+ // if we haven't we will return and try this query again in our loop.
72
+ $ this ->rollBack ();
73
+
74
+ if ($ this ->causedByDeadlock ($ e ) &&
75
+ $ currentAttempt < $ maxAttempts ) {
76
+ return ;
58
77
}
78
+
79
+ throw $ e ;
59
80
}
60
81
61
82
/**
@@ -65,27 +86,59 @@ public function transaction(Closure $callback, $attempts = 1)
65
86
* @throws Exception
66
87
*/
67
88
public function beginTransaction ()
89
+ {
90
+ $ this ->createTransaction ();
91
+
92
+ ++$ this ->transactions ;
93
+
94
+ $ this ->fireConnectionEvent ('beganTransaction ' );
95
+ }
96
+
97
+ /**
98
+ * Create a transaction within the database.
99
+ *
100
+ * @return void
101
+ */
102
+ protected function createTransaction ()
68
103
{
69
104
if ($ this ->transactions == 0 ) {
70
105
try {
71
106
$ this ->getPdo ()->beginTransaction ();
72
107
} catch (Exception $ e ) {
73
- if ($ this ->causedByLostConnection ($ e )) {
74
- $ this ->reconnect ();
75
- $ this ->pdo ->beginTransaction ();
76
- } else {
77
- throw $ e ;
78
- }
108
+ $ this ->handleBeginTransactionException ($ e );
79
109
}
80
110
} elseif ($ this ->transactions >= 1 && $ this ->queryGrammar ->supportsSavepoints ()) {
81
- $ this ->getPdo ()->exec (
82
- $ this ->queryGrammar ->compileSavepoint ('trans ' .($ this ->transactions + 1 ))
83
- );
111
+ $ this ->createSavepoint ();
84
112
}
113
+ }
85
114
86
- ++$ this ->transactions ;
115
+ /**
116
+ * Create a save point within the database.
117
+ *
118
+ * @return void
119
+ */
120
+ protected function createSavepoint ()
121
+ {
122
+ $ this ->getPdo ()->exec (
123
+ $ this ->queryGrammar ->compileSavepoint ('trans ' .($ this ->transactions + 1 ))
124
+ );
125
+ }
87
126
88
- $ this ->fireConnectionEvent ('beganTransaction ' );
127
+ /**
128
+ * Handle an exception from a transaction beginning.
129
+ *
130
+ * @param \Exception $e
131
+ * @return void
132
+ */
133
+ protected function handleBeginTransactionException ($ e )
134
+ {
135
+ if ($ this ->causedByLostConnection ($ e )) {
136
+ $ this ->reconnect ();
137
+
138
+ $ this ->pdo ->beginTransaction ();
139
+ } else {
140
+ throw $ e ;
141
+ }
89
142
}
90
143
91
144
/**
@@ -112,25 +165,42 @@ public function commit()
112
165
*/
113
166
public function rollBack ($ toLevel = null )
114
167
{
115
- if (is_null ($ toLevel )) {
116
- $ toLevel = $ this ->transactions - 1 ;
117
- }
168
+ // We allow developers to rollback to a certain transaction level. We will verify
169
+ // that this given transaction level is valid before attempting to rollback to
170
+ // that level. If it's not we will just return out and not attempt anything.
171
+ $ toLevel = is_null ($ toLevel )
172
+ ? $ this ->transactions - 1
173
+ : $ toLevel ;
118
174
119
175
if ($ toLevel < 0 || $ toLevel >= $ this ->transactions ) {
120
176
return ;
121
177
}
122
178
179
+ // Next, we will actually perform this rollback within this database and fire the
180
+ // rollback event. We will also set the current transaction level to the given
181
+ // level that was passed into this method so it will be right from here out.
182
+ $ this ->performRollBack ($ toLevel );
183
+
184
+ $ this ->transactions = $ toLevel ;
185
+
186
+ $ this ->fireConnectionEvent ('rollingBack ' );
187
+ }
188
+
189
+ /**
190
+ * Perform a rollback within the database.
191
+ *
192
+ * @param int $toLevel
193
+ * @return void
194
+ */
195
+ protected function performRollBack ($ toLevel )
196
+ {
123
197
if ($ toLevel == 0 ) {
124
198
$ this ->getPdo ()->rollBack ();
125
199
} elseif ($ this ->queryGrammar ->supportsSavepoints ()) {
126
200
$ this ->getPdo ()->exec (
127
201
$ this ->queryGrammar ->compileSavepointRollBack ('trans ' .($ toLevel + 1 ))
128
202
);
129
203
}
130
-
131
- $ this ->transactions = $ toLevel ;
132
-
133
- $ this ->fireConnectionEvent ('rollingBack ' );
134
204
}
135
205
136
206
/**
0 commit comments