1+ <?php
2+
3+ namespace Saraf \QB \QueryBuilder \Clauses ;
4+
5+ use Saraf \QB \QueryBuilder \Core \DBFactory ;
6+ use Saraf \QB \QueryBuilder \Core \DBWorker ;
7+ use Saraf \QB \QueryBuilder \Exceptions \TransactionException ;
8+ use Saraf \QB \QueryBuilder \Helpers \QueryResult \QueryResult ;
9+ use Saraf \QB \QueryBuilder \Helpers \QueryResult \QueryResultCollection ;
10+ use function React \Promise \reject ;
11+ use function React \Promise \resolve ;
12+
13+ class Transaction
14+ {
15+ protected array $ queries = [];
16+ protected QueryResultCollection $ queryResultCollection ;
17+ private ?DBWorker $ connection = null ;
18+
19+ public function __construct (
20+ protected ?DBFactory $ dbFactory = null ,
21+ )
22+ {
23+ $ this ->queryResultCollection = new QueryResultCollection ();
24+ $ this ->connection = !is_null ($ this ->dbFactory ) ? $ this ->dbFactory ->reserveConnection () : null ;
25+ }
26+
27+ public function addQuery (string $ name , Select |Update |Delete |Insert $ query , ?\Closure $ callback = null ): static
28+ {
29+ $ this ->queries [] = compact ('name ' , 'query ' , 'callback ' );
30+ return $ this ;
31+ }
32+
33+ /**
34+ * @throws TransactionException
35+ */
36+ public function compile (): \React \Promise \PromiseInterface
37+ {
38+ if (count ($ this ->queries ) === 0 ) {
39+ throw new TransactionException ('There are no queries inside transaction. ' );
40+ }
41+
42+ return $ this ->connection ->query ("START TRANSACTION " )
43+ ->then (function () {
44+ return $ this ->resolveQueries ();
45+ })
46+ ->finally (function () {
47+ $ this ->dbFactory ->releaseConnection ($ this ->connection );
48+ });
49+ }
50+
51+ protected function resolveQueries (): \React \Promise \PromiseInterface
52+ {
53+ if (count ($ this ->queries ) === 0 ) {
54+ $ this ->connection ->query ('COMMIT ' );
55+ return resolve ($ this ->queryResultCollection ->last ()->toArray ());
56+ }
57+
58+ $ queryItem = array_shift ($ this ->queries );
59+
60+ $ query = $ queryItem ['query ' ];
61+ $ callback = $ queryItem ['callback ' ];
62+ $ name = $ queryItem ['name ' ];
63+
64+ return $ query ->compile ()->getQuery ()->then (function ($ result ) use ($ query , $ callback , $ name ) {
65+ $ queryString = $ result ['query ' ];
66+ return $ this ->connection ->query ($ queryString )
67+ ->then (function ($ result ) use ($ query , $ callback , $ name , $ queryString ) {
68+ if (!$ result ['result ' ]) {
69+ $ this ->connection ->query ('ROLLBACK ' );
70+ return reject (throw new TransactionException ('Transaction rolled back due to ' . $ result ['error ' ]));
71+ }
72+
73+ $ queryResult = new QueryResult (
74+ $ result ['result ' ],
75+ @$ result ['count ' ] ?? null ,
76+ @$ result ['rows ' ] ?? [],
77+ @$ result ['affectedRows ' ] ?? null ,
78+ @$ result ['insertId ' ] ?? null ,
79+ );
80+
81+ if (is_null ($ callback ) || $ callback ($ queryResult , $ this ->queryResultCollection )) {
82+ $ this ->queryResultCollection ->add ($ name , $ queryResult );
83+ return $ this ->resolveQueries ();
84+ }
85+
86+ $ this ->connection ->query ('ROLLBACK ' );
87+ return reject (throw new TransactionException ("Transaction rolled back,callback for query {$ queryString } doesn't return true " ));
88+ });
89+ });
90+ }
91+ }
0 commit comments