Passed
Pull Request — master (#20)
by Glynn
02:17
created
src/Binding.php 2 patches
Indentation   +164 added lines, -164 removed lines patch added patch discarded remove patch
@@ -7,168 +7,168 @@
 block discarded – undo
7 7
 
8 8
 class Binding
9 9
 {
10
-    public const STRING = '%s';
11
-    public const BOOL = '%d';
12
-    public const INT = '%d';
13
-    public const FLOAT = '%f';
14
-    public const JSON = '%s';
15
-    public const RAW = ':RAW';
16
-
17
-    /**
18
-     * Holds the value to bind with
19
-     *
20
-     * @var mixed
21
-     */
22
-    protected $value;
23
-
24
-    /**
25
-     * Denotes the type
26
-     *
27
-     * @var string|null
28
-     */
29
-    protected $type;
30
-
31
-    /**
32
-     * Denotes if the field is a RAW value
33
-     *
34
-     * @var bool
35
-     */
36
-    protected $isRaw = false;
37
-
38
-    /**
39
-     * @param mixed $value
40
-     * @param string|null $type
41
-     */
42
-    public function __construct($value, ?string $type = null)
43
-    {
44
-        $this->verifyType($type);
45
-        $this->value = $value;
46
-        $this->type = $type;
47
-        if (self::RAW === $type) {
48
-            $this->isRaw = true;
49
-        }
50
-    }
51
-
52
-    /**
53
-     * Creates a binding for a String
54
-     *
55
-     * @param mixed $value
56
-     * @return self
57
-     */
58
-    public static function asString($value): self
59
-    {
60
-        return new Binding($value, self::STRING);
61
-    }
62
-
63
-    /**
64
-     * Creates a binding for a Float
65
-     *
66
-     * @param mixed $value
67
-     * @return self
68
-     */
69
-    public static function asFloat($value): self
70
-    {
71
-        return new Binding($value, self::FLOAT);
72
-    }
73
-
74
-    /**
75
-     * Creates a binding for a Int
76
-     *
77
-     * @param mixed $value
78
-     * @return self
79
-     */
80
-    public static function asInt($value): self
81
-    {
82
-        return new Binding($value, self::INT);
83
-    }
84
-
85
-    /**
86
-     * Creates a binding for a Bool
87
-     *
88
-     * @param mixed $value
89
-     * @return self
90
-     */
91
-    public static function asBool($value): self
92
-    {
93
-        return new Binding($value, self::BOOL);
94
-    }
95
-
96
-    /**
97
-     * Creates a binding for a JSON
98
-     *
99
-     * @param mixed $value
100
-     * @return self
101
-     */
102
-    public static function asJSON($value): self
103
-    {
104
-        return new Binding($value, self::JSON);
105
-    }
106
-
107
-    /**
108
-     * Creates a binding for a Raw
109
-     *
110
-     * @param mixed $value
111
-     * @return self
112
-     */
113
-    public static function asRaw($value): self
114
-    {
115
-        return new Binding($value, self::RAW);
116
-    }
117
-
118
-    /**
119
-     * Verifies that the passed type is allowed
120
-     *
121
-     * @param string|null $type
122
-     * @return void
123
-     * @throws Exception if not a valid type.
124
-     */
125
-    protected function verifyType(?string $type): void
126
-    {
127
-        $validTypes = [self::STRING, self::BOOL, self::FLOAT, self::INT, self::JSON, self::RAW];
128
-        if (null !== $type && !in_array($type, $validTypes, true)) {
129
-            throw new Exception("{$type} is not a valid type to use for Bindings.", 1);
130
-        }
131
-    }
132
-
133
-    /**
134
-     * Checks if we have a type that will bind.
135
-     *
136
-     * @return bool
137
-     */
138
-    public function hasTypeDefined(): bool
139
-    {
140
-        return !\in_array($this->type, [null, self::RAW], true);
141
-    }
142
-
143
-    /**
144
-     * Returns the bindings values
145
-     *
146
-     * @return string|int|float|bool|Raw|null
147
-     */
148
-    public function getValue()
149
-    {
150
-        return ! $this->hasTypeDefined()
151
-            ? new Raw($this->value)
152
-            : $this->value;
153
-    }
154
-
155
-    /**
156
-     * Gets the types format Conversion Specifier
157
-     *
158
-     * @return string|null
159
-     */
160
-    public function getType(): ?string
161
-    {
162
-        return $this->type;
163
-    }
164
-
165
-    /**
166
-     * Get denotes if the field is a RAW value
167
-     *
168
-     * @return bool
169
-     */
170
-    public function isRaw(): bool
171
-    {
172
-        return $this->isRaw;
173
-    }
10
+	public const STRING = '%s';
11
+	public const BOOL = '%d';
12
+	public const INT = '%d';
13
+	public const FLOAT = '%f';
14
+	public const JSON = '%s';
15
+	public const RAW = ':RAW';
16
+
17
+	/**
18
+	 * Holds the value to bind with
19
+	 *
20
+	 * @var mixed
21
+	 */
22
+	protected $value;
23
+
24
+	/**
25
+	 * Denotes the type
26
+	 *
27
+	 * @var string|null
28
+	 */
29
+	protected $type;
30
+
31
+	/**
32
+	 * Denotes if the field is a RAW value
33
+	 *
34
+	 * @var bool
35
+	 */
36
+	protected $isRaw = false;
37
+
38
+	/**
39
+	 * @param mixed $value
40
+	 * @param string|null $type
41
+	 */
42
+	public function __construct($value, ?string $type = null)
43
+	{
44
+		$this->verifyType($type);
45
+		$this->value = $value;
46
+		$this->type = $type;
47
+		if (self::RAW === $type) {
48
+			$this->isRaw = true;
49
+		}
50
+	}
51
+
52
+	/**
53
+	 * Creates a binding for a String
54
+	 *
55
+	 * @param mixed $value
56
+	 * @return self
57
+	 */
58
+	public static function asString($value): self
59
+	{
60
+		return new Binding($value, self::STRING);
61
+	}
62
+
63
+	/**
64
+	 * Creates a binding for a Float
65
+	 *
66
+	 * @param mixed $value
67
+	 * @return self
68
+	 */
69
+	public static function asFloat($value): self
70
+	{
71
+		return new Binding($value, self::FLOAT);
72
+	}
73
+
74
+	/**
75
+	 * Creates a binding for a Int
76
+	 *
77
+	 * @param mixed $value
78
+	 * @return self
79
+	 */
80
+	public static function asInt($value): self
81
+	{
82
+		return new Binding($value, self::INT);
83
+	}
84
+
85
+	/**
86
+	 * Creates a binding for a Bool
87
+	 *
88
+	 * @param mixed $value
89
+	 * @return self
90
+	 */
91
+	public static function asBool($value): self
92
+	{
93
+		return new Binding($value, self::BOOL);
94
+	}
95
+
96
+	/**
97
+	 * Creates a binding for a JSON
98
+	 *
99
+	 * @param mixed $value
100
+	 * @return self
101
+	 */
102
+	public static function asJSON($value): self
103
+	{
104
+		return new Binding($value, self::JSON);
105
+	}
106
+
107
+	/**
108
+	 * Creates a binding for a Raw
109
+	 *
110
+	 * @param mixed $value
111
+	 * @return self
112
+	 */
113
+	public static function asRaw($value): self
114
+	{
115
+		return new Binding($value, self::RAW);
116
+	}
117
+
118
+	/**
119
+	 * Verifies that the passed type is allowed
120
+	 *
121
+	 * @param string|null $type
122
+	 * @return void
123
+	 * @throws Exception if not a valid type.
124
+	 */
125
+	protected function verifyType(?string $type): void
126
+	{
127
+		$validTypes = [self::STRING, self::BOOL, self::FLOAT, self::INT, self::JSON, self::RAW];
128
+		if (null !== $type && !in_array($type, $validTypes, true)) {
129
+			throw new Exception("{$type} is not a valid type to use for Bindings.", 1);
130
+		}
131
+	}
132
+
133
+	/**
134
+	 * Checks if we have a type that will bind.
135
+	 *
136
+	 * @return bool
137
+	 */
138
+	public function hasTypeDefined(): bool
139
+	{
140
+		return !\in_array($this->type, [null, self::RAW], true);
141
+	}
142
+
143
+	/**
144
+	 * Returns the bindings values
145
+	 *
146
+	 * @return string|int|float|bool|Raw|null
147
+	 */
148
+	public function getValue()
149
+	{
150
+		return ! $this->hasTypeDefined()
151
+			? new Raw($this->value)
152
+			: $this->value;
153
+	}
154
+
155
+	/**
156
+	 * Gets the types format Conversion Specifier
157
+	 *
158
+	 * @return string|null
159
+	 */
160
+	public function getType(): ?string
161
+	{
162
+		return $this->type;
163
+	}
164
+
165
+	/**
166
+	 * Get denotes if the field is a RAW value
167
+	 *
168
+	 * @return bool
169
+	 */
170
+	public function isRaw(): bool
171
+	{
172
+		return $this->isRaw;
173
+	}
174 174
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -125,7 +125,7 @@  discard block
 block discarded – undo
125 125
     protected function verifyType(?string $type): void
126 126
     {
127 127
         $validTypes = [self::STRING, self::BOOL, self::FLOAT, self::INT, self::JSON, self::RAW];
128
-        if (null !== $type && !in_array($type, $validTypes, true)) {
128
+        if (null !== $type && ! in_array($type, $validTypes, true)) {
129 129
             throw new Exception("{$type} is not a valid type to use for Bindings.", 1);
130 130
         }
131 131
     }
@@ -137,7 +137,7 @@  discard block
 block discarded – undo
137 137
      */
138 138
     public function hasTypeDefined(): bool
139 139
     {
140
-        return !\in_array($this->type, [null, self::RAW], true);
140
+        return ! \in_array($this->type, [null, self::RAW], true);
141 141
     }
142 142
 
143 143
     /**
Please login to merge, or discard this patch.
src/Connection.php 2 patches
Indentation   +149 added lines, -149 removed lines patch added patch discarded remove patch
@@ -11,153 +11,153 @@
 block discarded – undo
11 11
 
12 12
 class Connection
13 13
 {
14
-    /**
15
-     * @var Container
16
-     */
17
-    protected $container;
18
-
19
-    /**
20
-     * @var string
21
-     */
22
-    protected $adapter;
23
-
24
-    /**
25
-     * @var array<string, mixed>
26
-     */
27
-    protected $adapterConfig;
28
-
29
-    /**
30
-     * @var wpdb
31
-     */
32
-    protected $dbInstance;
33
-
34
-    /**
35
-     * @var Connection|null
36
-     */
37
-    protected static $storedConnection;
38
-
39
-    /**
40
-     * @var EventHandler
41
-     */
42
-    protected $eventHandler;
43
-
44
-    /**
45
-     * @param wpdb                 $wpdb
46
-     * @param array<string, mixed>  $adapterConfig
47
-     * @param string|null           $alias
48
-     * @param Container|null        $container
49
-     */
50
-    public function __construct(
51
-        wpdb $wpdb,
52
-        array $adapterConfig = [],
53
-        ?string $alias = null,
54
-        ?Container $container = null
55
-    ) {
56
-        $this->dbInstance = $wpdb;
57
-        $this->setAdapterConfig($adapterConfig);
58
-
59
-        $this->container    = $container ?? new Container();
60
-        $this->eventHandler = $this->container->build(EventHandler::class);
61
-
62
-        if ($alias) {
63
-            $this->createAlias($alias);
64
-        }
65
-
66
-        // Preserve the first database connection with a static property
67
-        if (!static::$storedConnection) {
68
-            static::$storedConnection = $this;
69
-        }
70
-    }
71
-
72
-    /**
73
-     * Create an easily accessible query builder alias
74
-     *
75
-     * @param string $alias
76
-     */
77
-    public function createAlias(string $alias): void
78
-    {
79
-        class_alias(AliasFacade::class, $alias);
80
-        $builder = $this->container->build(QueryBuilderHandler::class, [$this]);
81
-        AliasFacade::setQueryBuilderInstance($builder);
82
-    }
83
-
84
-    /**
85
-     * Returns an instance of Query Builder
86
-     */
87
-    public function getQueryBuilder(): QueryBuilderHandler
88
-    {
89
-        return $this->container->build(QueryBuilderHandler::class, [$this]);
90
-    }
91
-
92
-    /**
93
-     * @param wpdb $wpdb
94
-     *
95
-     * @return $this
96
-     */
97
-    public function setDbInstance(wpdb $wpdb)
98
-    {
99
-        $this->dbInstance = $wpdb;
100
-
101
-        return $this;
102
-    }
103
-
104
-    /**
105
-     * @return wpdb
106
-     */
107
-    public function getDbInstance()
108
-    {
109
-        return $this->dbInstance;
110
-    }
111
-
112
-    /**
113
-     * @param array<string, mixed> $adapterConfig
114
-     *
115
-     * @return $this
116
-     */
117
-    public function setAdapterConfig(array $adapterConfig)
118
-    {
119
-        $this->adapterConfig = $adapterConfig;
120
-
121
-        return $this;
122
-    }
123
-
124
-    /**
125
-     * @return array<string, mixed>
126
-     */
127
-    public function getAdapterConfig()
128
-    {
129
-        return $this->adapterConfig;
130
-    }
131
-
132
-    /**
133
-     * @return Container
134
-     */
135
-    public function getContainer()
136
-    {
137
-        return $this->container;
138
-    }
139
-
140
-    /**
141
-     * @return EventHandler
142
-     */
143
-    public function getEventHandler()
144
-    {
145
-        return $this->eventHandler;
146
-    }
147
-
148
-    /**
149
-     * Returns the initial instance created.
150
-     *
151
-     * @return Connection
152
-     *
153
-     * @throws Exception If connection not already established
154
-     */
155
-    public static function getStoredConnection()
156
-    {
157
-        if (null === static::$storedConnection) {
158
-            throw new Exception('No initial instance of Connection created');
159
-        }
160
-
161
-        return static::$storedConnection;
162
-    }
14
+	/**
15
+	 * @var Container
16
+	 */
17
+	protected $container;
18
+
19
+	/**
20
+	 * @var string
21
+	 */
22
+	protected $adapter;
23
+
24
+	/**
25
+	 * @var array<string, mixed>
26
+	 */
27
+	protected $adapterConfig;
28
+
29
+	/**
30
+	 * @var wpdb
31
+	 */
32
+	protected $dbInstance;
33
+
34
+	/**
35
+	 * @var Connection|null
36
+	 */
37
+	protected static $storedConnection;
38
+
39
+	/**
40
+	 * @var EventHandler
41
+	 */
42
+	protected $eventHandler;
43
+
44
+	/**
45
+	 * @param wpdb                 $wpdb
46
+	 * @param array<string, mixed>  $adapterConfig
47
+	 * @param string|null           $alias
48
+	 * @param Container|null        $container
49
+	 */
50
+	public function __construct(
51
+		wpdb $wpdb,
52
+		array $adapterConfig = [],
53
+		?string $alias = null,
54
+		?Container $container = null
55
+	) {
56
+		$this->dbInstance = $wpdb;
57
+		$this->setAdapterConfig($adapterConfig);
58
+
59
+		$this->container    = $container ?? new Container();
60
+		$this->eventHandler = $this->container->build(EventHandler::class);
61
+
62
+		if ($alias) {
63
+			$this->createAlias($alias);
64
+		}
65
+
66
+		// Preserve the first database connection with a static property
67
+		if (!static::$storedConnection) {
68
+			static::$storedConnection = $this;
69
+		}
70
+	}
71
+
72
+	/**
73
+	 * Create an easily accessible query builder alias
74
+	 *
75
+	 * @param string $alias
76
+	 */
77
+	public function createAlias(string $alias): void
78
+	{
79
+		class_alias(AliasFacade::class, $alias);
80
+		$builder = $this->container->build(QueryBuilderHandler::class, [$this]);
81
+		AliasFacade::setQueryBuilderInstance($builder);
82
+	}
83
+
84
+	/**
85
+	 * Returns an instance of Query Builder
86
+	 */
87
+	public function getQueryBuilder(): QueryBuilderHandler
88
+	{
89
+		return $this->container->build(QueryBuilderHandler::class, [$this]);
90
+	}
91
+
92
+	/**
93
+	 * @param wpdb $wpdb
94
+	 *
95
+	 * @return $this
96
+	 */
97
+	public function setDbInstance(wpdb $wpdb)
98
+	{
99
+		$this->dbInstance = $wpdb;
100
+
101
+		return $this;
102
+	}
103
+
104
+	/**
105
+	 * @return wpdb
106
+	 */
107
+	public function getDbInstance()
108
+	{
109
+		return $this->dbInstance;
110
+	}
111
+
112
+	/**
113
+	 * @param array<string, mixed> $adapterConfig
114
+	 *
115
+	 * @return $this
116
+	 */
117
+	public function setAdapterConfig(array $adapterConfig)
118
+	{
119
+		$this->adapterConfig = $adapterConfig;
120
+
121
+		return $this;
122
+	}
123
+
124
+	/**
125
+	 * @return array<string, mixed>
126
+	 */
127
+	public function getAdapterConfig()
128
+	{
129
+		return $this->adapterConfig;
130
+	}
131
+
132
+	/**
133
+	 * @return Container
134
+	 */
135
+	public function getContainer()
136
+	{
137
+		return $this->container;
138
+	}
139
+
140
+	/**
141
+	 * @return EventHandler
142
+	 */
143
+	public function getEventHandler()
144
+	{
145
+		return $this->eventHandler;
146
+	}
147
+
148
+	/**
149
+	 * Returns the initial instance created.
150
+	 *
151
+	 * @return Connection
152
+	 *
153
+	 * @throws Exception If connection not already established
154
+	 */
155
+	public static function getStoredConnection()
156
+	{
157
+		if (null === static::$storedConnection) {
158
+			throw new Exception('No initial instance of Connection created');
159
+		}
160
+
161
+		return static::$storedConnection;
162
+	}
163 163
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -64,7 +64,7 @@
 block discarded – undo
64 64
         }
65 65
 
66 66
         // Preserve the first database connection with a static property
67
-        if (!static::$storedConnection) {
67
+        if ( ! static::$storedConnection) {
68 68
             static::$storedConnection = $this;
69 69
         }
70 70
     }
Please login to merge, or discard this patch.
src/QueryBuilder/NestedCriteria.php 1 patch
Indentation   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -4,19 +4,19 @@
 block discarded – undo
4 4
 
5 5
 class NestedCriteria extends QueryBuilderHandler
6 6
 {
7
-    /**
8
-     * @param string|Raw $key
9
-     * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
10
-     * @param mixed|null $value
11
-     * @param string $joiner
12
-     *
13
-     * @return $this
14
-     */
15
-    protected function whereHandler($key, $operator = null, $value = null, $joiner = 'AND')
16
-    {
17
-        $key                            = $this->addTablePrefix($key);
18
-        $this->statements['criteria'][] = compact('key', 'operator', 'value', 'joiner');
7
+	/**
8
+	 * @param string|Raw $key
9
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
10
+	 * @param mixed|null $value
11
+	 * @param string $joiner
12
+	 *
13
+	 * @return $this
14
+	 */
15
+	protected function whereHandler($key, $operator = null, $value = null, $joiner = 'AND')
16
+	{
17
+		$key                            = $this->addTablePrefix($key);
18
+		$this->statements['criteria'][] = compact('key', 'operator', 'value', 'joiner');
19 19
 
20
-        return $this;
21
-    }
20
+		return $this;
21
+	}
22 22
 }
Please login to merge, or discard this patch.
src/QueryBuilder/WPDBAdapter.php 2 patches
Indentation   +700 added lines, -700 removed lines patch added patch discarded remove patch
@@ -17,704 +17,704 @@
 block discarded – undo
17 17
 
18 18
 class WPDBAdapter
19 19
 {
20
-    /**
21
-     * @var string
22
-     */
23
-    protected $sanitizer = '';
24
-
25
-    /**
26
-     * @var \Pixie\Connection
27
-     */
28
-    protected $connection;
29
-
30
-    /**
31
-     * @var \Viocon\Container
32
-     */
33
-    protected $container;
34
-
35
-    public function __construct(Connection $connection)
36
-    {
37
-        $this->connection = $connection;
38
-        $this->container  = $this->connection->getContainer();
39
-    }
40
-
41
-    /**
42
-     * Build select query string and bindings
43
-     *
44
-     * @param array<string|Closure, mixed|mixed[]> $statements
45
-     *
46
-     * @throws Exception
47
-     *
48
-     * @return array{sql:string,bindings:mixed[]}
49
-     */
50
-    public function select(array $statements): array
51
-    {
52
-        if (!array_key_exists('tables', $statements)) {
53
-            throw new Exception('No table specified.', 3);
54
-        } elseif (!array_key_exists('selects', $statements)) {
55
-            $statements['selects'][] = '*';
56
-        }
57
-
58
-        // From
59
-        $tables = $this->arrayStr($statements['tables'], ', ');
60
-        // Select
61
-        $selects = $this->arrayStr($statements['selects'], ', ');
62
-
63
-        // Wheres
64
-        list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
65
-        // Group bys
66
-        $groupBys = '';
67
-        if (isset($statements['groupBys']) && $groupBys = $this->arrayStr($statements['groupBys'], ', ')) {
68
-            $groupBys = 'GROUP BY ' . $groupBys;
69
-        }
70
-
71
-        // Order bys
72
-        $orderBys = '';
73
-        if (isset($statements['orderBys']) && is_array($statements['orderBys'])) {
74
-            foreach ($statements['orderBys'] as $orderBy) {
75
-                $field = $this->wrapSanitizer($orderBy['field']);
76
-                if ($field instanceof Closure) {
77
-                    continue;
78
-                }
79
-                $orderBys .= $field . ' ' . $orderBy['type'] . ', ';
80
-            }
81
-
82
-            if ($orderBys = trim($orderBys, ', ')) {
83
-                $orderBys = 'ORDER BY ' . $orderBys;
84
-            }
85
-        }
86
-
87
-        // Limit and offset
88
-        $limit  = isset($statements['limit']) ? 'LIMIT ' . (int) $statements['limit'] : '';
89
-        $offset = isset($statements['offset']) ? 'OFFSET ' . (int) $statements['offset'] : '';
90
-
91
-        // Having
92
-        list($havingCriteria, $havingBindings) = $this->buildCriteriaWithType($statements, 'havings', 'HAVING');
93
-
94
-        // Joins
95
-        $joinString = $this->buildJoin($statements);
96
-
97
-        /** @var string[] */
98
-        $sqlArray = [
99
-            'SELECT' . (isset($statements['distinct']) ? ' DISTINCT' : ''),
100
-            $selects,
101
-            'FROM',
102
-            $tables,
103
-            $joinString,
104
-            $whereCriteria,
105
-            $groupBys,
106
-            $havingCriteria,
107
-            $orderBys,
108
-            $limit,
109
-            $offset,
110
-        ];
111
-
112
-        $sql = $this->concatenateQuery($sqlArray);
113
-
114
-        $bindings = array_merge(
115
-            $whereBindings,
116
-            $havingBindings
117
-        );
118
-
119
-        return compact('sql', 'bindings');
120
-    }
121
-
122
-    /**
123
-     * Build just criteria part of the query
124
-     *
125
-     * @param array<string|Closure, mixed|mixed[]> $statements
126
-     * @param bool $bindValues
127
-     *
128
-     * @return array{sql:string[]|string, bindings:array<mixed>}
129
-     */
130
-    public function criteriaOnly(array $statements, bool $bindValues = true): array
131
-    {
132
-        $sql = $bindings = [];
133
-        if (!isset($statements['criteria'])) {
134
-            return compact('sql', 'bindings');
135
-        }
136
-
137
-        list($sql, $bindings) = $this->buildCriteria($statements['criteria'], $bindValues);
138
-
139
-        return compact('sql', 'bindings');
140
-    }
141
-
142
-    /**
143
-     * Build a generic insert/ignore/replace query
144
-     *
145
-     * @param array<string|Closure, mixed|mixed[]> $statements
146
-     * @param array<string, mixed> $data
147
-     * @param string $type
148
-     *
149
-     * @return array{sql:string, bindings:mixed[]}
150
-     *
151
-     * @throws Exception
152
-     */
153
-    private function doInsert(array $statements, array $data, string $type): array
154
-    {
155
-        if (!isset($statements['tables'])) {
156
-            throw new Exception('No table specified', 3);
157
-        }
158
-
159
-        $table = end($statements['tables']);
160
-
161
-        $bindings = $keys = $values = [];
162
-
163
-        foreach ($data as $key => $value) {
164
-            $keys[] = $key;
165
-
166
-            // Handle value as bindings
167
-            $isBindings = $value instanceof Binding;
168
-            // If this is a raw binding, extract the Raw and replace value.
169
-            if ($isBindings && $value->isRaw()) {
170
-                $value = $value->getValue();
171
-            }
172
-
173
-            if ($value instanceof Raw) {
174
-                $values[] = $this->parseRaw($value);
175
-            } elseif ($isBindings) {
176
-                $values[]   =  $value->getType();
177
-                $bindings[] = $value->getValue();
178
-            } else {
179
-                $values[]   =  $this->inferType($value);
180
-                $bindings[] = $value;
181
-            }
182
-        }
183
-
184
-        $sqlArray = [
185
-        $type . ' INTO',
186
-        $this->wrapSanitizer($table),
187
-        '(' . $this->arrayStr($keys, ',') . ')',
188
-        'VALUES',
189
-        '(' . $this->arrayStr($values, ',') . ')',
190
-        ];
191
-
192
-        if (isset($statements['onduplicate'])) {
193
-            if (count($statements['onduplicate']) < 1) {
194
-                throw new Exception('No data given.', 4);
195
-            }
196
-            list($updateStatement, $updateBindings) = $this->getUpdateStatement($statements['onduplicate']);
197
-            $sqlArray[]                             = 'ON DUPLICATE KEY UPDATE ' . $updateStatement;
198
-            $bindings                               = array_merge($bindings, $updateBindings);
199
-        }
200
-
201
-        $sql = $this->concatenateQuery($this->stringifyValues($sqlArray));
202
-
203
-        return compact('sql', 'bindings');
204
-    }
205
-
206
-    /**
207
-     * Attempts to stringify an array of values.
208
-     *
209
-     * @param array<string|int, string|Closure> $values
210
-     *
211
-     * @return string[]
212
-     */
213
-    protected function stringifyValues(array $values): array
214
-    {
215
-        return array_filter(array_map([$this, 'stringifyValue'], $values));
216
-    }
217
-
218
-    /**
219
-     * Attempts to stringify a single of values.
220
-     *
221
-     * @param string|Closure|Raw $value
222
-     *
223
-     * @return string|null
224
-     */
225
-    protected function stringifyValue($value): ?string
226
-    {
227
-        if ($value instanceof Closure) {
228
-            $value = $value();
229
-
230
-            return is_string($value) ? $value : null;
231
-        }
232
-
233
-        if ($value instanceof Raw) {
234
-            return $this->parseRaw($value);
235
-        }
236
-
237
-        return $value;
238
-    }
239
-
240
-    /**
241
-     * Build Insert query
242
-     *
243
-     * @param array<string|Closure, mixed|mixed[]> $statements
244
-     * @param array<string, mixed> $data $data
245
-     *
246
-     * @return array{sql:string, bindings:mixed[]}
247
-     *
248
-     * @throws Exception
249
-     */
250
-    public function insert($statements, array $data)
251
-    {
252
-        return $this->doInsert($statements, $data, 'INSERT');
253
-    }
254
-
255
-    /**
256
-     * Build Insert Ignore query
257
-     *
258
-     * @param array<string|Closure, mixed|mixed[]> $statements
259
-     * @param array<string, mixed> $data $data
260
-     *
261
-     * @return array{sql:string, bindings:mixed[]}
262
-     *
263
-     * @throws Exception
264
-     */
265
-    public function insertIgnore($statements, array $data)
266
-    {
267
-        return $this->doInsert($statements, $data, 'INSERT IGNORE');
268
-    }
269
-
270
-    /**
271
-     * Build Insert Ignore query
272
-     *
273
-     * @param array<string|Closure, mixed|mixed[]> $statements
274
-     * @param array<string, mixed> $data $data
275
-     *
276
-     * @return array{sql:string, bindings:mixed[]}
277
-     *
278
-     * @throws Exception
279
-     */
280
-    public function replace($statements, array $data)
281
-    {
282
-        return $this->doInsert($statements, $data, 'REPLACE');
283
-    }
284
-
285
-    /**
286
-     * Build fields assignment part of SET ... or ON DUBLICATE KEY UPDATE ... statements
287
-     *
288
-     * @param array<string, mixed> $data
289
-     *
290
-     * @return array{0:string,1:mixed[]}
291
-     */
292
-    private function getUpdateStatement(array $data): array
293
-    {
294
-        $bindings  = [];
295
-        $statement = '';
296
-
297
-        foreach ($data as $key => $value) {
298
-            $isBindings = $value instanceof Binding;
299
-            // If this is a raw binding, extract the Raw and replace value.
300
-            if ($isBindings && $value->isRaw()) {
301
-                $value = $value->getValue();
302
-            }
303
-
304
-            if ($value instanceof Raw) {
305
-                $statement .= $this->stringifyValue($this->wrapSanitizer($key)) . '=' . $value . ',';
306
-            } elseif ($isBindings) {
307
-                $statement .= $this->stringifyValue($this->wrapSanitizer($key)) . sprintf('=%s,', $value->getType());
308
-                $bindings[] = $value->getValue();
309
-            } else {
310
-                $statement .= $this->stringifyValue($this->wrapSanitizer($key)) . sprintf('=%s,', $this->inferType($value));
311
-                $bindings[] = $value;
312
-            }
313
-        }
314
-
315
-        $statement = trim($statement, ',');
316
-
317
-        return [$statement, $bindings];
318
-    }
319
-
320
-    /**
321
-     * Build update query
322
-     *
323
-     * @param array<string|Closure, mixed|mixed[]> $statements
324
-     * @param array<string, mixed> $data
325
-     *
326
-     * @return array{sql:string, bindings:mixed[]}
327
-     *
328
-     * @throws Exception
329
-     */
330
-    public function update($statements, array $data)
331
-    {
332
-        if (!isset($statements['tables'])) {
333
-            throw new Exception('No table specified', 3);
334
-        } elseif (count($data) < 1) {
335
-            throw new Exception('No data given.', 4);
336
-        }
337
-
338
-        $table = end($statements['tables']);
339
-
340
-        // Update statement
341
-        list($updateStatement, $bindings) = $this->getUpdateStatement($data);
342
-
343
-        // Wheres
344
-        list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
345
-
346
-        // Limit
347
-        $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
348
-
349
-        $sqlArray = [
350
-            'UPDATE',
351
-            $this->wrapSanitizer($table),
352
-            'SET ' . $updateStatement,
353
-            $whereCriteria,
354
-            $limit,
355
-        ];
356
-
357
-        $sql = $this->concatenateQuery($this->stringifyValues($sqlArray));
358
-
359
-        $bindings = array_merge($bindings, $whereBindings);
360
-
361
-        return compact('sql', 'bindings');
362
-    }
363
-
364
-    /**
365
-     * Build delete query
366
-     *
367
-     * @param array<string|Closure, mixed|mixed[]> $statements
368
-     *
369
-     * @return array{sql:string, bindings:mixed[]}
370
-     *
371
-     * @throws Exception
372
-     */
373
-    public function delete($statements)
374
-    {
375
-        if (!isset($statements['tables'])) {
376
-            throw new Exception('No table specified', 3);
377
-        }
378
-
379
-        $table = end($statements['tables']);
380
-        // Ensure table name is a string
381
-        $table = $this->stringifyValue($this->wrapSanitizer($table));
382
-        if (null === $table) {
383
-            throw new Exception('Table must be a valid string.', 5);
384
-        }
385
-
386
-        // Wheres
387
-        list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
388
-
389
-        // Limit
390
-        $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
391
-
392
-        $sqlArray = ['DELETE FROM', $table, $whereCriteria];
393
-        $sql      = $this->concatenateQuery($sqlArray);
394
-        $bindings = $whereBindings;
395
-
396
-        return compact('sql', 'bindings');
397
-    }
398
-
399
-    /**
400
-     * Array concatenating method, like implode.
401
-     * But it does wrap sanitizer and trims last glue
402
-     *
403
-     * @param array<string|int, string> $pieces
404
-     * @param string $glue
405
-     *
406
-     * @return string
407
-     */
408
-    protected function arrayStr(array $pieces, string $glue): string
409
-    {
410
-        $str = '';
411
-        foreach ($pieces as $key => $piece) {
412
-            if (!is_int($key)) {
413
-                $piece = $key . ' AS ' . $piece;
414
-            }
415
-
416
-            $str .= $piece . $glue;
417
-        }
418
-
419
-        return trim($str, $glue);
420
-    }
421
-
422
-    /**
423
-     * Join different part of queries with a space.
424
-     *
425
-     * @param array<string|int, string> $pieces
426
-     *
427
-     * @return string
428
-     */
429
-    protected function concatenateQuery(array $pieces): string
430
-    {
431
-        $str = '';
432
-        foreach ($pieces as $piece) {
433
-            $str = trim($str) . ' ' . trim($piece);
434
-        }
435
-
436
-        return trim($str);
437
-    }
438
-
439
-    /**
440
-     * Gets the type of a value, either from a binding or infered
441
-     *
442
-     * @param mixed $value
443
-     * @return string
444
-     */
445
-    public function getType($value): string
446
-    {
447
-        return $value instanceof Binding && $value->getType() !== null
448
-            ? $value->getType() : $this->inferType($value) ;
449
-    }
450
-
451
-    /**
452
-     * Get the value from a possible Bindings object.
453
-     *
454
-     * @param mixed $value
455
-     * @return mixed
456
-     */
457
-    public function getValue($value)
458
-    {
459
-        return $value instanceof Binding ? $value->getValue() : $value;
460
-    }
461
-
462
-    /**
463
-     * Attempts to parse a raw query, if bindings are defined then they will be bound first.
464
-     *
465
-     * @param Raw $raw
466
-     * @requires string
467
-     */
468
-    public function parseRaw(Raw $raw): string
469
-    {
470
-        $bindings = $raw->getBindings();
471
-        return 0 === count($bindings)
472
-            ? (string) $raw
473
-            : $this->interpolateQuery($raw->getValue(), $bindings);
474
-    }
475
-
476
-    /**
477
-     * Interpolates a query
478
-     *
479
-     * @param string $query
480
-     * @param array<mixed> $bindings
481
-     * @return string
482
-     */
483
-    public function interpolateQuery(string $query, array $bindings = []): string
484
-    {
485
-        if (0 === count($bindings)) {
486
-            return $query;
487
-        }
488
-
489
-
490
-        $bindings = array_map([$this, 'getValue'], $bindings);
491
-        $query = $this->connection->getDbInstance()->prepare($query, $bindings) ;
492
-        return is_string($query) ? $query : '';
493
-    }
494
-
495
-    /**
496
-     * Build generic criteria string and bindings from statements, like "a = b and c = ?"
497
-     *
498
-     * @param array<string|Closure, mixed|mixed[]> $statements
499
-     * @param bool $bindValues
500
-     *
501
-     * @return array{0:string,1:string[]}
502
-     */
503
-    protected function buildCriteria(array $statements, bool $bindValues = true): array
504
-    {
505
-        $criteria = '';
506
-        $bindings = [];
507
-        foreach ($statements as $statement) {
508
-            $key   = $statement['key'];
509
-            $value = $statement['value'];
510
-
511
-            // If the value is a Raw Binding, cast to raw
512
-            if ($value instanceof Binding && Binding::RAW === $value->getType()) {
513
-                /** @var Raw */
514
-                $value = $value->getValue();
515
-            }
516
-
517
-            if (is_null($value) && $key instanceof Closure) {
518
-                // We have a closure, a nested criteria
519
-
520
-                // Build a new NestedCriteria class, keep it by reference so any changes made
521
-                // in the closure should reflect here
522
-                $nestedCriteria = $this->container->build(NestedCriteria::class, [$this->connection]);
523
-
524
-                $nestedCriteria = &$nestedCriteria;
525
-                // Call the closure with our new nestedCriteria object
526
-                $key($nestedCriteria);
527
-                // Get the criteria only query from the nestedCriteria object
528
-                $queryObject = $nestedCriteria->getQuery('criteriaOnly', true);
529
-                // Merge the bindings we get from nestedCriteria object
530
-                $bindings = array_merge($bindings, $queryObject->getBindings());
531
-                // Append the sql we get from the nestedCriteria object
532
-                $criteria .= $statement['joiner'] . ' (' . $queryObject->getSql() . ') ';
533
-            } elseif (is_array($value)) {
534
-                // where_in or between like query
535
-                $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'];
536
-
537
-                switch ($statement['operator']) {
538
-                    case 'BETWEEN':
539
-                        $bindings = array_merge($bindings, $statement['value']);
540
-                        $criteria .= sprintf(
541
-                            ' %s AND %s ',
542
-                            $this->getType($value[0]),
543
-                            $this->getType($value[1])
544
-                        );
545
-
546
-                        // Maybe cast the values bindings.
547
-                        $value[0] = $this->getValue($value[0]);
548
-                        $value[1] = $this->getValue($value[1]);
549
-                        break;
550
-                    default:
551
-                        $valuePlaceholder = '';
552
-                        foreach ($statement['value'] as $subValue) {
553
-                            // Get its value.
554
-                            if ($this->getValue($subValue) instanceof Raw) {
555
-                                /** @var Raw $subValue */
556
-                                $subValue = $this->getValue($subValue);
557
-                                $valuePlaceholder .= sprintf('%s, ', $this->parseRaw($subValue));
558
-                                continue;
559
-                            }
560
-
561
-
562
-                            // Add in format placeholders.
563
-                            $valuePlaceholder .= sprintf('%s, ', $this->getType($subValue)); // glynn
564
-                            $bindings[] = $this->getValue($subValue);
565
-                        }
566
-
567
-                        $valuePlaceholder = trim($valuePlaceholder, ', ');
568
-                        $criteria .= ' (' . $valuePlaceholder . ') ';
569
-                        break;
570
-                }
571
-            } elseif ($value instanceof Raw) {
572
-                $value = $this->parseRaw($value);
573
-                $criteria .= "{$statement['joiner']} {$key} {$statement['operator']} $value ";
574
-            } else {
575
-                // Usual where like criteria
576
-                if (!$bindValues) {
577
-                    // Specially for joins
578
-                    // We are not binding values, lets sanitize then
579
-                    $value = $this->stringifyValue($this->wrapSanitizer($value)) ?? '';
580
-                    $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $value . ' ';
581
-                } elseif ($statement['key'] instanceof Raw) {
582
-                    $criteria .= $statement['joiner'] . ' ' . $key . ' ';
583
-                    $bindings = array_merge($bindings, $statement['key']->getBindings());
584
-                } else {
585
-                    // For wheres
586
-                    $bindings[] = $this->getValue($value);
587
-
588
-                    $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' '
589
-                    . $this->getType($value) . ' ';
590
-                }
591
-            }
592
-        }
593
-
594
-        // Clear all white spaces, and, or from beginning and white spaces from ending
595
-        $criteria = preg_replace('/^(\s?AND ?|\s?OR ?)|\s$/i', '', $criteria);
596
-
597
-        return [$criteria ?? '', $bindings];
598
-    }
599
-
600
-    /**
601
-     * Asserts the types place holder based on its value
602
-     *
603
-     * @param mixed $value
604
-     *
605
-     * @return string
606
-     */
607
-    public function inferType($value): string
608
-    {
609
-        switch (true) {
610
-            case is_string($value):
611
-                return '%s';
612
-            case \is_int($value):
613
-            case is_bool($value):
614
-                return '%d';
615
-            case is_float($value):
616
-                return '%f';
617
-            default:
618
-                return '';
619
-        }
620
-    }
621
-
622
-    /**
623
-     * Wrap values with adapter's sanitizer like, '`'
624
-     *
625
-     * @param string|Raw|Closure $value
626
-     *
627
-     * @return string|Closure
628
-     */
629
-    public function wrapSanitizer($value)
630
-    {
631
-        // Its a raw query, just cast as string, object has __toString()
632
-        if ($value instanceof Raw) {
633
-            return $this->parseRaw($value);
634
-        } elseif ($value instanceof Closure) {
635
-            return $value;
636
-        }
637
-
638
-        // Separate our table and fields which are joined with a ".",
639
-        // like my_table.id
640
-        $valueArr = explode('.', $value, 2);
641
-
642
-        foreach ($valueArr as $key => $subValue) {
643
-            // Don't wrap if we have *, which is not a usual field
644
-            $valueArr[$key] = '*' == trim($subValue) ? $subValue : $this->sanitizer . $subValue . $this->sanitizer;
645
-        }
646
-
647
-        // Join these back with "." and return
648
-        return implode('.', $valueArr);
649
-    }
650
-
651
-    /**
652
-     * Build criteria string and binding with various types added, like WHERE and Having
653
-     *
654
-     * @param array<string|Closure, mixed|mixed[]> $statements
655
-     * @param string $key
656
-     * @param string $type
657
-     * @param bool $bindValues
658
-     *
659
-     * @return array{0:string, 1:string[]}
660
-     */
661
-    protected function buildCriteriaWithType(array $statements, string $key, string $type, bool $bindValues = true)
662
-    {
663
-        $criteria = '';
664
-        $bindings = [];
665
-
666
-        if (isset($statements[$key])) {
667
-            // Get the generic/adapter agnostic criteria string from parent
668
-            list($criteria, $bindings) = $this->buildCriteria($statements[$key], $bindValues);
669
-
670
-            if ($criteria) {
671
-                $criteria = $type . ' ' . $criteria;
672
-            }
673
-        }
674
-
675
-        return [$criteria, $bindings];
676
-    }
677
-
678
-    /**
679
-     * Build join string
680
-     *
681
-     * @param array<string|Closure, mixed|mixed[]> $statements
682
-     *
683
-     * @return string
684
-     */
685
-    protected function buildJoin(array $statements): string
686
-    {
687
-        $sql = '';
688
-
689
-        if (!array_key_exists('joins', $statements) || !is_array($statements['joins'])) {
690
-            return $sql;
691
-        }
692
-
693
-        foreach ($statements['joins'] as $joinArr) {
694
-            if (is_array($joinArr['table'])) {
695
-                $mainTable  = $this->stringifyValue($this->wrapSanitizer($joinArr['table'][0]));
696
-                $aliasTable = $this->stringifyValue($this->wrapSanitizer($joinArr['table'][1]));
697
-                $table      = $mainTable . ' AS ' . $aliasTable;
698
-            } else {
699
-                $table = $joinArr['table'] instanceof Raw
700
-                    ? $this->parseRaw($joinArr['table'])
701
-                    : $this->wrapSanitizer($joinArr['table']);
702
-            }
703
-            $joinBuilder = $joinArr['joinBuilder'];
704
-
705
-            /** @var string[] */
706
-            $sqlArr = [
707
-                $sql,
708
-                strtoupper($joinArr['type']),
709
-                'JOIN',
710
-                $table,
711
-                'ON',
712
-                $joinBuilder->getQuery('criteriaOnly', false)->getSql(),
713
-            ];
714
-
715
-            $sql = $this->concatenateQuery($sqlArr);
716
-        }
717
-
718
-        return $sql;
719
-    }
20
+	/**
21
+	 * @var string
22
+	 */
23
+	protected $sanitizer = '';
24
+
25
+	/**
26
+	 * @var \Pixie\Connection
27
+	 */
28
+	protected $connection;
29
+
30
+	/**
31
+	 * @var \Viocon\Container
32
+	 */
33
+	protected $container;
34
+
35
+	public function __construct(Connection $connection)
36
+	{
37
+		$this->connection = $connection;
38
+		$this->container  = $this->connection->getContainer();
39
+	}
40
+
41
+	/**
42
+	 * Build select query string and bindings
43
+	 *
44
+	 * @param array<string|Closure, mixed|mixed[]> $statements
45
+	 *
46
+	 * @throws Exception
47
+	 *
48
+	 * @return array{sql:string,bindings:mixed[]}
49
+	 */
50
+	public function select(array $statements): array
51
+	{
52
+		if (!array_key_exists('tables', $statements)) {
53
+			throw new Exception('No table specified.', 3);
54
+		} elseif (!array_key_exists('selects', $statements)) {
55
+			$statements['selects'][] = '*';
56
+		}
57
+
58
+		// From
59
+		$tables = $this->arrayStr($statements['tables'], ', ');
60
+		// Select
61
+		$selects = $this->arrayStr($statements['selects'], ', ');
62
+
63
+		// Wheres
64
+		list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
65
+		// Group bys
66
+		$groupBys = '';
67
+		if (isset($statements['groupBys']) && $groupBys = $this->arrayStr($statements['groupBys'], ', ')) {
68
+			$groupBys = 'GROUP BY ' . $groupBys;
69
+		}
70
+
71
+		// Order bys
72
+		$orderBys = '';
73
+		if (isset($statements['orderBys']) && is_array($statements['orderBys'])) {
74
+			foreach ($statements['orderBys'] as $orderBy) {
75
+				$field = $this->wrapSanitizer($orderBy['field']);
76
+				if ($field instanceof Closure) {
77
+					continue;
78
+				}
79
+				$orderBys .= $field . ' ' . $orderBy['type'] . ', ';
80
+			}
81
+
82
+			if ($orderBys = trim($orderBys, ', ')) {
83
+				$orderBys = 'ORDER BY ' . $orderBys;
84
+			}
85
+		}
86
+
87
+		// Limit and offset
88
+		$limit  = isset($statements['limit']) ? 'LIMIT ' . (int) $statements['limit'] : '';
89
+		$offset = isset($statements['offset']) ? 'OFFSET ' . (int) $statements['offset'] : '';
90
+
91
+		// Having
92
+		list($havingCriteria, $havingBindings) = $this->buildCriteriaWithType($statements, 'havings', 'HAVING');
93
+
94
+		// Joins
95
+		$joinString = $this->buildJoin($statements);
96
+
97
+		/** @var string[] */
98
+		$sqlArray = [
99
+			'SELECT' . (isset($statements['distinct']) ? ' DISTINCT' : ''),
100
+			$selects,
101
+			'FROM',
102
+			$tables,
103
+			$joinString,
104
+			$whereCriteria,
105
+			$groupBys,
106
+			$havingCriteria,
107
+			$orderBys,
108
+			$limit,
109
+			$offset,
110
+		];
111
+
112
+		$sql = $this->concatenateQuery($sqlArray);
113
+
114
+		$bindings = array_merge(
115
+			$whereBindings,
116
+			$havingBindings
117
+		);
118
+
119
+		return compact('sql', 'bindings');
120
+	}
121
+
122
+	/**
123
+	 * Build just criteria part of the query
124
+	 *
125
+	 * @param array<string|Closure, mixed|mixed[]> $statements
126
+	 * @param bool $bindValues
127
+	 *
128
+	 * @return array{sql:string[]|string, bindings:array<mixed>}
129
+	 */
130
+	public function criteriaOnly(array $statements, bool $bindValues = true): array
131
+	{
132
+		$sql = $bindings = [];
133
+		if (!isset($statements['criteria'])) {
134
+			return compact('sql', 'bindings');
135
+		}
136
+
137
+		list($sql, $bindings) = $this->buildCriteria($statements['criteria'], $bindValues);
138
+
139
+		return compact('sql', 'bindings');
140
+	}
141
+
142
+	/**
143
+	 * Build a generic insert/ignore/replace query
144
+	 *
145
+	 * @param array<string|Closure, mixed|mixed[]> $statements
146
+	 * @param array<string, mixed> $data
147
+	 * @param string $type
148
+	 *
149
+	 * @return array{sql:string, bindings:mixed[]}
150
+	 *
151
+	 * @throws Exception
152
+	 */
153
+	private function doInsert(array $statements, array $data, string $type): array
154
+	{
155
+		if (!isset($statements['tables'])) {
156
+			throw new Exception('No table specified', 3);
157
+		}
158
+
159
+		$table = end($statements['tables']);
160
+
161
+		$bindings = $keys = $values = [];
162
+
163
+		foreach ($data as $key => $value) {
164
+			$keys[] = $key;
165
+
166
+			// Handle value as bindings
167
+			$isBindings = $value instanceof Binding;
168
+			// If this is a raw binding, extract the Raw and replace value.
169
+			if ($isBindings && $value->isRaw()) {
170
+				$value = $value->getValue();
171
+			}
172
+
173
+			if ($value instanceof Raw) {
174
+				$values[] = $this->parseRaw($value);
175
+			} elseif ($isBindings) {
176
+				$values[]   =  $value->getType();
177
+				$bindings[] = $value->getValue();
178
+			} else {
179
+				$values[]   =  $this->inferType($value);
180
+				$bindings[] = $value;
181
+			}
182
+		}
183
+
184
+		$sqlArray = [
185
+		$type . ' INTO',
186
+		$this->wrapSanitizer($table),
187
+		'(' . $this->arrayStr($keys, ',') . ')',
188
+		'VALUES',
189
+		'(' . $this->arrayStr($values, ',') . ')',
190
+		];
191
+
192
+		if (isset($statements['onduplicate'])) {
193
+			if (count($statements['onduplicate']) < 1) {
194
+				throw new Exception('No data given.', 4);
195
+			}
196
+			list($updateStatement, $updateBindings) = $this->getUpdateStatement($statements['onduplicate']);
197
+			$sqlArray[]                             = 'ON DUPLICATE KEY UPDATE ' . $updateStatement;
198
+			$bindings                               = array_merge($bindings, $updateBindings);
199
+		}
200
+
201
+		$sql = $this->concatenateQuery($this->stringifyValues($sqlArray));
202
+
203
+		return compact('sql', 'bindings');
204
+	}
205
+
206
+	/**
207
+	 * Attempts to stringify an array of values.
208
+	 *
209
+	 * @param array<string|int, string|Closure> $values
210
+	 *
211
+	 * @return string[]
212
+	 */
213
+	protected function stringifyValues(array $values): array
214
+	{
215
+		return array_filter(array_map([$this, 'stringifyValue'], $values));
216
+	}
217
+
218
+	/**
219
+	 * Attempts to stringify a single of values.
220
+	 *
221
+	 * @param string|Closure|Raw $value
222
+	 *
223
+	 * @return string|null
224
+	 */
225
+	protected function stringifyValue($value): ?string
226
+	{
227
+		if ($value instanceof Closure) {
228
+			$value = $value();
229
+
230
+			return is_string($value) ? $value : null;
231
+		}
232
+
233
+		if ($value instanceof Raw) {
234
+			return $this->parseRaw($value);
235
+		}
236
+
237
+		return $value;
238
+	}
239
+
240
+	/**
241
+	 * Build Insert query
242
+	 *
243
+	 * @param array<string|Closure, mixed|mixed[]> $statements
244
+	 * @param array<string, mixed> $data $data
245
+	 *
246
+	 * @return array{sql:string, bindings:mixed[]}
247
+	 *
248
+	 * @throws Exception
249
+	 */
250
+	public function insert($statements, array $data)
251
+	{
252
+		return $this->doInsert($statements, $data, 'INSERT');
253
+	}
254
+
255
+	/**
256
+	 * Build Insert Ignore query
257
+	 *
258
+	 * @param array<string|Closure, mixed|mixed[]> $statements
259
+	 * @param array<string, mixed> $data $data
260
+	 *
261
+	 * @return array{sql:string, bindings:mixed[]}
262
+	 *
263
+	 * @throws Exception
264
+	 */
265
+	public function insertIgnore($statements, array $data)
266
+	{
267
+		return $this->doInsert($statements, $data, 'INSERT IGNORE');
268
+	}
269
+
270
+	/**
271
+	 * Build Insert Ignore query
272
+	 *
273
+	 * @param array<string|Closure, mixed|mixed[]> $statements
274
+	 * @param array<string, mixed> $data $data
275
+	 *
276
+	 * @return array{sql:string, bindings:mixed[]}
277
+	 *
278
+	 * @throws Exception
279
+	 */
280
+	public function replace($statements, array $data)
281
+	{
282
+		return $this->doInsert($statements, $data, 'REPLACE');
283
+	}
284
+
285
+	/**
286
+	 * Build fields assignment part of SET ... or ON DUBLICATE KEY UPDATE ... statements
287
+	 *
288
+	 * @param array<string, mixed> $data
289
+	 *
290
+	 * @return array{0:string,1:mixed[]}
291
+	 */
292
+	private function getUpdateStatement(array $data): array
293
+	{
294
+		$bindings  = [];
295
+		$statement = '';
296
+
297
+		foreach ($data as $key => $value) {
298
+			$isBindings = $value instanceof Binding;
299
+			// If this is a raw binding, extract the Raw and replace value.
300
+			if ($isBindings && $value->isRaw()) {
301
+				$value = $value->getValue();
302
+			}
303
+
304
+			if ($value instanceof Raw) {
305
+				$statement .= $this->stringifyValue($this->wrapSanitizer($key)) . '=' . $value . ',';
306
+			} elseif ($isBindings) {
307
+				$statement .= $this->stringifyValue($this->wrapSanitizer($key)) . sprintf('=%s,', $value->getType());
308
+				$bindings[] = $value->getValue();
309
+			} else {
310
+				$statement .= $this->stringifyValue($this->wrapSanitizer($key)) . sprintf('=%s,', $this->inferType($value));
311
+				$bindings[] = $value;
312
+			}
313
+		}
314
+
315
+		$statement = trim($statement, ',');
316
+
317
+		return [$statement, $bindings];
318
+	}
319
+
320
+	/**
321
+	 * Build update query
322
+	 *
323
+	 * @param array<string|Closure, mixed|mixed[]> $statements
324
+	 * @param array<string, mixed> $data
325
+	 *
326
+	 * @return array{sql:string, bindings:mixed[]}
327
+	 *
328
+	 * @throws Exception
329
+	 */
330
+	public function update($statements, array $data)
331
+	{
332
+		if (!isset($statements['tables'])) {
333
+			throw new Exception('No table specified', 3);
334
+		} elseif (count($data) < 1) {
335
+			throw new Exception('No data given.', 4);
336
+		}
337
+
338
+		$table = end($statements['tables']);
339
+
340
+		// Update statement
341
+		list($updateStatement, $bindings) = $this->getUpdateStatement($data);
342
+
343
+		// Wheres
344
+		list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
345
+
346
+		// Limit
347
+		$limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
348
+
349
+		$sqlArray = [
350
+			'UPDATE',
351
+			$this->wrapSanitizer($table),
352
+			'SET ' . $updateStatement,
353
+			$whereCriteria,
354
+			$limit,
355
+		];
356
+
357
+		$sql = $this->concatenateQuery($this->stringifyValues($sqlArray));
358
+
359
+		$bindings = array_merge($bindings, $whereBindings);
360
+
361
+		return compact('sql', 'bindings');
362
+	}
363
+
364
+	/**
365
+	 * Build delete query
366
+	 *
367
+	 * @param array<string|Closure, mixed|mixed[]> $statements
368
+	 *
369
+	 * @return array{sql:string, bindings:mixed[]}
370
+	 *
371
+	 * @throws Exception
372
+	 */
373
+	public function delete($statements)
374
+	{
375
+		if (!isset($statements['tables'])) {
376
+			throw new Exception('No table specified', 3);
377
+		}
378
+
379
+		$table = end($statements['tables']);
380
+		// Ensure table name is a string
381
+		$table = $this->stringifyValue($this->wrapSanitizer($table));
382
+		if (null === $table) {
383
+			throw new Exception('Table must be a valid string.', 5);
384
+		}
385
+
386
+		// Wheres
387
+		list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
388
+
389
+		// Limit
390
+		$limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
391
+
392
+		$sqlArray = ['DELETE FROM', $table, $whereCriteria];
393
+		$sql      = $this->concatenateQuery($sqlArray);
394
+		$bindings = $whereBindings;
395
+
396
+		return compact('sql', 'bindings');
397
+	}
398
+
399
+	/**
400
+	 * Array concatenating method, like implode.
401
+	 * But it does wrap sanitizer and trims last glue
402
+	 *
403
+	 * @param array<string|int, string> $pieces
404
+	 * @param string $glue
405
+	 *
406
+	 * @return string
407
+	 */
408
+	protected function arrayStr(array $pieces, string $glue): string
409
+	{
410
+		$str = '';
411
+		foreach ($pieces as $key => $piece) {
412
+			if (!is_int($key)) {
413
+				$piece = $key . ' AS ' . $piece;
414
+			}
415
+
416
+			$str .= $piece . $glue;
417
+		}
418
+
419
+		return trim($str, $glue);
420
+	}
421
+
422
+	/**
423
+	 * Join different part of queries with a space.
424
+	 *
425
+	 * @param array<string|int, string> $pieces
426
+	 *
427
+	 * @return string
428
+	 */
429
+	protected function concatenateQuery(array $pieces): string
430
+	{
431
+		$str = '';
432
+		foreach ($pieces as $piece) {
433
+			$str = trim($str) . ' ' . trim($piece);
434
+		}
435
+
436
+		return trim($str);
437
+	}
438
+
439
+	/**
440
+	 * Gets the type of a value, either from a binding or infered
441
+	 *
442
+	 * @param mixed $value
443
+	 * @return string
444
+	 */
445
+	public function getType($value): string
446
+	{
447
+		return $value instanceof Binding && $value->getType() !== null
448
+			? $value->getType() : $this->inferType($value) ;
449
+	}
450
+
451
+	/**
452
+	 * Get the value from a possible Bindings object.
453
+	 *
454
+	 * @param mixed $value
455
+	 * @return mixed
456
+	 */
457
+	public function getValue($value)
458
+	{
459
+		return $value instanceof Binding ? $value->getValue() : $value;
460
+	}
461
+
462
+	/**
463
+	 * Attempts to parse a raw query, if bindings are defined then they will be bound first.
464
+	 *
465
+	 * @param Raw $raw
466
+	 * @requires string
467
+	 */
468
+	public function parseRaw(Raw $raw): string
469
+	{
470
+		$bindings = $raw->getBindings();
471
+		return 0 === count($bindings)
472
+			? (string) $raw
473
+			: $this->interpolateQuery($raw->getValue(), $bindings);
474
+	}
475
+
476
+	/**
477
+	 * Interpolates a query
478
+	 *
479
+	 * @param string $query
480
+	 * @param array<mixed> $bindings
481
+	 * @return string
482
+	 */
483
+	public function interpolateQuery(string $query, array $bindings = []): string
484
+	{
485
+		if (0 === count($bindings)) {
486
+			return $query;
487
+		}
488
+
489
+
490
+		$bindings = array_map([$this, 'getValue'], $bindings);
491
+		$query = $this->connection->getDbInstance()->prepare($query, $bindings) ;
492
+		return is_string($query) ? $query : '';
493
+	}
494
+
495
+	/**
496
+	 * Build generic criteria string and bindings from statements, like "a = b and c = ?"
497
+	 *
498
+	 * @param array<string|Closure, mixed|mixed[]> $statements
499
+	 * @param bool $bindValues
500
+	 *
501
+	 * @return array{0:string,1:string[]}
502
+	 */
503
+	protected function buildCriteria(array $statements, bool $bindValues = true): array
504
+	{
505
+		$criteria = '';
506
+		$bindings = [];
507
+		foreach ($statements as $statement) {
508
+			$key   = $statement['key'];
509
+			$value = $statement['value'];
510
+
511
+			// If the value is a Raw Binding, cast to raw
512
+			if ($value instanceof Binding && Binding::RAW === $value->getType()) {
513
+				/** @var Raw */
514
+				$value = $value->getValue();
515
+			}
516
+
517
+			if (is_null($value) && $key instanceof Closure) {
518
+				// We have a closure, a nested criteria
519
+
520
+				// Build a new NestedCriteria class, keep it by reference so any changes made
521
+				// in the closure should reflect here
522
+				$nestedCriteria = $this->container->build(NestedCriteria::class, [$this->connection]);
523
+
524
+				$nestedCriteria = &$nestedCriteria;
525
+				// Call the closure with our new nestedCriteria object
526
+				$key($nestedCriteria);
527
+				// Get the criteria only query from the nestedCriteria object
528
+				$queryObject = $nestedCriteria->getQuery('criteriaOnly', true);
529
+				// Merge the bindings we get from nestedCriteria object
530
+				$bindings = array_merge($bindings, $queryObject->getBindings());
531
+				// Append the sql we get from the nestedCriteria object
532
+				$criteria .= $statement['joiner'] . ' (' . $queryObject->getSql() . ') ';
533
+			} elseif (is_array($value)) {
534
+				// where_in or between like query
535
+				$criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'];
536
+
537
+				switch ($statement['operator']) {
538
+					case 'BETWEEN':
539
+						$bindings = array_merge($bindings, $statement['value']);
540
+						$criteria .= sprintf(
541
+							' %s AND %s ',
542
+							$this->getType($value[0]),
543
+							$this->getType($value[1])
544
+						);
545
+
546
+						// Maybe cast the values bindings.
547
+						$value[0] = $this->getValue($value[0]);
548
+						$value[1] = $this->getValue($value[1]);
549
+						break;
550
+					default:
551
+						$valuePlaceholder = '';
552
+						foreach ($statement['value'] as $subValue) {
553
+							// Get its value.
554
+							if ($this->getValue($subValue) instanceof Raw) {
555
+								/** @var Raw $subValue */
556
+								$subValue = $this->getValue($subValue);
557
+								$valuePlaceholder .= sprintf('%s, ', $this->parseRaw($subValue));
558
+								continue;
559
+							}
560
+
561
+
562
+							// Add in format placeholders.
563
+							$valuePlaceholder .= sprintf('%s, ', $this->getType($subValue)); // glynn
564
+							$bindings[] = $this->getValue($subValue);
565
+						}
566
+
567
+						$valuePlaceholder = trim($valuePlaceholder, ', ');
568
+						$criteria .= ' (' . $valuePlaceholder . ') ';
569
+						break;
570
+				}
571
+			} elseif ($value instanceof Raw) {
572
+				$value = $this->parseRaw($value);
573
+				$criteria .= "{$statement['joiner']} {$key} {$statement['operator']} $value ";
574
+			} else {
575
+				// Usual where like criteria
576
+				if (!$bindValues) {
577
+					// Specially for joins
578
+					// We are not binding values, lets sanitize then
579
+					$value = $this->stringifyValue($this->wrapSanitizer($value)) ?? '';
580
+					$criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $value . ' ';
581
+				} elseif ($statement['key'] instanceof Raw) {
582
+					$criteria .= $statement['joiner'] . ' ' . $key . ' ';
583
+					$bindings = array_merge($bindings, $statement['key']->getBindings());
584
+				} else {
585
+					// For wheres
586
+					$bindings[] = $this->getValue($value);
587
+
588
+					$criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' '
589
+					. $this->getType($value) . ' ';
590
+				}
591
+			}
592
+		}
593
+
594
+		// Clear all white spaces, and, or from beginning and white spaces from ending
595
+		$criteria = preg_replace('/^(\s?AND ?|\s?OR ?)|\s$/i', '', $criteria);
596
+
597
+		return [$criteria ?? '', $bindings];
598
+	}
599
+
600
+	/**
601
+	 * Asserts the types place holder based on its value
602
+	 *
603
+	 * @param mixed $value
604
+	 *
605
+	 * @return string
606
+	 */
607
+	public function inferType($value): string
608
+	{
609
+		switch (true) {
610
+			case is_string($value):
611
+				return '%s';
612
+			case \is_int($value):
613
+			case is_bool($value):
614
+				return '%d';
615
+			case is_float($value):
616
+				return '%f';
617
+			default:
618
+				return '';
619
+		}
620
+	}
621
+
622
+	/**
623
+	 * Wrap values with adapter's sanitizer like, '`'
624
+	 *
625
+	 * @param string|Raw|Closure $value
626
+	 *
627
+	 * @return string|Closure
628
+	 */
629
+	public function wrapSanitizer($value)
630
+	{
631
+		// Its a raw query, just cast as string, object has __toString()
632
+		if ($value instanceof Raw) {
633
+			return $this->parseRaw($value);
634
+		} elseif ($value instanceof Closure) {
635
+			return $value;
636
+		}
637
+
638
+		// Separate our table and fields which are joined with a ".",
639
+		// like my_table.id
640
+		$valueArr = explode('.', $value, 2);
641
+
642
+		foreach ($valueArr as $key => $subValue) {
643
+			// Don't wrap if we have *, which is not a usual field
644
+			$valueArr[$key] = '*' == trim($subValue) ? $subValue : $this->sanitizer . $subValue . $this->sanitizer;
645
+		}
646
+
647
+		// Join these back with "." and return
648
+		return implode('.', $valueArr);
649
+	}
650
+
651
+	/**
652
+	 * Build criteria string and binding with various types added, like WHERE and Having
653
+	 *
654
+	 * @param array<string|Closure, mixed|mixed[]> $statements
655
+	 * @param string $key
656
+	 * @param string $type
657
+	 * @param bool $bindValues
658
+	 *
659
+	 * @return array{0:string, 1:string[]}
660
+	 */
661
+	protected function buildCriteriaWithType(array $statements, string $key, string $type, bool $bindValues = true)
662
+	{
663
+		$criteria = '';
664
+		$bindings = [];
665
+
666
+		if (isset($statements[$key])) {
667
+			// Get the generic/adapter agnostic criteria string from parent
668
+			list($criteria, $bindings) = $this->buildCriteria($statements[$key], $bindValues);
669
+
670
+			if ($criteria) {
671
+				$criteria = $type . ' ' . $criteria;
672
+			}
673
+		}
674
+
675
+		return [$criteria, $bindings];
676
+	}
677
+
678
+	/**
679
+	 * Build join string
680
+	 *
681
+	 * @param array<string|Closure, mixed|mixed[]> $statements
682
+	 *
683
+	 * @return string
684
+	 */
685
+	protected function buildJoin(array $statements): string
686
+	{
687
+		$sql = '';
688
+
689
+		if (!array_key_exists('joins', $statements) || !is_array($statements['joins'])) {
690
+			return $sql;
691
+		}
692
+
693
+		foreach ($statements['joins'] as $joinArr) {
694
+			if (is_array($joinArr['table'])) {
695
+				$mainTable  = $this->stringifyValue($this->wrapSanitizer($joinArr['table'][0]));
696
+				$aliasTable = $this->stringifyValue($this->wrapSanitizer($joinArr['table'][1]));
697
+				$table      = $mainTable . ' AS ' . $aliasTable;
698
+			} else {
699
+				$table = $joinArr['table'] instanceof Raw
700
+					? $this->parseRaw($joinArr['table'])
701
+					: $this->wrapSanitizer($joinArr['table']);
702
+			}
703
+			$joinBuilder = $joinArr['joinBuilder'];
704
+
705
+			/** @var string[] */
706
+			$sqlArr = [
707
+				$sql,
708
+				strtoupper($joinArr['type']),
709
+				'JOIN',
710
+				$table,
711
+				'ON',
712
+				$joinBuilder->getQuery('criteriaOnly', false)->getSql(),
713
+			];
714
+
715
+			$sql = $this->concatenateQuery($sqlArr);
716
+		}
717
+
718
+		return $sql;
719
+	}
720 720
 }
Please login to merge, or discard this patch.
Spacing   +42 added lines, -42 removed lines patch added patch discarded remove patch
@@ -49,9 +49,9 @@  discard block
 block discarded – undo
49 49
      */
50 50
     public function select(array $statements): array
51 51
     {
52
-        if (!array_key_exists('tables', $statements)) {
52
+        if ( ! array_key_exists('tables', $statements)) {
53 53
             throw new Exception('No table specified.', 3);
54
-        } elseif (!array_key_exists('selects', $statements)) {
54
+        } elseif ( ! array_key_exists('selects', $statements)) {
55 55
             $statements['selects'][] = '*';
56 56
         }
57 57
 
@@ -65,7 +65,7 @@  discard block
 block discarded – undo
65 65
         // Group bys
66 66
         $groupBys = '';
67 67
         if (isset($statements['groupBys']) && $groupBys = $this->arrayStr($statements['groupBys'], ', ')) {
68
-            $groupBys = 'GROUP BY ' . $groupBys;
68
+            $groupBys = 'GROUP BY '.$groupBys;
69 69
         }
70 70
 
71 71
         // Order bys
@@ -76,17 +76,17 @@  discard block
 block discarded – undo
76 76
                 if ($field instanceof Closure) {
77 77
                     continue;
78 78
                 }
79
-                $orderBys .= $field . ' ' . $orderBy['type'] . ', ';
79
+                $orderBys .= $field.' '.$orderBy['type'].', ';
80 80
             }
81 81
 
82 82
             if ($orderBys = trim($orderBys, ', ')) {
83
-                $orderBys = 'ORDER BY ' . $orderBys;
83
+                $orderBys = 'ORDER BY '.$orderBys;
84 84
             }
85 85
         }
86 86
 
87 87
         // Limit and offset
88
-        $limit  = isset($statements['limit']) ? 'LIMIT ' . (int) $statements['limit'] : '';
89
-        $offset = isset($statements['offset']) ? 'OFFSET ' . (int) $statements['offset'] : '';
88
+        $limit  = isset($statements['limit']) ? 'LIMIT '.(int) $statements['limit'] : '';
89
+        $offset = isset($statements['offset']) ? 'OFFSET '.(int) $statements['offset'] : '';
90 90
 
91 91
         // Having
92 92
         list($havingCriteria, $havingBindings) = $this->buildCriteriaWithType($statements, 'havings', 'HAVING');
@@ -96,7 +96,7 @@  discard block
 block discarded – undo
96 96
 
97 97
         /** @var string[] */
98 98
         $sqlArray = [
99
-            'SELECT' . (isset($statements['distinct']) ? ' DISTINCT' : ''),
99
+            'SELECT'.(isset($statements['distinct']) ? ' DISTINCT' : ''),
100 100
             $selects,
101 101
             'FROM',
102 102
             $tables,
@@ -130,7 +130,7 @@  discard block
 block discarded – undo
130 130
     public function criteriaOnly(array $statements, bool $bindValues = true): array
131 131
     {
132 132
         $sql = $bindings = [];
133
-        if (!isset($statements['criteria'])) {
133
+        if ( ! isset($statements['criteria'])) {
134 134
             return compact('sql', 'bindings');
135 135
         }
136 136
 
@@ -152,7 +152,7 @@  discard block
 block discarded – undo
152 152
      */
153 153
     private function doInsert(array $statements, array $data, string $type): array
154 154
     {
155
-        if (!isset($statements['tables'])) {
155
+        if ( ! isset($statements['tables'])) {
156 156
             throw new Exception('No table specified', 3);
157 157
         }
158 158
 
@@ -173,20 +173,20 @@  discard block
 block discarded – undo
173 173
             if ($value instanceof Raw) {
174 174
                 $values[] = $this->parseRaw($value);
175 175
             } elseif ($isBindings) {
176
-                $values[]   =  $value->getType();
176
+                $values[]   = $value->getType();
177 177
                 $bindings[] = $value->getValue();
178 178
             } else {
179
-                $values[]   =  $this->inferType($value);
179
+                $values[]   = $this->inferType($value);
180 180
                 $bindings[] = $value;
181 181
             }
182 182
         }
183 183
 
184 184
         $sqlArray = [
185
-        $type . ' INTO',
185
+        $type.' INTO',
186 186
         $this->wrapSanitizer($table),
187
-        '(' . $this->arrayStr($keys, ',') . ')',
187
+        '('.$this->arrayStr($keys, ',').')',
188 188
         'VALUES',
189
-        '(' . $this->arrayStr($values, ',') . ')',
189
+        '('.$this->arrayStr($values, ',').')',
190 190
         ];
191 191
 
192 192
         if (isset($statements['onduplicate'])) {
@@ -194,7 +194,7 @@  discard block
 block discarded – undo
194 194
                 throw new Exception('No data given.', 4);
195 195
             }
196 196
             list($updateStatement, $updateBindings) = $this->getUpdateStatement($statements['onduplicate']);
197
-            $sqlArray[]                             = 'ON DUPLICATE KEY UPDATE ' . $updateStatement;
197
+            $sqlArray[]                             = 'ON DUPLICATE KEY UPDATE '.$updateStatement;
198 198
             $bindings                               = array_merge($bindings, $updateBindings);
199 199
         }
200 200
 
@@ -302,12 +302,12 @@  discard block
 block discarded – undo
302 302
             }
303 303
 
304 304
             if ($value instanceof Raw) {
305
-                $statement .= $this->stringifyValue($this->wrapSanitizer($key)) . '=' . $value . ',';
305
+                $statement .= $this->stringifyValue($this->wrapSanitizer($key)).'='.$value.',';
306 306
             } elseif ($isBindings) {
307
-                $statement .= $this->stringifyValue($this->wrapSanitizer($key)) . sprintf('=%s,', $value->getType());
307
+                $statement .= $this->stringifyValue($this->wrapSanitizer($key)).sprintf('=%s,', $value->getType());
308 308
                 $bindings[] = $value->getValue();
309 309
             } else {
310
-                $statement .= $this->stringifyValue($this->wrapSanitizer($key)) . sprintf('=%s,', $this->inferType($value));
310
+                $statement .= $this->stringifyValue($this->wrapSanitizer($key)).sprintf('=%s,', $this->inferType($value));
311 311
                 $bindings[] = $value;
312 312
             }
313 313
         }
@@ -329,7 +329,7 @@  discard block
 block discarded – undo
329 329
      */
330 330
     public function update($statements, array $data)
331 331
     {
332
-        if (!isset($statements['tables'])) {
332
+        if ( ! isset($statements['tables'])) {
333 333
             throw new Exception('No table specified', 3);
334 334
         } elseif (count($data) < 1) {
335 335
             throw new Exception('No data given.', 4);
@@ -344,12 +344,12 @@  discard block
 block discarded – undo
344 344
         list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
345 345
 
346 346
         // Limit
347
-        $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
347
+        $limit = isset($statements['limit']) ? 'LIMIT '.$statements['limit'] : '';
348 348
 
349 349
         $sqlArray = [
350 350
             'UPDATE',
351 351
             $this->wrapSanitizer($table),
352
-            'SET ' . $updateStatement,
352
+            'SET '.$updateStatement,
353 353
             $whereCriteria,
354 354
             $limit,
355 355
         ];
@@ -372,7 +372,7 @@  discard block
 block discarded – undo
372 372
      */
373 373
     public function delete($statements)
374 374
     {
375
-        if (!isset($statements['tables'])) {
375
+        if ( ! isset($statements['tables'])) {
376 376
             throw new Exception('No table specified', 3);
377 377
         }
378 378
 
@@ -387,7 +387,7 @@  discard block
 block discarded – undo
387 387
         list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
388 388
 
389 389
         // Limit
390
-        $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
390
+        $limit = isset($statements['limit']) ? 'LIMIT '.$statements['limit'] : '';
391 391
 
392 392
         $sqlArray = ['DELETE FROM', $table, $whereCriteria];
393 393
         $sql      = $this->concatenateQuery($sqlArray);
@@ -409,11 +409,11 @@  discard block
 block discarded – undo
409 409
     {
410 410
         $str = '';
411 411
         foreach ($pieces as $key => $piece) {
412
-            if (!is_int($key)) {
413
-                $piece = $key . ' AS ' . $piece;
412
+            if ( ! is_int($key)) {
413
+                $piece = $key.' AS '.$piece;
414 414
             }
415 415
 
416
-            $str .= $piece . $glue;
416
+            $str .= $piece.$glue;
417 417
         }
418 418
 
419 419
         return trim($str, $glue);
@@ -430,7 +430,7 @@  discard block
 block discarded – undo
430 430
     {
431 431
         $str = '';
432 432
         foreach ($pieces as $piece) {
433
-            $str = trim($str) . ' ' . trim($piece);
433
+            $str = trim($str).' '.trim($piece);
434 434
         }
435 435
 
436 436
         return trim($str);
@@ -445,7 +445,7 @@  discard block
 block discarded – undo
445 445
     public function getType($value): string
446 446
     {
447 447
         return $value instanceof Binding && $value->getType() !== null
448
-            ? $value->getType() : $this->inferType($value) ;
448
+            ? $value->getType() : $this->inferType($value);
449 449
     }
450 450
 
451 451
     /**
@@ -488,7 +488,7 @@  discard block
 block discarded – undo
488 488
 
489 489
 
490 490
         $bindings = array_map([$this, 'getValue'], $bindings);
491
-        $query = $this->connection->getDbInstance()->prepare($query, $bindings) ;
491
+        $query = $this->connection->getDbInstance()->prepare($query, $bindings);
492 492
         return is_string($query) ? $query : '';
493 493
     }
494 494
 
@@ -529,10 +529,10 @@  discard block
 block discarded – undo
529 529
                 // Merge the bindings we get from nestedCriteria object
530 530
                 $bindings = array_merge($bindings, $queryObject->getBindings());
531 531
                 // Append the sql we get from the nestedCriteria object
532
-                $criteria .= $statement['joiner'] . ' (' . $queryObject->getSql() . ') ';
532
+                $criteria .= $statement['joiner'].' ('.$queryObject->getSql().') ';
533 533
             } elseif (is_array($value)) {
534 534
                 // where_in or between like query
535
-                $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'];
535
+                $criteria .= $statement['joiner'].' '.$key.' '.$statement['operator'];
536 536
 
537 537
                 switch ($statement['operator']) {
538 538
                     case 'BETWEEN':
@@ -565,7 +565,7 @@  discard block
 block discarded – undo
565 565
                         }
566 566
 
567 567
                         $valuePlaceholder = trim($valuePlaceholder, ', ');
568
-                        $criteria .= ' (' . $valuePlaceholder . ') ';
568
+                        $criteria .= ' ('.$valuePlaceholder.') ';
569 569
                         break;
570 570
                 }
571 571
             } elseif ($value instanceof Raw) {
@@ -573,20 +573,20 @@  discard block
 block discarded – undo
573 573
                 $criteria .= "{$statement['joiner']} {$key} {$statement['operator']} $value ";
574 574
             } else {
575 575
                 // Usual where like criteria
576
-                if (!$bindValues) {
576
+                if ( ! $bindValues) {
577 577
                     // Specially for joins
578 578
                     // We are not binding values, lets sanitize then
579 579
                     $value = $this->stringifyValue($this->wrapSanitizer($value)) ?? '';
580
-                    $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $value . ' ';
580
+                    $criteria .= $statement['joiner'].' '.$key.' '.$statement['operator'].' '.$value.' ';
581 581
                 } elseif ($statement['key'] instanceof Raw) {
582
-                    $criteria .= $statement['joiner'] . ' ' . $key . ' ';
582
+                    $criteria .= $statement['joiner'].' '.$key.' ';
583 583
                     $bindings = array_merge($bindings, $statement['key']->getBindings());
584 584
                 } else {
585 585
                     // For wheres
586 586
                     $bindings[] = $this->getValue($value);
587 587
 
588
-                    $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' '
589
-                    . $this->getType($value) . ' ';
588
+                    $criteria .= $statement['joiner'].' '.$key.' '.$statement['operator'].' '
589
+                    . $this->getType($value).' ';
590 590
                 }
591 591
             }
592 592
         }
@@ -641,7 +641,7 @@  discard block
 block discarded – undo
641 641
 
642 642
         foreach ($valueArr as $key => $subValue) {
643 643
             // Don't wrap if we have *, which is not a usual field
644
-            $valueArr[$key] = '*' == trim($subValue) ? $subValue : $this->sanitizer . $subValue . $this->sanitizer;
644
+            $valueArr[$key] = '*' == trim($subValue) ? $subValue : $this->sanitizer.$subValue.$this->sanitizer;
645 645
         }
646 646
 
647 647
         // Join these back with "." and return
@@ -668,7 +668,7 @@  discard block
 block discarded – undo
668 668
             list($criteria, $bindings) = $this->buildCriteria($statements[$key], $bindValues);
669 669
 
670 670
             if ($criteria) {
671
-                $criteria = $type . ' ' . $criteria;
671
+                $criteria = $type.' '.$criteria;
672 672
             }
673 673
         }
674 674
 
@@ -686,7 +686,7 @@  discard block
 block discarded – undo
686 686
     {
687 687
         $sql = '';
688 688
 
689
-        if (!array_key_exists('joins', $statements) || !is_array($statements['joins'])) {
689
+        if ( ! array_key_exists('joins', $statements) || ! is_array($statements['joins'])) {
690 690
             return $sql;
691 691
         }
692 692
 
@@ -694,7 +694,7 @@  discard block
 block discarded – undo
694 694
             if (is_array($joinArr['table'])) {
695 695
                 $mainTable  = $this->stringifyValue($this->wrapSanitizer($joinArr['table'][0]));
696 696
                 $aliasTable = $this->stringifyValue($this->wrapSanitizer($joinArr['table'][1]));
697
-                $table      = $mainTable . ' AS ' . $aliasTable;
697
+                $table      = $mainTable.' AS '.$aliasTable;
698 698
             } else {
699 699
                 $table = $joinArr['table'] instanceof Raw
700 700
                     ? $this->parseRaw($joinArr['table'])
Please login to merge, or discard this patch.
src/QueryBuilder/JoinBuilder.php 1 patch
Indentation   +36 added lines, -36 removed lines patch added patch discarded remove patch
@@ -4,43 +4,43 @@
 block discarded – undo
4 4
 
5 5
 class JoinBuilder extends QueryBuilderHandler
6 6
 {
7
-    /**
8
-     * @param string|Raw $key
9
-     * @param string|null $operator
10
-     * @param mixed $value
11
-     *
12
-     * @return static
13
-     */
14
-    public function on($key, ?string $operator, $value): self
15
-    {
16
-        return $this->joinHandler($key, $operator, $value, 'AND');
17
-    }
7
+	/**
8
+	 * @param string|Raw $key
9
+	 * @param string|null $operator
10
+	 * @param mixed $value
11
+	 *
12
+	 * @return static
13
+	 */
14
+	public function on($key, ?string $operator, $value): self
15
+	{
16
+		return $this->joinHandler($key, $operator, $value, 'AND');
17
+	}
18 18
 
19
-    /**
20
-     * @param string|Raw $key
21
-     * @param string|null $operator
22
-     * @param mixed $value
23
-     *
24
-     * @return static
25
-     */
26
-    public function orOn($key, ?string $operator, $value): self
27
-    {
28
-        return $this->joinHandler($key, $operator, $value, 'OR');
29
-    }
19
+	/**
20
+	 * @param string|Raw $key
21
+	 * @param string|null $operator
22
+	 * @param mixed $value
23
+	 *
24
+	 * @return static
25
+	 */
26
+	public function orOn($key, ?string $operator, $value): self
27
+	{
28
+		return $this->joinHandler($key, $operator, $value, 'OR');
29
+	}
30 30
 
31
-    /**
32
-     * @param string|Raw $key
33
-     * @param string|null $operator
34
-     * @param mixed $value
35
-     *
36
-     * @return static
37
-     */
38
-    protected function joinHandler($key, ?string $operator = null, $value = null, string $joiner = 'AND'): self
39
-    {
40
-        $key                            = $this->addTablePrefix($key);
41
-        $value                          = $this->addTablePrefix($value);
42
-        $this->statements['criteria'][] = compact('key', 'operator', 'value', 'joiner');
31
+	/**
32
+	 * @param string|Raw $key
33
+	 * @param string|null $operator
34
+	 * @param mixed $value
35
+	 *
36
+	 * @return static
37
+	 */
38
+	protected function joinHandler($key, ?string $operator = null, $value = null, string $joiner = 'AND'): self
39
+	{
40
+		$key                            = $this->addTablePrefix($key);
41
+		$value                          = $this->addTablePrefix($value);
42
+		$this->statements['criteria'][] = compact('key', 'operator', 'value', 'joiner');
43 43
 
44
-        return $this;
45
-    }
44
+		return $this;
45
+	}
46 46
 }
Please login to merge, or discard this patch.
src/QueryBuilder/QueryObject.php 2 patches
Indentation   +60 added lines, -61 removed lines patch added patch discarded remove patch
@@ -6,73 +6,72 @@
 block discarded – undo
6 6
 
7 7
 class QueryObject
8 8
 {
9
-    /**
10
-     * @var string
11
-     */
12
-    protected $sql;
9
+	/**
10
+	 * @var string
11
+	 */
12
+	protected $sql;
13 13
 
14
-    /**
15
-     * @var mixed[]
16
-     */
17
-    protected $bindings = [];
14
+	/**
15
+	 * @var mixed[]
16
+	 */
17
+	protected $bindings = [];
18 18
 
19
-    /**
20
-     * @var wpdb
21
-     */
22
-    protected $dbInstance;
19
+	/**
20
+	 * @var wpdb
21
+	 */
22
+	protected $dbInstance;
23 23
 
24
-    /**
25
-     * @param string $sql
26
-     * @param mixed[] $bindings
27
-     * @param wpdb $dbInstance
28
-     */
29
-    public function __construct(string $sql, array $bindings, wpdb $dbInstance)
30
-    {
31
-        $this->sql        = (string)$sql;
32
-        $this->bindings   = $bindings;
33
-        $this->dbInstance = $dbInstance;
34
-    }
24
+	/**
25
+	 * @param string $sql
26
+	 * @param mixed[] $bindings
27
+	 * @param wpdb $dbInstance
28
+	 */
29
+	public function __construct(string $sql, array $bindings, wpdb $dbInstance)
30
+	{
31
+		$this->sql        = (string)$sql;
32
+		$this->bindings   = $bindings;
33
+		$this->dbInstance = $dbInstance;
34
+	}
35 35
 
36
-    /**
37
-     * @return string
38
-     */
39
-    public function getSql()
40
-    {
41
-        return $this->sql;
42
-    }
36
+	/**
37
+	 * @return string
38
+	 */
39
+	public function getSql()
40
+	{
41
+		return $this->sql;
42
+	}
43 43
 
44
-    /**
45
-     * @return mixed[]
46
-     */
47
-    public function getBindings()
48
-    {
49
-        return $this->bindings;
50
-    }
44
+	/**
45
+	 * @return mixed[]
46
+	 */
47
+	public function getBindings()
48
+	{
49
+		return $this->bindings;
50
+	}
51 51
 
52
-    /**
53
-     * Get the raw/bound sql
54
-     *
55
-     * @return string
56
-     */
57
-    public function getRawSql()
58
-    {
59
-        return $this->interpolateQuery($this->sql, $this->bindings);
60
-    }
52
+	/**
53
+	 * Get the raw/bound sql
54
+	 *
55
+	 * @return string
56
+	 */
57
+	public function getRawSql()
58
+	{
59
+		return $this->interpolateQuery($this->sql, $this->bindings);
60
+	}
61 61
 
62
-    /**
63
-     * Uses WPDB::prepare() to interpolate the query passed.
62
+	/**
63
+	 * Uses WPDB::prepare() to interpolate the query passed.
64
+	 *
65
+	 * @param string $query  The sql query with parameter placeholders
66
+	 * @param mixed[]  $params The array of substitution parameters
67
+	 *
68
+	 * @return string The interpolated query
69
+	 */
70
+	protected function interpolateQuery($query, $params): string
71
+	{
72
+		// Only call this when we have valid params (avoids wpdb::prepare() incorrectly called error)
73
+		$value = empty($params) ? $query : $this->dbInstance->prepare($query, $params);
64 74
 
65
-     *
66
-     * @param string $query  The sql query with parameter placeholders
67
-     * @param mixed[]  $params The array of substitution parameters
68
-     *
69
-     * @return string The interpolated query
70
-     */
71
-    protected function interpolateQuery($query, $params): string
72
-    {
73
-        // Only call this when we have valid params (avoids wpdb::prepare() incorrectly called error)
74
-        $value = empty($params) ? $query : $this->dbInstance->prepare($query, $params);
75
-
76
-        return is_string($value) ? $value : '';
77
-    }
75
+		return is_string($value) ? $value : '';
76
+	}
78 77
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -28,7 +28,7 @@
 block discarded – undo
28 28
      */
29 29
     public function __construct(string $sql, array $bindings, wpdb $dbInstance)
30 30
     {
31
-        $this->sql        = (string)$sql;
31
+        $this->sql        = (string) $sql;
32 32
         $this->bindings   = $bindings;
33 33
         $this->dbInstance = $dbInstance;
34 34
     }
Please login to merge, or discard this patch.
src/QueryBuilder/Raw.php 2 patches
Indentation   +42 added lines, -42 removed lines patch added patch discarded remove patch
@@ -4,51 +4,51 @@
 block discarded – undo
4 4
 
5 5
 class Raw
6 6
 {
7
-    /**
8
-     * @var string
9
-     */
10
-    protected $value;
7
+	/**
8
+	 * @var string
9
+	 */
10
+	protected $value;
11 11
 
12
-    /**
13
-     * @var mixed[]
14
-     */
15
-    protected $bindings;
12
+	/**
13
+	 * @var mixed[]
14
+	 */
15
+	protected $bindings;
16 16
 
17
-    /**
18
-     * @param string $value
19
-     * @param mixed|mixed[] $bindings
20
-     */
21
-    public function __construct($value, $bindings = [])
22
-    {
23
-        $this->value    = (string)$value;
24
-        $this->bindings = (array)$bindings;
25
-    }
17
+	/**
18
+	 * @param string $value
19
+	 * @param mixed|mixed[] $bindings
20
+	 */
21
+	public function __construct($value, $bindings = [])
22
+	{
23
+		$this->value    = (string)$value;
24
+		$this->bindings = (array)$bindings;
25
+	}
26 26
 
27
-    /**
28
-     * Returns the current bindings
29
-     *
30
-     * @return mixed[]
31
-     */
32
-    public function getBindings(): array
33
-    {
34
-        return $this->bindings;
35
-    }
27
+	/**
28
+	 * Returns the current bindings
29
+	 *
30
+	 * @return mixed[]
31
+	 */
32
+	public function getBindings(): array
33
+	{
34
+		return $this->bindings;
35
+	}
36 36
 
37
-    /**
38
-     * Returns the current value held.
39
-     *
40
-     * @return string
41
-     */
42
-    public function getValue(): string
43
-    {
44
-        return (string) $this->value;
45
-    }
37
+	/**
38
+	 * Returns the current value held.
39
+	 *
40
+	 * @return string
41
+	 */
42
+	public function getValue(): string
43
+	{
44
+		return (string) $this->value;
45
+	}
46 46
 
47
-    /**
48
-     * @return string
49
-     */
50
-    public function __toString()
51
-    {
52
-        return (string)$this->value;
53
-    }
47
+	/**
48
+	 * @return string
49
+	 */
50
+	public function __toString()
51
+	{
52
+		return (string)$this->value;
53
+	}
54 54
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -20,8 +20,8 @@  discard block
 block discarded – undo
20 20
      */
21 21
     public function __construct($value, $bindings = [])
22 22
     {
23
-        $this->value    = (string)$value;
24
-        $this->bindings = (array)$bindings;
23
+        $this->value    = (string) $value;
24
+        $this->bindings = (array) $bindings;
25 25
     }
26 26
 
27 27
     /**
@@ -49,6 +49,6 @@  discard block
 block discarded – undo
49 49
      */
50 50
     public function __toString()
51 51
     {
52
-        return (string)$this->value;
52
+        return (string) $this->value;
53 53
     }
54 54
 }
Please login to merge, or discard this patch.
src/QueryBuilder/QueryBuilderHandler.php 2 patches
Indentation   +1427 added lines, -1427 removed lines patch added patch discarded remove patch
@@ -20,1431 +20,1431 @@
 block discarded – undo
20 20
 
21 21
 class QueryBuilderHandler
22 22
 {
23
-    /**
24
-     * @var \Viocon\Container
25
-     */
26
-    protected $container;
27
-
28
-    /**
29
-     * @var Connection
30
-     */
31
-    protected $connection;
32
-
33
-    /**
34
-     * @var array<string, mixed[]|mixed>
35
-     */
36
-    protected $statements = [];
37
-
38
-    /**
39
-     * @var wpdb
40
-     */
41
-    protected $dbInstance;
42
-
43
-    /**
44
-     * @var string|string[]|null
45
-     */
46
-    protected $sqlStatement = null;
47
-
48
-    /**
49
-     * @var string|null
50
-     */
51
-    protected $tablePrefix = null;
52
-
53
-    /**
54
-     * @var WPDBAdapter
55
-     */
56
-    protected $adapterInstance;
57
-
58
-    /**
59
-     * The mode to return results as.
60
-     * Accepts WPDB constants or class names.
61
-     *
62
-     * @var string
63
-     */
64
-    protected $fetchMode;
65
-
66
-    /**
67
-     * Custom args used to construct models for hydrator
68
-     *
69
-     * @var array<int, mixed>|null
70
-     */
71
-    protected $hydratorConstructorArgs;
72
-
73
-    /**
74
-     * @param \Pixie\Connection|null $connection
75
-     * @param string $fetchMode
76
-     * @param mixed[] $hydratorConstructorArgs
77
-     *
78
-     * @throws Exception if no connection passed and not previously established
79
-     */
80
-    final public function __construct(
81
-        Connection $connection = null,
82
-        string $fetchMode = \OBJECT,
83
-        ?array $hydratorConstructorArgs = null
84
-    ) {
85
-        if (is_null($connection)) {
86
-            // throws if connection not already established.
87
-            $connection = Connection::getStoredConnection();
88
-        }
89
-
90
-        // Set all dependencies from connection.
91
-        $this->connection = $connection;
92
-        $this->container  = $this->connection->getContainer();
93
-        $this->dbInstance = $this->connection->getDbInstance();
94
-        $this->setAdapterConfig($this->connection->getAdapterConfig());
95
-
96
-        // Set up optional hydration details.
97
-        $this->setFetchMode($fetchMode);
98
-        $this->hydratorConstructorArgs = $hydratorConstructorArgs;
99
-
100
-        // Query builder adapter instance
101
-        $this->adapterInstance = $this->container->build(
102
-            WPDBAdapter::class,
103
-            [$this->connection]
104
-        );
105
-    }
106
-
107
-    /**
108
-     * Sets the config for WPDB
109
-     *
110
-     * @param array<string, mixed> $adapterConfig
111
-     *
112
-     * @return void
113
-     */
114
-    protected function setAdapterConfig(array $adapterConfig): void
115
-    {
116
-        if (isset($adapterConfig['prefix'])) {
117
-            $this->tablePrefix = $adapterConfig['prefix'];
118
-        }
119
-    }
120
-
121
-    /**
122
-     * Set the fetch mode
123
-     *
124
-     * @param string $mode
125
-     * @param array<int, mixed>|null $constructorArgs
126
-     *
127
-     * @return static
128
-     */
129
-    public function setFetchMode(string $mode, ?array $constructorArgs = null): self
130
-    {
131
-        $this->fetchMode               = $mode;
132
-        $this->hydratorConstructorArgs = $constructorArgs;
133
-
134
-        return $this;
135
-    }
136
-
137
-    /**
138
-     * @param Connection|null $connection
139
-     *
140
-     * @return static
141
-     *
142
-     * @throws Exception
143
-     */
144
-    public function newQuery(Connection $connection = null): self
145
-    {
146
-        if (is_null($connection)) {
147
-            $connection = $this->connection;
148
-        }
149
-
150
-        $newQuery = $this->constructCurrentBuilderClass($connection);
151
-        $newQuery->setFetchMode($this->getFetchMode(), $this->hydratorConstructorArgs);
152
-
153
-        return $newQuery;
154
-    }
155
-
156
-    /**
157
-     * Returns a new instance of the current, with the passed connection.
158
-     *
159
-     * @param \Pixie\Connection $connection
160
-     *
161
-     * @return static
162
-     */
163
-    protected function constructCurrentBuilderClass(Connection $connection): self
164
-    {
165
-        return new static($connection);
166
-    }
167
-
168
-    /**
169
-     * Interpolates a query
170
-     *
171
-     * @param string $query
172
-     * @param array<mixed> $bindings
173
-     * @return string
174
-     */
175
-    public function interpolateQuery(string $query, array $bindings = []): string
176
-    {
177
-        return $this->adapterInstance->interpolateQuery($query, $bindings);
178
-    }
179
-
180
-    /**
181
-     * @param string           $sql
182
-     * @param array<int,mixed> $bindings
183
-     *
184
-     * @return static
185
-     */
186
-    public function query($sql, $bindings = []): self
187
-    {
188
-        list($this->sqlStatement) = $this->statement($sql, $bindings);
189
-
190
-        return $this;
191
-    }
192
-
193
-    /**
194
-     * @param string           $sql
195
-     * @param array<int,mixed> $bindings
196
-     *
197
-     * @return array{0:string, 1:float}
198
-     */
199
-    public function statement(string $sql, $bindings = []): array
200
-    {
201
-        $start        = microtime(true);
202
-        $sqlStatement = empty($bindings) ? $sql : $this->interpolateQuery($sql, $bindings);
203
-
204
-        if (!is_string($sqlStatement)) {
205
-            throw new Exception('Could not interpolate query', 1);
206
-        }
207
-
208
-        return [$sqlStatement, microtime(true) - $start];
209
-    }
210
-
211
-    /**
212
-     * Get all rows
213
-     *
214
-     * @return array<mixed,mixed>|null
215
-     *
216
-     * @throws Exception
217
-     */
218
-    public function get()
219
-    {
220
-        $eventResult = $this->fireEvents('before-select');
221
-        if (!is_null($eventResult)) {
222
-            return $eventResult;
223
-        }
224
-        $executionTime = 0;
225
-        if (is_null($this->sqlStatement)) {
226
-            $queryObject = $this->getQuery('select');
227
-            $statement   = $this->statement(
228
-                $queryObject->getSql(),
229
-                $queryObject->getBindings()
230
-            );
231
-
232
-            $this->sqlStatement = $statement[0];
233
-            $executionTime      = $statement[1];
234
-        }
235
-
236
-        $start  = microtime(true);
237
-        $result = $this->dbInstance()->get_results(
238
-            is_array($this->sqlStatement) ? (end($this->sqlStatement) ?: '') : $this->sqlStatement,
239
-            // If we are using the hydrator, return as OBJECT and let the hydrator map the correct model.
240
-            $this->useHydrator() ? OBJECT : $this->getFetchMode()
241
-        );
242
-        $executionTime += microtime(true) - $start;
243
-        $this->sqlStatement = null;
244
-
245
-        // Ensure we have an array of results.
246
-        if (!is_array($result) && null !== $result) {
247
-            $result = [$result];
248
-        }
249
-
250
-        // Maybe hydrate the results.
251
-        if (null !== $result && $this->useHydrator()) {
252
-            $result = $this->getHydrator()->fromMany($result);
253
-        }
254
-
255
-        $this->fireEvents('after-select', $result, $executionTime);
256
-
257
-        return $result;
258
-    }
259
-
260
-    /**
261
-     * Returns a populated instance of the Hydrator.
262
-     *
263
-     * @return Hydrator
264
-     */
265
-    protected function getHydrator(): Hydrator /* @phpstan-ignore-line */
266
-    {
267
-        $hydrator = new Hydrator($this->getFetchMode(), $this->hydratorConstructorArgs ?? []); /* @phpstan-ignore-line */
268
-
269
-        return $hydrator;
270
-    }
271
-
272
-    /**
273
-     * Checks if the results should be mapped via the hydrator
274
-     *
275
-     * @return bool
276
-     */
277
-    protected function useHydrator(): bool
278
-    {
279
-        return !in_array($this->getFetchMode(), [\ARRAY_A, \ARRAY_N, \OBJECT, \OBJECT_K]);
280
-    }
281
-
282
-    /**
283
-     * Find all matching a simple where condition.
284
-     *
285
-     * Shortcut of ->where('key','=','value')->limit(1)->get();
286
-     *
287
-     * @return \stdClass\array<mixed,mixed>|object|null Can return any object using hydrator
288
-     */
289
-    public function first()
290
-    {
291
-        $this->limit(1);
292
-        $result = $this->get();
293
-
294
-        return empty($result) ? null : $result[0];
295
-    }
296
-
297
-    /**
298
-     * Find all matching a simple where condition.
299
-     *
300
-     * Shortcut of ->where('key','=','value')->get();
301
-     *
302
-     * @param string $fieldName
303
-     * @param mixed $value
304
-     *
305
-     * @return array<mixed,mixed>|null Can return any object using hydrator
306
-     */
307
-    public function findAll($fieldName, $value)
308
-    {
309
-        $this->where($fieldName, '=', $value);
310
-
311
-        return $this->get();
312
-    }
313
-
314
-    /**
315
-     * @param string $fieldName
316
-     * @param mixed $value
317
-     *
318
-     * @return \stdClass\array<mixed,mixed>|object|null Can return any object using hydrator
319
-     */
320
-    public function find($value, $fieldName = 'id')
321
-    {
322
-        $this->where($fieldName, '=', $value);
323
-
324
-        return $this->first();
325
-    }
326
-
327
-    /**
328
-     * @param string $fieldName
329
-     * @param mixed $value
330
-     *
331
-     * @return \stdClass\array<mixed,mixed>|object Can return any object using hydrator
332
-     * @throws Exception If fails to find
333
-     */
334
-    public function findOrFail($value, $fieldName = 'id')
335
-    {
336
-        $result = $this->find($value, $fieldName);
337
-        if (null === $result) {
338
-            throw new Exception("Failed to find {$fieldName}={$value}", 1);
339
-        }
340
-        return $result;
341
-    }
342
-
343
-    /**
344
-     * Used to handle all aggregation method.
345
-     *
346
-     * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/
347
-     *
348
-     * @param string $type
349
-     * @param string $field
350
-     *
351
-     * @return float
352
-     */
353
-    protected function aggregate(string $type, string $field = '*'): float
354
-    {
355
-        // Verify that field exists
356
-        if ('*' !== $field && true === isset($this->statements['selects']) && false === \in_array($field, $this->statements['selects'], true)) {
357
-            throw new \Exception(sprintf('Failed %s query - the column %s hasn\'t been selected in the query.', $type, $field));
358
-        }
359
-
360
-        if (false === isset($this->statements['tables'])) {
361
-            throw new Exception('No table selected');
362
-        }
363
-
364
-        $count = $this
365
-            ->table($this->subQuery($this, 'count'))
366
-            ->select([$this->raw(sprintf('%s(%s) AS field', strtoupper($type), $field))])
367
-            ->first();
368
-
369
-        return true === isset($count->field) ? (float)$count->field : 0;
370
-    }
371
-
372
-    /**
373
-     * Get count of all the rows for the current query
374
-     *
375
-     * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/
376
-     *
377
-     * @param string $field
378
-     *
379
-     * @return int
380
-     *
381
-     * @throws Exception
382
-     */
383
-    public function count(string $field = '*'): int
384
-    {
385
-        return (int)$this->aggregate('count', $field);
386
-    }
387
-
388
-    /**
389
-     * Get the sum for a field in the current query
390
-     *
391
-     * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/
392
-     *
393
-     * @param string $field
394
-     *
395
-     * @return float
396
-     *
397
-     * @throws Exception
398
-     */
399
-    public function sum(string $field): float
400
-    {
401
-        return $this->aggregate('sum', $field);
402
-    }
403
-
404
-    /**
405
-     * Get the average for a field in the current query
406
-     *
407
-     * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/
408
-     *
409
-     * @param string $field
410
-     *
411
-     * @return float
412
-     *
413
-     * @throws Exception
414
-     */
415
-    public function average(string $field): float
416
-    {
417
-        return $this->aggregate('avg', $field);
418
-    }
419
-
420
-    /**
421
-     * Get the minimum for a field in the current query
422
-     *
423
-     * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/
424
-     *
425
-     * @param string $field
426
-     *
427
-     * @return float
428
-     *
429
-     * @throws Exception
430
-     */
431
-    public function min(string $field): float
432
-    {
433
-        return $this->aggregate('min', $field);
434
-    }
435
-
436
-    /**
437
-     * Get the maximum for a field in the current query
438
-     *
439
-     * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/
440
-     *
441
-     * @param string $field
442
-     *
443
-     * @return float
444
-     *
445
-     * @throws Exception
446
-     */
447
-    public function max(string $field): float
448
-    {
449
-        return $this->aggregate('max', $field);
450
-    }
451
-
452
-    /**
453
-     * @param string $type
454
-     * @param bool|array<mixed, mixed> $dataToBePassed
455
-     *
456
-     * @return mixed
457
-     *
458
-     * @throws Exception
459
-     */
460
-    public function getQuery(string $type = 'select', $dataToBePassed = [])
461
-    {
462
-        $allowedTypes = ['select', 'insert', 'insertignore', 'replace', 'delete', 'update', 'criteriaonly'];
463
-        if (!in_array(strtolower($type), $allowedTypes)) {
464
-            throw new Exception($type . ' is not a known type.', 2);
465
-        }
466
-
467
-        $queryArr = $this->adapterInstance->$type($this->statements, $dataToBePassed);
468
-
469
-        return $this->container->build(
470
-            QueryObject::class,
471
-            [$queryArr['sql'], $queryArr['bindings'], $this->dbInstance]
472
-        );
473
-    }
474
-
475
-    /**
476
-     * @param QueryBuilderHandler $queryBuilder
477
-     * @param string|null $alias
478
-     *
479
-     * @return Raw
480
-     */
481
-    public function subQuery(QueryBuilderHandler $queryBuilder, ?string $alias = null)
482
-    {
483
-        $sql = '(' . $queryBuilder->getQuery()->getRawSql() . ')';
484
-        if (is_string($alias) && 0 !== mb_strlen($alias)) {
485
-            $sql = $sql . ' as ' . $alias;
486
-        }
487
-
488
-        return $queryBuilder->raw($sql);
489
-    }
490
-
491
-    /**
492
-     * Handles the various insert operations based on the type.
493
-     *
494
-     * @param array<int|string, mixed|mixed[]> $data
495
-     * @param string $type
496
-     *
497
-     * @return int|int[]|mixed|null can return a single row id, array of row ids, null (for failed) or any other value short circuited from event
498
-     */
499
-    private function doInsert(array $data, string $type)
500
-    {
501
-        $eventResult = $this->fireEvents('before-insert');
502
-        if (!is_null($eventResult)) {
503
-            return $eventResult;
504
-        }
505
-
506
-        // If first value is not an array () not a batch insert)
507
-        if (!is_array(current($data))) {
508
-            $queryObject = $this->getQuery($type, $data);
509
-
510
-            list($preparedQuery, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings());
511
-            $this->dbInstance->get_results($preparedQuery);
512
-
513
-            // Check we have a result.
514
-            $return = 1 === $this->dbInstance->rows_affected ? $this->dbInstance->insert_id : null;
515
-        } else {
516
-            // Its a batch insert
517
-            $return        = [];
518
-            $executionTime = 0;
519
-            foreach ($data as $subData) {
520
-                $queryObject = $this->getQuery($type, $subData);
521
-
522
-                list($preparedQuery, $time) = $this->statement($queryObject->getSql(), $queryObject->getBindings());
523
-                $this->dbInstance->get_results($preparedQuery);
524
-                $executionTime += $time;
525
-
526
-                if (1 === $this->dbInstance->rows_affected) {
527
-                    $return[] = $this->dbInstance->insert_id;
528
-                }
529
-            }
530
-        }
531
-
532
-        $this->fireEvents('after-insert', $return, $executionTime);
533
-
534
-        return $return;
535
-    }
536
-
537
-    /**
538
-     * @param array<int|string, mixed|mixed[]> $data either key=>value array for single or array of arrays for bulk
539
-     *
540
-     * @return int|int[]|mixed|null can return a single row id, array of row ids, null (for failed) or any other value short circuited from event
541
-     */
542
-    public function insert($data)
543
-    {
544
-        return $this->doInsert($data, 'insert');
545
-    }
546
-
547
-    /**
548
-     *
549
-     * @param array<int|string, mixed|mixed[]> $data either key=>value array for single or array of arrays for bulk
550
-     *
551
-     * @return int|int[]|mixed|null can return a single row id, array of row ids, null (for failed) or any other value short circuited from event
552
-     */
553
-    public function insertIgnore($data)
554
-    {
555
-        return $this->doInsert($data, 'insertignore');
556
-    }
557
-
558
-    /**
559
-     *
560
-     * @param array<int|string, mixed|mixed[]> $data either key=>value array for single or array of arrays for bulk
561
-     *
562
-     * @return int|int[]|mixed|null can return a single row id, array of row ids, null (for failed) or any other value short circuited from event
563
-     */
564
-    public function replace($data)
565
-    {
566
-        return $this->doInsert($data, 'replace');
567
-    }
568
-
569
-    /**
570
-     * @param array<string, mixed> $data
571
-     *
572
-     * @return int|null
573
-     */
574
-    public function update($data)
575
-    {
576
-        $eventResult = $this->fireEvents('before-update');
577
-        if (!is_null($eventResult)) {
578
-            return $eventResult;
579
-        }
580
-        $queryObject                         = $this->getQuery('update', $data);
581
-        list($preparedQuery, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings());
582
-
583
-        $this->dbInstance()->get_results($preparedQuery);
584
-        $this->fireEvents('after-update', $queryObject, $executionTime);
585
-
586
-        return 0 !== $this->dbInstance()->rows_affected
587
-            ? $this->dbInstance()->rows_affected
588
-            : null;
589
-    }
590
-
591
-    /**
592
-     * @param array<string, mixed> $data
593
-     *
594
-     * @return int|null will return row id for insert and bool for success/fail on update
595
-     */
596
-    public function updateOrInsert($data)
597
-    {
598
-        if ($this->first()) {
599
-            return $this->update($data);
600
-        }
601
-
602
-        return $this->insert($data);
603
-    }
604
-
605
-    /**
606
-     * @param array<string, mixed> $data
607
-     *
608
-     * @return static
609
-     */
610
-    public function onDuplicateKeyUpdate($data)
611
-    {
612
-        $this->addStatement('onduplicate', $data);
613
-
614
-        return $this;
615
-    }
616
-
617
-    /**
618
-     * @return int number of rows effected
619
-     */
620
-    public function delete(): int
621
-    {
622
-        $eventResult = $this->fireEvents('before-delete');
623
-        if (!is_null($eventResult)) {
624
-            return $eventResult;
625
-        }
626
-
627
-        $queryObject = $this->getQuery('delete');
628
-
629
-        list($preparedQuery, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings());
630
-        $this->dbInstance()->get_results($preparedQuery);
631
-        $this->fireEvents('after-delete', $queryObject, $executionTime);
632
-
633
-        return $this->dbInstance()->rows_affected;
634
-    }
635
-
636
-    /**
637
-     * @param string|Raw ...$tables Single table or array of tables
638
-     *
639
-     * @return static
640
-     *
641
-     * @throws Exception
642
-     */
643
-    public function table(...$tables): QueryBuilderHandler
644
-    {
645
-        $instance =  $this->constructCurrentBuilderClass($this->connection);
646
-        $this->setFetchMode($this->getFetchMode(), $this->hydratorConstructorArgs);
647
-        $tables = $this->addTablePrefix($tables, false);
648
-        $instance->addStatement('tables', $tables);
649
-
650
-        return $instance;
651
-    }
652
-
653
-    /**
654
-     * @param string|Raw ...$tables Single table or array of tables
655
-     *
656
-     * @return static
657
-     */
658
-    public function from(...$tables): self
659
-    {
660
-        $tables = $this->addTablePrefix($tables, false);
661
-        $this->addStatement('tables', $tables);
662
-
663
-        return $this;
664
-    }
665
-
666
-    /**
667
-     * @param string|string[]|Raw[]|array<string, string> $fields
668
-     *
669
-     * @return static
670
-     */
671
-    public function select($fields): self
672
-    {
673
-        if (!is_array($fields)) {
674
-            $fields = func_get_args();
675
-        }
676
-
677
-        $fields = $this->addTablePrefix($fields);
678
-        $this->addStatement('selects', $fields);
679
-
680
-        return $this;
681
-    }
682
-
683
-    /**
684
-     * @param string|string[]|Raw[]|array<string, string> $fields
685
-     *
686
-     * @return static
687
-     */
688
-    public function selectDistinct($fields)
689
-    {
690
-        $this->select($fields);
691
-        $this->addStatement('distinct', true);
692
-
693
-        return $this;
694
-    }
695
-
696
-    /**
697
-     * @param string|string[] $field either the single field or an array of fields
698
-     *
699
-     * @return static
700
-     */
701
-    public function groupBy($field): self
702
-    {
703
-        $field = $this->addTablePrefix($field);
704
-        $this->addStatement('groupBys', $field);
705
-
706
-        return $this;
707
-    }
708
-
709
-    /**
710
-     * @param string|array<string|Raw, mixed> $fields
711
-     * @param string          $defaultDirection
712
-     *
713
-     * @return static
714
-     */
715
-    public function orderBy($fields, string $defaultDirection = 'ASC'): self
716
-    {
717
-        if (!is_array($fields)) {
718
-            $fields = [$fields];
719
-        }
720
-
721
-        foreach ($fields as $key => $value) {
722
-            $field = $key;
723
-            $type  = $value;
724
-            if (is_int($key)) {
725
-                $field = $value;
726
-                $type  = $defaultDirection;
727
-            }
728
-            if (!$field instanceof Raw) {
729
-                $field = $this->addTablePrefix($field);
730
-            }
731
-            $this->statements['orderBys'][] = compact('field', 'type');
732
-        }
733
-
734
-        return $this;
735
-    }
736
-
737
-    /**
738
-     * @param int $limit
739
-     *
740
-     * @return static
741
-     */
742
-    public function limit(int $limit): self
743
-    {
744
-        $this->statements['limit'] = $limit;
745
-
746
-        return $this;
747
-    }
748
-
749
-    /**
750
-     * @param int $offset
751
-     *
752
-     * @return static
753
-     */
754
-    public function offset(int $offset): self
755
-    {
756
-        $this->statements['offset'] = $offset;
757
-
758
-        return $this;
759
-    }
760
-
761
-    /**
762
-     * @param string|string[]|Raw|Raw[]       $key
763
-     * @param string $operator
764
-     * @param mixed $value
765
-     * @param string $joiner
766
-     *
767
-     * @return static
768
-     */
769
-    public function having($key, string $operator, $value, string $joiner = 'AND')
770
-    {
771
-        $key                           = $this->addTablePrefix($key);
772
-        $this->statements['havings'][] = compact('key', 'operator', 'value', 'joiner');
773
-
774
-        return $this;
775
-    }
776
-
777
-    /**
778
-     * @param string|string[]|Raw|Raw[]       $key
779
-     * @param string $operator
780
-     * @param mixed $value
781
-     *
782
-     * @return static
783
-     */
784
-    public function orHaving($key, $operator, $value)
785
-    {
786
-        return $this->having($key, $operator, $value, 'OR');
787
-    }
788
-
789
-    /**
790
-     * @param string|Raw $key
791
-     * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
792
-     * @param mixed|null $value
793
-     *
794
-     * @return static
795
-     */
796
-    public function where($key, $operator = null, $value = null): self
797
-    {
798
-        // If two params are given then assume operator is =
799
-        if (2 == func_num_args()) {
800
-            $value    = $operator;
801
-            $operator = '=';
802
-        }
803
-
804
-        return $this->whereHandler($key, $operator, $value);
805
-    }
806
-
807
-    /**
808
-     * @param string|Raw $key
809
-     * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
810
-     * @param mixed|null $value
811
-     *
812
-     * @return static
813
-     */
814
-    public function orWhere($key, $operator = null, $value = null): self
815
-    {
816
-        // If two params are given then assume operator is =
817
-        if (2 == func_num_args()) {
818
-            $value    = $operator;
819
-            $operator = '=';
820
-        }
821
-
822
-        return $this->whereHandler($key, $operator, $value, 'OR');
823
-    }
824
-
825
-    /**
826
-     * @param string|Raw $key
827
-     * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
828
-     * @param mixed|null $value
829
-     *
830
-     * @return static
831
-     */
832
-    public function whereNot($key, $operator = null, $value = null): self
833
-    {
834
-        // If two params are given then assume operator is =
835
-        if (2 == func_num_args()) {
836
-            $value    = $operator;
837
-            $operator = '=';
838
-        }
839
-
840
-        return $this->whereHandler($key, $operator, $value, 'AND NOT');
841
-    }
842
-
843
-    /**
844
-     * @param string|Raw $key
845
-     * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
846
-     * @param mixed|null $value
847
-     *
848
-     * @return static
849
-     */
850
-    public function orWhereNot($key, $operator = null, $value = null)
851
-    {
852
-        // If two params are given then assume operator is =
853
-        if (2 == func_num_args()) {
854
-            $value    = $operator;
855
-            $operator = '=';
856
-        }
857
-
858
-        return $this->whereHandler($key, $operator, $value, 'OR NOT');
859
-    }
860
-
861
-    /**
862
-     * @param string|Raw $key
863
-     * @param mixed[]|string|Raw $values
864
-     *
865
-     * @return static
866
-     */
867
-    public function whereIn($key, $values): self
868
-    {
869
-        return $this->whereHandler($key, 'IN', $values, 'AND');
870
-    }
871
-
872
-    /**
873
-     * @param string|Raw $key
874
-     * @param mixed[]|string|Raw $values
875
-     *
876
-     * @return static
877
-     */
878
-    public function whereNotIn($key, $values): self
879
-    {
880
-        return $this->whereHandler($key, 'NOT IN', $values, 'AND');
881
-    }
882
-
883
-    /**
884
-     * @param string|Raw $key
885
-     * @param mixed[]|string|Raw $values
886
-     *
887
-     * @return static
888
-     */
889
-    public function orWhereIn($key, $values): self
890
-    {
891
-        return $this->whereHandler($key, 'IN', $values, 'OR');
892
-    }
893
-
894
-    /**
895
-     * @param string|Raw $key
896
-     * @param mixed[]|string|Raw $values
897
-     *
898
-     * @return static
899
-     */
900
-    public function orWhereNotIn($key, $values): self
901
-    {
902
-        return $this->whereHandler($key, 'NOT IN', $values, 'OR');
903
-    }
904
-
905
-    /**
906
-     * @param string|Raw $key
907
-     * @param mixed $valueFrom
908
-     * @param mixed $valueTo
909
-     *
910
-     * @return static
911
-     */
912
-    public function whereBetween($key, $valueFrom, $valueTo): self
913
-    {
914
-        return $this->whereHandler($key, 'BETWEEN', [$valueFrom, $valueTo], 'AND');
915
-    }
916
-
917
-    /**
918
-     * @param string|Raw $key
919
-     * @param mixed $valueFrom
920
-     * @param mixed $valueTo
921
-     *
922
-     * @return static
923
-     */
924
-    public function orWhereBetween($key, $valueFrom, $valueTo): self
925
-    {
926
-        return $this->whereHandler($key, 'BETWEEN', [$valueFrom, $valueTo], 'OR');
927
-    }
928
-
929
-    /**
930
-     * Handles all function call based where conditions
931
-     *
932
-     * @param string|Raw $key
933
-     * @param string $function
934
-     * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
935
-     * @param mixed|null $value
936
-     * @return static
937
-     */
938
-    protected function whereFunctionCallHandler($key, $function, $operator, $value): self
939
-    {
940
-        $key = \sprintf('%s(%s)', $function, $this->addTablePrefix($key));
941
-        return $this->where($key, $operator, $value);
942
-    }
943
-
944
-    /**
945
-     * @param string|Raw $key
946
-     * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
947
-     * @param mixed|null $value
948
-     * @return self
949
-     */
950
-    public function whereMonth($key, $operator = null, $value = null): self
951
-    {
952
-        // If two params are given then assume operator is =
953
-        if (2 == func_num_args()) {
954
-            $value    = $operator;
955
-            $operator = '=';
956
-        }
957
-        return $this->whereFunctionCallHandler($key, 'MONTH', $operator, $value);
958
-    }
959
-
960
-    /**
961
-     * @param string|Raw $key
962
-     * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
963
-     * @param mixed|null $value
964
-     * @return self
965
-     */
966
-    public function whereDay($key, $operator = null, $value = null): self
967
-    {
968
-        // If two params are given then assume operator is =
969
-        if (2 == func_num_args()) {
970
-            $value    = $operator;
971
-            $operator = '=';
972
-        }
973
-        return $this->whereFunctionCallHandler($key, 'DAY', $operator, $value);
974
-    }
975
-
976
-    /**
977
-     * @param string|Raw $key
978
-     * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
979
-     * @param mixed|null $value
980
-     * @return self
981
-     */
982
-    public function whereYear($key, $operator = null, $value = null): self
983
-    {
984
-        // If two params are given then assume operator is =
985
-        if (2 == func_num_args()) {
986
-            $value    = $operator;
987
-            $operator = '=';
988
-        }
989
-        return $this->whereFunctionCallHandler($key, 'YEAR', $operator, $value);
990
-    }
991
-
992
-    /**
993
-     * @param string|Raw $key
994
-     * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
995
-     * @param mixed|null $value
996
-     * @return self
997
-     */
998
-    public function whereDate($key, $operator = null, $value = null): self
999
-    {
1000
-        // If two params are given then assume operator is =
1001
-        if (2 == func_num_args()) {
1002
-            $value    = $operator;
1003
-            $operator = '=';
1004
-        }
1005
-        return $this->whereFunctionCallHandler($key, 'DATE', $operator, $value);
1006
-    }
1007
-
1008
-    /**
1009
-     * @param string|Raw $key
1010
-     *
1011
-     * @return static
1012
-     */
1013
-    public function whereNull($key): self
1014
-    {
1015
-        return $this->whereNullHandler($key);
1016
-    }
1017
-
1018
-    /**
1019
-     * @param string|Raw $key
1020
-     *
1021
-     * @return static
1022
-     */
1023
-    public function whereNotNull($key): self
1024
-    {
1025
-        return $this->whereNullHandler($key, 'NOT');
1026
-    }
1027
-
1028
-    /**
1029
-     * @param string|Raw $key
1030
-     *
1031
-     * @return static
1032
-     */
1033
-    public function orWhereNull($key): self
1034
-    {
1035
-        return $this->whereNullHandler($key, '', 'or');
1036
-    }
1037
-
1038
-    /**
1039
-     * @param string|Raw $key
1040
-     *
1041
-     * @return static
1042
-     */
1043
-    public function orWhereNotNull($key): self
1044
-    {
1045
-        return $this->whereNullHandler($key, 'NOT', 'or');
1046
-    }
1047
-
1048
-    /**
1049
-     * @param string|Raw $key
1050
-     * @param string $prefix
1051
-     * @param string $operator
1052
-     *
1053
-     * @return static
1054
-     */
1055
-    protected function whereNullHandler($key, string $prefix = '', $operator = ''): self
1056
-    {
1057
-        $prefix = 0 === mb_strlen($prefix) ? '' : " {$prefix}";
1058
-
1059
-        if ($key instanceof Raw) {
1060
-            $key = $this->adapterInstance->parseRaw($key);
1061
-        }
1062
-
1063
-        $key = $this->adapterInstance->wrapSanitizer($this->addTablePrefix($key));
1064
-        if ($key instanceof Closure) {
1065
-            throw new Exception('Key used for whereNull condition must be a string or raw exrpession.', 1);
1066
-        }
1067
-
1068
-        return $this->{$operator . 'Where'}($this->raw("{$key} IS{$prefix} NULL"));
1069
-    }
1070
-
1071
-    /**
1072
-     * @param string|Raw $table
1073
-     * @param string|Raw|Closure $key
1074
-     * @param string|null $operator
1075
-     * @param mixed $value
1076
-     * @param string $type
1077
-     *
1078
-     * @return static
1079
-     */
1080
-    public function join($table, $key, ?string $operator = null, $value = null, $type = 'inner')
1081
-    {
1082
-        if (!$key instanceof Closure) {
1083
-            $key = function ($joinBuilder) use ($key, $operator, $value) {
1084
-                $joinBuilder->on($key, $operator, $value);
1085
-            };
1086
-        }
1087
-
1088
-        // Build a new JoinBuilder class, keep it by reference so any changes made
1089
-        // in the closure should reflect here
1090
-        $joinBuilder = $this->container->build(JoinBuilder::class, [$this->connection]);
1091
-        $joinBuilder = &$joinBuilder;
1092
-        // Call the closure with our new joinBuilder object
1093
-        $key($joinBuilder);
1094
-        $table = $this->addTablePrefix($table, false);
1095
-        // Get the criteria only query from the joinBuilder object
1096
-        $this->statements['joins'][] = compact('type', 'table', 'joinBuilder');
1097
-        return $this;
1098
-    }
1099
-
1100
-    /**
1101
-     * Runs a transaction
1102
-     *
1103
-     * @param \Closure(Transaction):void $callback
1104
-     *
1105
-     * @return static
1106
-     */
1107
-    public function transaction(Closure $callback): self
1108
-    {
1109
-        try {
1110
-            // Begin the transaction
1111
-            $this->dbInstance->query('START TRANSACTION');
1112
-
1113
-            // Get the Transaction class
1114
-            $transaction = $this->container->build(Transaction::class, [$this->connection]);
1115
-
1116
-            $this->handleTransactionCall($callback, $transaction);
1117
-
1118
-            // If no errors have been thrown or the transaction wasn't completed within
1119
-            $this->dbInstance->query('COMMIT');
1120
-
1121
-            return $this;
1122
-        } catch (TransactionHaltException $e) {
1123
-            // Commit or rollback behavior has been handled in the closure, so exit
1124
-            return $this;
1125
-        } catch (\Exception $e) {
1126
-            // something happened, rollback changes
1127
-            $this->dbInstance->query('ROLLBACK');
1128
-
1129
-            return $this;
1130
-        }
1131
-    }
1132
-
1133
-    /**
1134
-     * Handles the transaction call.
1135
-     *
1136
-     * Catches any WP Errors (printed)
1137
-     *
1138
-     * @param Closure    $callback
1139
-     * @param Transaction $transaction
1140
-     *
1141
-     * @return void
1142
-     *
1143
-     * @throws Exception
1144
-     */
1145
-    protected function handleTransactionCall(Closure $callback, Transaction $transaction): void
1146
-    {
1147
-        try {
1148
-            ob_start();
1149
-            $callback($transaction);
1150
-            $output = ob_get_clean() ?: '';
1151
-        } catch (Throwable $th) {
1152
-            ob_end_clean();
1153
-            throw $th;
1154
-        }
1155
-
1156
-        // If we caught an error, throw an exception.
1157
-        if (0 !== mb_strlen($output)) {
1158
-            throw new Exception($output);
1159
-        }
1160
-    }
1161
-
1162
-    /**
1163
-     * @param string|Raw $table
1164
-     * @param string|Raw|Closure $key
1165
-     * @param string|null $operator
1166
-     * @param mixed $value
1167
-     *
1168
-     * @return static
1169
-     */
1170
-    public function leftJoin($table, $key, $operator = null, $value = null)
1171
-    {
1172
-        return $this->join($table, $key, $operator, $value, 'left');
1173
-    }
1174
-
1175
-    /**
1176
-     * @param string|Raw $table
1177
-     * @param string|Raw|Closure $key
1178
-     * @param string|null $operator
1179
-     * @param mixed $value
1180
-     *
1181
-     * @return static
1182
-     */
1183
-    public function rightJoin($table, $key, $operator = null, $value = null)
1184
-    {
1185
-        return $this->join($table, $key, $operator, $value, 'right');
1186
-    }
1187
-
1188
-    /**
1189
-     * @param string|Raw $table
1190
-     * @param string|Raw|Closure $key
1191
-     * @param string|null $operator
1192
-     * @param mixed $value
1193
-     *
1194
-     * @return static
1195
-     */
1196
-    public function innerJoin($table, $key, $operator = null, $value = null)
1197
-    {
1198
-        return $this->join($table, $key, $operator, $value, 'inner');
1199
-    }
1200
-
1201
-    /**
1202
-     * @param string|Raw $table
1203
-     * @param string|Raw|Closure $key
1204
-     * @param string|null $operator
1205
-     * @param mixed $value
1206
-     *
1207
-     * @return static
1208
-     */
1209
-    public function crossJoin($table, $key, $operator = null, $value = null)
1210
-    {
1211
-        return $this->join($table, $key, $operator, $value, 'cross');
1212
-    }
1213
-
1214
-    /**
1215
-     * @param string|Raw $table
1216
-     * @param string|Raw|Closure $key
1217
-     * @param string|null $operator
1218
-     * @param mixed $value
1219
-     *
1220
-     * @return static
1221
-     */
1222
-    public function outerJoin($table, $key, $operator = null, $value = null)
1223
-    {
1224
-        return $this->join($table, $key, $operator, $value, 'outer');
1225
-    }
1226
-
1227
-    /**
1228
-     * Shortcut to join 2 tables on the same key name with equals
1229
-     *
1230
-     * @param string $table
1231
-     * @param string $key
1232
-     * @param string $type
1233
-     * @return self
1234
-     * @throws Exception If base table is set as more than 1 or 0
1235
-     */
1236
-    public function joinUsing(string $table, string $key, string $type = 'INNER'): self
1237
-    {
1238
-        if (!array_key_exists('tables', $this->statements) || count($this->statements['tables']) !== 1) {
1239
-            throw new Exception("JoinUsing can only be used with a single table set as the base of the query", 1);
1240
-        }
1241
-        $baseTable = end($this->statements['tables']);
1242
-
1243
-        $remoteKey = $table = $this->addTablePrefix("{$table}.{$key}", true);
1244
-        $localKey = $table = $this->addTablePrefix("{$baseTable}.{$key}", true);
1245
-        return $this->join($table, $remoteKey, '=', $localKey, $type);
1246
-    }
1247
-
1248
-    /**
1249
-     * Add a raw query
1250
-     *
1251
-     * @param string|Raw $value
1252
-     * @param mixed|mixed[] $bindings
1253
-     *
1254
-     * @return Raw
1255
-     */
1256
-    public function raw($value, $bindings = []): Raw
1257
-    {
1258
-        return new Raw($value, $bindings);
1259
-    }
1260
-
1261
-    /**
1262
-     * Return wpdb instance
1263
-     *
1264
-     * @return wpdb
1265
-     */
1266
-    public function dbInstance(): wpdb
1267
-    {
1268
-        return $this->dbInstance;
1269
-    }
1270
-
1271
-    /**
1272
-     * @param Connection $connection
1273
-     *
1274
-     * @return static
1275
-     */
1276
-    public function setConnection(Connection $connection): self
1277
-    {
1278
-        $this->connection = $connection;
1279
-
1280
-        return $this;
1281
-    }
1282
-
1283
-    /**
1284
-     * @return Connection
1285
-     */
1286
-    public function getConnection()
1287
-    {
1288
-        return $this->connection;
1289
-    }
1290
-
1291
-    /**
1292
-     * @param string|Raw|Closure $key
1293
-     * @param string|null      $operator
1294
-     * @param mixed|null       $value
1295
-     * @param string $joiner
1296
-     *
1297
-     * @return static
1298
-     */
1299
-    protected function whereHandler($key, $operator = null, $value = null, $joiner = 'AND')
1300
-    {
1301
-        $key                          = $this->addTablePrefix($key);
1302
-        $this->statements['wheres'][] = compact('key', 'operator', 'value', 'joiner');
1303
-        return $this;
1304
-    }
1305
-
1306
-    /**
1307
-     * Add table prefix (if given) on given string.
1308
-     *
1309
-     * @param array<string|int, string|int|float|bool|Raw|Closure>|string|int|float|bool|Raw|Closure     $values
1310
-     * @param bool $tableFieldMix If we have mixes of field and table names with a "."
1311
-     *
1312
-     * @return mixed|mixed[]
1313
-     */
1314
-    public function addTablePrefix($values, bool $tableFieldMix = true)
1315
-    {
1316
-        if (is_null($this->tablePrefix)) {
1317
-            return $values;
1318
-        }
1319
-
1320
-        // $value will be an array and we will add prefix to all table names
1321
-
1322
-        // If supplied value is not an array then make it one
1323
-        $single = false;
1324
-        if (!is_array($values)) {
1325
-            $values = [$values];
1326
-            // We had single value, so should return a single value
1327
-            $single = true;
1328
-        }
1329
-
1330
-        $return = [];
1331
-
1332
-        foreach ($values as $key => $value) {
1333
-            // It's a raw query, just add it to our return array and continue next
1334
-            if ($value instanceof Raw || $value instanceof Closure) {
1335
-                $return[$key] = $value;
1336
-                continue;
1337
-            }
1338
-
1339
-            // If key is not integer, it is likely a alias mapping,
1340
-            // so we need to change prefix target
1341
-            $target = &$value;
1342
-            if (!is_int($key)) {
1343
-                $target = &$key;
1344
-            }
1345
-
1346
-            if (!$tableFieldMix || (is_string($target) && false !== strpos($target, '.'))) {
1347
-                $target = $this->tablePrefix . $target;
1348
-            }
1349
-
1350
-            $return[$key] = $value;
1351
-        }
1352
-
1353
-        // If we had single value then we should return a single value (end value of the array)
1354
-        return true === $single ? end($return) : $return;
1355
-    }
1356
-
1357
-    /**
1358
-     * @param string $key
1359
-     * @param mixed|mixed[]|bool $value
1360
-     *
1361
-     * @return void
1362
-     */
1363
-    protected function addStatement($key, $value)
1364
-    {
1365
-        if (!is_array($value)) {
1366
-            $value = [$value];
1367
-        }
1368
-
1369
-        if (!array_key_exists($key, $this->statements)) {
1370
-            $this->statements[$key] = $value;
1371
-        } else {
1372
-            $this->statements[$key] = array_merge($this->statements[$key], $value);
1373
-        }
1374
-    }
1375
-
1376
-    /**
1377
-     * @param string $event
1378
-     * @param string|Raw $table
1379
-     *
1380
-     * @return callable|null
1381
-     */
1382
-    public function getEvent(string $event, $table = ':any'): ?callable
1383
-    {
1384
-        return $this->connection->getEventHandler()->getEvent($event, $table);
1385
-    }
1386
-
1387
-    /**
1388
-     * @param string $event
1389
-     * @param string|Raw $table
1390
-     * @param Closure $action
1391
-     *
1392
-     * @return void
1393
-     */
1394
-    public function registerEvent($event, $table, Closure $action): void
1395
-    {
1396
-        $table = $table ?: ':any';
1397
-
1398
-        if (':any' != $table) {
1399
-            $table = $this->addTablePrefix($table, false);
1400
-        }
1401
-
1402
-        $this->connection->getEventHandler()->registerEvent($event, $table, $action);
1403
-    }
1404
-
1405
-    /**
1406
-     * @param string $event
1407
-     * @param string|Raw $table
1408
-     *
1409
-     * @return void
1410
-     */
1411
-    public function removeEvent(string $event, $table = ':any')
1412
-    {
1413
-        if (':any' != $table) {
1414
-            $table = $this->addTablePrefix($table, false);
1415
-        }
1416
-
1417
-        $this->connection->getEventHandler()->removeEvent($event, $table);
1418
-    }
1419
-
1420
-    /**
1421
-     * @param string $event
1422
-     *
1423
-     * @return mixed
1424
-     */
1425
-    public function fireEvents(string $event)
1426
-    {
1427
-        $params = func_get_args(); // @todo Replace this with an easier to read alteratnive
1428
-        array_unshift($params, $this);
1429
-
1430
-        return call_user_func_array([$this->connection->getEventHandler(), 'fireEvents'], $params);
1431
-    }
1432
-
1433
-    /**
1434
-     * @return array<string, mixed[]>
1435
-     */
1436
-    public function getStatements()
1437
-    {
1438
-        return $this->statements;
1439
-    }
1440
-
1441
-    /**
1442
-     * @return string will return WPDB Fetch mode
1443
-     */
1444
-    public function getFetchMode()
1445
-    {
1446
-        return null !== $this->fetchMode
1447
-            ? $this->fetchMode
1448
-            : \OBJECT;
1449
-    }
23
+	/**
24
+	 * @var \Viocon\Container
25
+	 */
26
+	protected $container;
27
+
28
+	/**
29
+	 * @var Connection
30
+	 */
31
+	protected $connection;
32
+
33
+	/**
34
+	 * @var array<string, mixed[]|mixed>
35
+	 */
36
+	protected $statements = [];
37
+
38
+	/**
39
+	 * @var wpdb
40
+	 */
41
+	protected $dbInstance;
42
+
43
+	/**
44
+	 * @var string|string[]|null
45
+	 */
46
+	protected $sqlStatement = null;
47
+
48
+	/**
49
+	 * @var string|null
50
+	 */
51
+	protected $tablePrefix = null;
52
+
53
+	/**
54
+	 * @var WPDBAdapter
55
+	 */
56
+	protected $adapterInstance;
57
+
58
+	/**
59
+	 * The mode to return results as.
60
+	 * Accepts WPDB constants or class names.
61
+	 *
62
+	 * @var string
63
+	 */
64
+	protected $fetchMode;
65
+
66
+	/**
67
+	 * Custom args used to construct models for hydrator
68
+	 *
69
+	 * @var array<int, mixed>|null
70
+	 */
71
+	protected $hydratorConstructorArgs;
72
+
73
+	/**
74
+	 * @param \Pixie\Connection|null $connection
75
+	 * @param string $fetchMode
76
+	 * @param mixed[] $hydratorConstructorArgs
77
+	 *
78
+	 * @throws Exception if no connection passed and not previously established
79
+	 */
80
+	final public function __construct(
81
+		Connection $connection = null,
82
+		string $fetchMode = \OBJECT,
83
+		?array $hydratorConstructorArgs = null
84
+	) {
85
+		if (is_null($connection)) {
86
+			// throws if connection not already established.
87
+			$connection = Connection::getStoredConnection();
88
+		}
89
+
90
+		// Set all dependencies from connection.
91
+		$this->connection = $connection;
92
+		$this->container  = $this->connection->getContainer();
93
+		$this->dbInstance = $this->connection->getDbInstance();
94
+		$this->setAdapterConfig($this->connection->getAdapterConfig());
95
+
96
+		// Set up optional hydration details.
97
+		$this->setFetchMode($fetchMode);
98
+		$this->hydratorConstructorArgs = $hydratorConstructorArgs;
99
+
100
+		// Query builder adapter instance
101
+		$this->adapterInstance = $this->container->build(
102
+			WPDBAdapter::class,
103
+			[$this->connection]
104
+		);
105
+	}
106
+
107
+	/**
108
+	 * Sets the config for WPDB
109
+	 *
110
+	 * @param array<string, mixed> $adapterConfig
111
+	 *
112
+	 * @return void
113
+	 */
114
+	protected function setAdapterConfig(array $adapterConfig): void
115
+	{
116
+		if (isset($adapterConfig['prefix'])) {
117
+			$this->tablePrefix = $adapterConfig['prefix'];
118
+		}
119
+	}
120
+
121
+	/**
122
+	 * Set the fetch mode
123
+	 *
124
+	 * @param string $mode
125
+	 * @param array<int, mixed>|null $constructorArgs
126
+	 *
127
+	 * @return static
128
+	 */
129
+	public function setFetchMode(string $mode, ?array $constructorArgs = null): self
130
+	{
131
+		$this->fetchMode               = $mode;
132
+		$this->hydratorConstructorArgs = $constructorArgs;
133
+
134
+		return $this;
135
+	}
136
+
137
+	/**
138
+	 * @param Connection|null $connection
139
+	 *
140
+	 * @return static
141
+	 *
142
+	 * @throws Exception
143
+	 */
144
+	public function newQuery(Connection $connection = null): self
145
+	{
146
+		if (is_null($connection)) {
147
+			$connection = $this->connection;
148
+		}
149
+
150
+		$newQuery = $this->constructCurrentBuilderClass($connection);
151
+		$newQuery->setFetchMode($this->getFetchMode(), $this->hydratorConstructorArgs);
152
+
153
+		return $newQuery;
154
+	}
155
+
156
+	/**
157
+	 * Returns a new instance of the current, with the passed connection.
158
+	 *
159
+	 * @param \Pixie\Connection $connection
160
+	 *
161
+	 * @return static
162
+	 */
163
+	protected function constructCurrentBuilderClass(Connection $connection): self
164
+	{
165
+		return new static($connection);
166
+	}
167
+
168
+	/**
169
+	 * Interpolates a query
170
+	 *
171
+	 * @param string $query
172
+	 * @param array<mixed> $bindings
173
+	 * @return string
174
+	 */
175
+	public function interpolateQuery(string $query, array $bindings = []): string
176
+	{
177
+		return $this->adapterInstance->interpolateQuery($query, $bindings);
178
+	}
179
+
180
+	/**
181
+	 * @param string           $sql
182
+	 * @param array<int,mixed> $bindings
183
+	 *
184
+	 * @return static
185
+	 */
186
+	public function query($sql, $bindings = []): self
187
+	{
188
+		list($this->sqlStatement) = $this->statement($sql, $bindings);
189
+
190
+		return $this;
191
+	}
192
+
193
+	/**
194
+	 * @param string           $sql
195
+	 * @param array<int,mixed> $bindings
196
+	 *
197
+	 * @return array{0:string, 1:float}
198
+	 */
199
+	public function statement(string $sql, $bindings = []): array
200
+	{
201
+		$start        = microtime(true);
202
+		$sqlStatement = empty($bindings) ? $sql : $this->interpolateQuery($sql, $bindings);
203
+
204
+		if (!is_string($sqlStatement)) {
205
+			throw new Exception('Could not interpolate query', 1);
206
+		}
207
+
208
+		return [$sqlStatement, microtime(true) - $start];
209
+	}
210
+
211
+	/**
212
+	 * Get all rows
213
+	 *
214
+	 * @return array<mixed,mixed>|null
215
+	 *
216
+	 * @throws Exception
217
+	 */
218
+	public function get()
219
+	{
220
+		$eventResult = $this->fireEvents('before-select');
221
+		if (!is_null($eventResult)) {
222
+			return $eventResult;
223
+		}
224
+		$executionTime = 0;
225
+		if (is_null($this->sqlStatement)) {
226
+			$queryObject = $this->getQuery('select');
227
+			$statement   = $this->statement(
228
+				$queryObject->getSql(),
229
+				$queryObject->getBindings()
230
+			);
231
+
232
+			$this->sqlStatement = $statement[0];
233
+			$executionTime      = $statement[1];
234
+		}
235
+
236
+		$start  = microtime(true);
237
+		$result = $this->dbInstance()->get_results(
238
+			is_array($this->sqlStatement) ? (end($this->sqlStatement) ?: '') : $this->sqlStatement,
239
+			// If we are using the hydrator, return as OBJECT and let the hydrator map the correct model.
240
+			$this->useHydrator() ? OBJECT : $this->getFetchMode()
241
+		);
242
+		$executionTime += microtime(true) - $start;
243
+		$this->sqlStatement = null;
244
+
245
+		// Ensure we have an array of results.
246
+		if (!is_array($result) && null !== $result) {
247
+			$result = [$result];
248
+		}
249
+
250
+		// Maybe hydrate the results.
251
+		if (null !== $result && $this->useHydrator()) {
252
+			$result = $this->getHydrator()->fromMany($result);
253
+		}
254
+
255
+		$this->fireEvents('after-select', $result, $executionTime);
256
+
257
+		return $result;
258
+	}
259
+
260
+	/**
261
+	 * Returns a populated instance of the Hydrator.
262
+	 *
263
+	 * @return Hydrator
264
+	 */
265
+	protected function getHydrator(): Hydrator /* @phpstan-ignore-line */
266
+	{
267
+		$hydrator = new Hydrator($this->getFetchMode(), $this->hydratorConstructorArgs ?? []); /* @phpstan-ignore-line */
268
+
269
+		return $hydrator;
270
+	}
271
+
272
+	/**
273
+	 * Checks if the results should be mapped via the hydrator
274
+	 *
275
+	 * @return bool
276
+	 */
277
+	protected function useHydrator(): bool
278
+	{
279
+		return !in_array($this->getFetchMode(), [\ARRAY_A, \ARRAY_N, \OBJECT, \OBJECT_K]);
280
+	}
281
+
282
+	/**
283
+	 * Find all matching a simple where condition.
284
+	 *
285
+	 * Shortcut of ->where('key','=','value')->limit(1)->get();
286
+	 *
287
+	 * @return \stdClass\array<mixed,mixed>|object|null Can return any object using hydrator
288
+	 */
289
+	public function first()
290
+	{
291
+		$this->limit(1);
292
+		$result = $this->get();
293
+
294
+		return empty($result) ? null : $result[0];
295
+	}
296
+
297
+	/**
298
+	 * Find all matching a simple where condition.
299
+	 *
300
+	 * Shortcut of ->where('key','=','value')->get();
301
+	 *
302
+	 * @param string $fieldName
303
+	 * @param mixed $value
304
+	 *
305
+	 * @return array<mixed,mixed>|null Can return any object using hydrator
306
+	 */
307
+	public function findAll($fieldName, $value)
308
+	{
309
+		$this->where($fieldName, '=', $value);
310
+
311
+		return $this->get();
312
+	}
313
+
314
+	/**
315
+	 * @param string $fieldName
316
+	 * @param mixed $value
317
+	 *
318
+	 * @return \stdClass\array<mixed,mixed>|object|null Can return any object using hydrator
319
+	 */
320
+	public function find($value, $fieldName = 'id')
321
+	{
322
+		$this->where($fieldName, '=', $value);
323
+
324
+		return $this->first();
325
+	}
326
+
327
+	/**
328
+	 * @param string $fieldName
329
+	 * @param mixed $value
330
+	 *
331
+	 * @return \stdClass\array<mixed,mixed>|object Can return any object using hydrator
332
+	 * @throws Exception If fails to find
333
+	 */
334
+	public function findOrFail($value, $fieldName = 'id')
335
+	{
336
+		$result = $this->find($value, $fieldName);
337
+		if (null === $result) {
338
+			throw new Exception("Failed to find {$fieldName}={$value}", 1);
339
+		}
340
+		return $result;
341
+	}
342
+
343
+	/**
344
+	 * Used to handle all aggregation method.
345
+	 *
346
+	 * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/
347
+	 *
348
+	 * @param string $type
349
+	 * @param string $field
350
+	 *
351
+	 * @return float
352
+	 */
353
+	protected function aggregate(string $type, string $field = '*'): float
354
+	{
355
+		// Verify that field exists
356
+		if ('*' !== $field && true === isset($this->statements['selects']) && false === \in_array($field, $this->statements['selects'], true)) {
357
+			throw new \Exception(sprintf('Failed %s query - the column %s hasn\'t been selected in the query.', $type, $field));
358
+		}
359
+
360
+		if (false === isset($this->statements['tables'])) {
361
+			throw new Exception('No table selected');
362
+		}
363
+
364
+		$count = $this
365
+			->table($this->subQuery($this, 'count'))
366
+			->select([$this->raw(sprintf('%s(%s) AS field', strtoupper($type), $field))])
367
+			->first();
368
+
369
+		return true === isset($count->field) ? (float)$count->field : 0;
370
+	}
371
+
372
+	/**
373
+	 * Get count of all the rows for the current query
374
+	 *
375
+	 * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/
376
+	 *
377
+	 * @param string $field
378
+	 *
379
+	 * @return int
380
+	 *
381
+	 * @throws Exception
382
+	 */
383
+	public function count(string $field = '*'): int
384
+	{
385
+		return (int)$this->aggregate('count', $field);
386
+	}
387
+
388
+	/**
389
+	 * Get the sum for a field in the current query
390
+	 *
391
+	 * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/
392
+	 *
393
+	 * @param string $field
394
+	 *
395
+	 * @return float
396
+	 *
397
+	 * @throws Exception
398
+	 */
399
+	public function sum(string $field): float
400
+	{
401
+		return $this->aggregate('sum', $field);
402
+	}
403
+
404
+	/**
405
+	 * Get the average for a field in the current query
406
+	 *
407
+	 * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/
408
+	 *
409
+	 * @param string $field
410
+	 *
411
+	 * @return float
412
+	 *
413
+	 * @throws Exception
414
+	 */
415
+	public function average(string $field): float
416
+	{
417
+		return $this->aggregate('avg', $field);
418
+	}
419
+
420
+	/**
421
+	 * Get the minimum for a field in the current query
422
+	 *
423
+	 * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/
424
+	 *
425
+	 * @param string $field
426
+	 *
427
+	 * @return float
428
+	 *
429
+	 * @throws Exception
430
+	 */
431
+	public function min(string $field): float
432
+	{
433
+		return $this->aggregate('min', $field);
434
+	}
435
+
436
+	/**
437
+	 * Get the maximum for a field in the current query
438
+	 *
439
+	 * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/
440
+	 *
441
+	 * @param string $field
442
+	 *
443
+	 * @return float
444
+	 *
445
+	 * @throws Exception
446
+	 */
447
+	public function max(string $field): float
448
+	{
449
+		return $this->aggregate('max', $field);
450
+	}
451
+
452
+	/**
453
+	 * @param string $type
454
+	 * @param bool|array<mixed, mixed> $dataToBePassed
455
+	 *
456
+	 * @return mixed
457
+	 *
458
+	 * @throws Exception
459
+	 */
460
+	public function getQuery(string $type = 'select', $dataToBePassed = [])
461
+	{
462
+		$allowedTypes = ['select', 'insert', 'insertignore', 'replace', 'delete', 'update', 'criteriaonly'];
463
+		if (!in_array(strtolower($type), $allowedTypes)) {
464
+			throw new Exception($type . ' is not a known type.', 2);
465
+		}
466
+
467
+		$queryArr = $this->adapterInstance->$type($this->statements, $dataToBePassed);
468
+
469
+		return $this->container->build(
470
+			QueryObject::class,
471
+			[$queryArr['sql'], $queryArr['bindings'], $this->dbInstance]
472
+		);
473
+	}
474
+
475
+	/**
476
+	 * @param QueryBuilderHandler $queryBuilder
477
+	 * @param string|null $alias
478
+	 *
479
+	 * @return Raw
480
+	 */
481
+	public function subQuery(QueryBuilderHandler $queryBuilder, ?string $alias = null)
482
+	{
483
+		$sql = '(' . $queryBuilder->getQuery()->getRawSql() . ')';
484
+		if (is_string($alias) && 0 !== mb_strlen($alias)) {
485
+			$sql = $sql . ' as ' . $alias;
486
+		}
487
+
488
+		return $queryBuilder->raw($sql);
489
+	}
490
+
491
+	/**
492
+	 * Handles the various insert operations based on the type.
493
+	 *
494
+	 * @param array<int|string, mixed|mixed[]> $data
495
+	 * @param string $type
496
+	 *
497
+	 * @return int|int[]|mixed|null can return a single row id, array of row ids, null (for failed) or any other value short circuited from event
498
+	 */
499
+	private function doInsert(array $data, string $type)
500
+	{
501
+		$eventResult = $this->fireEvents('before-insert');
502
+		if (!is_null($eventResult)) {
503
+			return $eventResult;
504
+		}
505
+
506
+		// If first value is not an array () not a batch insert)
507
+		if (!is_array(current($data))) {
508
+			$queryObject = $this->getQuery($type, $data);
509
+
510
+			list($preparedQuery, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings());
511
+			$this->dbInstance->get_results($preparedQuery);
512
+
513
+			// Check we have a result.
514
+			$return = 1 === $this->dbInstance->rows_affected ? $this->dbInstance->insert_id : null;
515
+		} else {
516
+			// Its a batch insert
517
+			$return        = [];
518
+			$executionTime = 0;
519
+			foreach ($data as $subData) {
520
+				$queryObject = $this->getQuery($type, $subData);
521
+
522
+				list($preparedQuery, $time) = $this->statement($queryObject->getSql(), $queryObject->getBindings());
523
+				$this->dbInstance->get_results($preparedQuery);
524
+				$executionTime += $time;
525
+
526
+				if (1 === $this->dbInstance->rows_affected) {
527
+					$return[] = $this->dbInstance->insert_id;
528
+				}
529
+			}
530
+		}
531
+
532
+		$this->fireEvents('after-insert', $return, $executionTime);
533
+
534
+		return $return;
535
+	}
536
+
537
+	/**
538
+	 * @param array<int|string, mixed|mixed[]> $data either key=>value array for single or array of arrays for bulk
539
+	 *
540
+	 * @return int|int[]|mixed|null can return a single row id, array of row ids, null (for failed) or any other value short circuited from event
541
+	 */
542
+	public function insert($data)
543
+	{
544
+		return $this->doInsert($data, 'insert');
545
+	}
546
+
547
+	/**
548
+	 *
549
+	 * @param array<int|string, mixed|mixed[]> $data either key=>value array for single or array of arrays for bulk
550
+	 *
551
+	 * @return int|int[]|mixed|null can return a single row id, array of row ids, null (for failed) or any other value short circuited from event
552
+	 */
553
+	public function insertIgnore($data)
554
+	{
555
+		return $this->doInsert($data, 'insertignore');
556
+	}
557
+
558
+	/**
559
+	 *
560
+	 * @param array<int|string, mixed|mixed[]> $data either key=>value array for single or array of arrays for bulk
561
+	 *
562
+	 * @return int|int[]|mixed|null can return a single row id, array of row ids, null (for failed) or any other value short circuited from event
563
+	 */
564
+	public function replace($data)
565
+	{
566
+		return $this->doInsert($data, 'replace');
567
+	}
568
+
569
+	/**
570
+	 * @param array<string, mixed> $data
571
+	 *
572
+	 * @return int|null
573
+	 */
574
+	public function update($data)
575
+	{
576
+		$eventResult = $this->fireEvents('before-update');
577
+		if (!is_null($eventResult)) {
578
+			return $eventResult;
579
+		}
580
+		$queryObject                         = $this->getQuery('update', $data);
581
+		list($preparedQuery, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings());
582
+
583
+		$this->dbInstance()->get_results($preparedQuery);
584
+		$this->fireEvents('after-update', $queryObject, $executionTime);
585
+
586
+		return 0 !== $this->dbInstance()->rows_affected
587
+			? $this->dbInstance()->rows_affected
588
+			: null;
589
+	}
590
+
591
+	/**
592
+	 * @param array<string, mixed> $data
593
+	 *
594
+	 * @return int|null will return row id for insert and bool for success/fail on update
595
+	 */
596
+	public function updateOrInsert($data)
597
+	{
598
+		if ($this->first()) {
599
+			return $this->update($data);
600
+		}
601
+
602
+		return $this->insert($data);
603
+	}
604
+
605
+	/**
606
+	 * @param array<string, mixed> $data
607
+	 *
608
+	 * @return static
609
+	 */
610
+	public function onDuplicateKeyUpdate($data)
611
+	{
612
+		$this->addStatement('onduplicate', $data);
613
+
614
+		return $this;
615
+	}
616
+
617
+	/**
618
+	 * @return int number of rows effected
619
+	 */
620
+	public function delete(): int
621
+	{
622
+		$eventResult = $this->fireEvents('before-delete');
623
+		if (!is_null($eventResult)) {
624
+			return $eventResult;
625
+		}
626
+
627
+		$queryObject = $this->getQuery('delete');
628
+
629
+		list($preparedQuery, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings());
630
+		$this->dbInstance()->get_results($preparedQuery);
631
+		$this->fireEvents('after-delete', $queryObject, $executionTime);
632
+
633
+		return $this->dbInstance()->rows_affected;
634
+	}
635
+
636
+	/**
637
+	 * @param string|Raw ...$tables Single table or array of tables
638
+	 *
639
+	 * @return static
640
+	 *
641
+	 * @throws Exception
642
+	 */
643
+	public function table(...$tables): QueryBuilderHandler
644
+	{
645
+		$instance =  $this->constructCurrentBuilderClass($this->connection);
646
+		$this->setFetchMode($this->getFetchMode(), $this->hydratorConstructorArgs);
647
+		$tables = $this->addTablePrefix($tables, false);
648
+		$instance->addStatement('tables', $tables);
649
+
650
+		return $instance;
651
+	}
652
+
653
+	/**
654
+	 * @param string|Raw ...$tables Single table or array of tables
655
+	 *
656
+	 * @return static
657
+	 */
658
+	public function from(...$tables): self
659
+	{
660
+		$tables = $this->addTablePrefix($tables, false);
661
+		$this->addStatement('tables', $tables);
662
+
663
+		return $this;
664
+	}
665
+
666
+	/**
667
+	 * @param string|string[]|Raw[]|array<string, string> $fields
668
+	 *
669
+	 * @return static
670
+	 */
671
+	public function select($fields): self
672
+	{
673
+		if (!is_array($fields)) {
674
+			$fields = func_get_args();
675
+		}
676
+
677
+		$fields = $this->addTablePrefix($fields);
678
+		$this->addStatement('selects', $fields);
679
+
680
+		return $this;
681
+	}
682
+
683
+	/**
684
+	 * @param string|string[]|Raw[]|array<string, string> $fields
685
+	 *
686
+	 * @return static
687
+	 */
688
+	public function selectDistinct($fields)
689
+	{
690
+		$this->select($fields);
691
+		$this->addStatement('distinct', true);
692
+
693
+		return $this;
694
+	}
695
+
696
+	/**
697
+	 * @param string|string[] $field either the single field or an array of fields
698
+	 *
699
+	 * @return static
700
+	 */
701
+	public function groupBy($field): self
702
+	{
703
+		$field = $this->addTablePrefix($field);
704
+		$this->addStatement('groupBys', $field);
705
+
706
+		return $this;
707
+	}
708
+
709
+	/**
710
+	 * @param string|array<string|Raw, mixed> $fields
711
+	 * @param string          $defaultDirection
712
+	 *
713
+	 * @return static
714
+	 */
715
+	public function orderBy($fields, string $defaultDirection = 'ASC'): self
716
+	{
717
+		if (!is_array($fields)) {
718
+			$fields = [$fields];
719
+		}
720
+
721
+		foreach ($fields as $key => $value) {
722
+			$field = $key;
723
+			$type  = $value;
724
+			if (is_int($key)) {
725
+				$field = $value;
726
+				$type  = $defaultDirection;
727
+			}
728
+			if (!$field instanceof Raw) {
729
+				$field = $this->addTablePrefix($field);
730
+			}
731
+			$this->statements['orderBys'][] = compact('field', 'type');
732
+		}
733
+
734
+		return $this;
735
+	}
736
+
737
+	/**
738
+	 * @param int $limit
739
+	 *
740
+	 * @return static
741
+	 */
742
+	public function limit(int $limit): self
743
+	{
744
+		$this->statements['limit'] = $limit;
745
+
746
+		return $this;
747
+	}
748
+
749
+	/**
750
+	 * @param int $offset
751
+	 *
752
+	 * @return static
753
+	 */
754
+	public function offset(int $offset): self
755
+	{
756
+		$this->statements['offset'] = $offset;
757
+
758
+		return $this;
759
+	}
760
+
761
+	/**
762
+	 * @param string|string[]|Raw|Raw[]       $key
763
+	 * @param string $operator
764
+	 * @param mixed $value
765
+	 * @param string $joiner
766
+	 *
767
+	 * @return static
768
+	 */
769
+	public function having($key, string $operator, $value, string $joiner = 'AND')
770
+	{
771
+		$key                           = $this->addTablePrefix($key);
772
+		$this->statements['havings'][] = compact('key', 'operator', 'value', 'joiner');
773
+
774
+		return $this;
775
+	}
776
+
777
+	/**
778
+	 * @param string|string[]|Raw|Raw[]       $key
779
+	 * @param string $operator
780
+	 * @param mixed $value
781
+	 *
782
+	 * @return static
783
+	 */
784
+	public function orHaving($key, $operator, $value)
785
+	{
786
+		return $this->having($key, $operator, $value, 'OR');
787
+	}
788
+
789
+	/**
790
+	 * @param string|Raw $key
791
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
792
+	 * @param mixed|null $value
793
+	 *
794
+	 * @return static
795
+	 */
796
+	public function where($key, $operator = null, $value = null): self
797
+	{
798
+		// If two params are given then assume operator is =
799
+		if (2 == func_num_args()) {
800
+			$value    = $operator;
801
+			$operator = '=';
802
+		}
803
+
804
+		return $this->whereHandler($key, $operator, $value);
805
+	}
806
+
807
+	/**
808
+	 * @param string|Raw $key
809
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
810
+	 * @param mixed|null $value
811
+	 *
812
+	 * @return static
813
+	 */
814
+	public function orWhere($key, $operator = null, $value = null): self
815
+	{
816
+		// If two params are given then assume operator is =
817
+		if (2 == func_num_args()) {
818
+			$value    = $operator;
819
+			$operator = '=';
820
+		}
821
+
822
+		return $this->whereHandler($key, $operator, $value, 'OR');
823
+	}
824
+
825
+	/**
826
+	 * @param string|Raw $key
827
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
828
+	 * @param mixed|null $value
829
+	 *
830
+	 * @return static
831
+	 */
832
+	public function whereNot($key, $operator = null, $value = null): self
833
+	{
834
+		// If two params are given then assume operator is =
835
+		if (2 == func_num_args()) {
836
+			$value    = $operator;
837
+			$operator = '=';
838
+		}
839
+
840
+		return $this->whereHandler($key, $operator, $value, 'AND NOT');
841
+	}
842
+
843
+	/**
844
+	 * @param string|Raw $key
845
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
846
+	 * @param mixed|null $value
847
+	 *
848
+	 * @return static
849
+	 */
850
+	public function orWhereNot($key, $operator = null, $value = null)
851
+	{
852
+		// If two params are given then assume operator is =
853
+		if (2 == func_num_args()) {
854
+			$value    = $operator;
855
+			$operator = '=';
856
+		}
857
+
858
+		return $this->whereHandler($key, $operator, $value, 'OR NOT');
859
+	}
860
+
861
+	/**
862
+	 * @param string|Raw $key
863
+	 * @param mixed[]|string|Raw $values
864
+	 *
865
+	 * @return static
866
+	 */
867
+	public function whereIn($key, $values): self
868
+	{
869
+		return $this->whereHandler($key, 'IN', $values, 'AND');
870
+	}
871
+
872
+	/**
873
+	 * @param string|Raw $key
874
+	 * @param mixed[]|string|Raw $values
875
+	 *
876
+	 * @return static
877
+	 */
878
+	public function whereNotIn($key, $values): self
879
+	{
880
+		return $this->whereHandler($key, 'NOT IN', $values, 'AND');
881
+	}
882
+
883
+	/**
884
+	 * @param string|Raw $key
885
+	 * @param mixed[]|string|Raw $values
886
+	 *
887
+	 * @return static
888
+	 */
889
+	public function orWhereIn($key, $values): self
890
+	{
891
+		return $this->whereHandler($key, 'IN', $values, 'OR');
892
+	}
893
+
894
+	/**
895
+	 * @param string|Raw $key
896
+	 * @param mixed[]|string|Raw $values
897
+	 *
898
+	 * @return static
899
+	 */
900
+	public function orWhereNotIn($key, $values): self
901
+	{
902
+		return $this->whereHandler($key, 'NOT IN', $values, 'OR');
903
+	}
904
+
905
+	/**
906
+	 * @param string|Raw $key
907
+	 * @param mixed $valueFrom
908
+	 * @param mixed $valueTo
909
+	 *
910
+	 * @return static
911
+	 */
912
+	public function whereBetween($key, $valueFrom, $valueTo): self
913
+	{
914
+		return $this->whereHandler($key, 'BETWEEN', [$valueFrom, $valueTo], 'AND');
915
+	}
916
+
917
+	/**
918
+	 * @param string|Raw $key
919
+	 * @param mixed $valueFrom
920
+	 * @param mixed $valueTo
921
+	 *
922
+	 * @return static
923
+	 */
924
+	public function orWhereBetween($key, $valueFrom, $valueTo): self
925
+	{
926
+		return $this->whereHandler($key, 'BETWEEN', [$valueFrom, $valueTo], 'OR');
927
+	}
928
+
929
+	/**
930
+	 * Handles all function call based where conditions
931
+	 *
932
+	 * @param string|Raw $key
933
+	 * @param string $function
934
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
935
+	 * @param mixed|null $value
936
+	 * @return static
937
+	 */
938
+	protected function whereFunctionCallHandler($key, $function, $operator, $value): self
939
+	{
940
+		$key = \sprintf('%s(%s)', $function, $this->addTablePrefix($key));
941
+		return $this->where($key, $operator, $value);
942
+	}
943
+
944
+	/**
945
+	 * @param string|Raw $key
946
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
947
+	 * @param mixed|null $value
948
+	 * @return self
949
+	 */
950
+	public function whereMonth($key, $operator = null, $value = null): self
951
+	{
952
+		// If two params are given then assume operator is =
953
+		if (2 == func_num_args()) {
954
+			$value    = $operator;
955
+			$operator = '=';
956
+		}
957
+		return $this->whereFunctionCallHandler($key, 'MONTH', $operator, $value);
958
+	}
959
+
960
+	/**
961
+	 * @param string|Raw $key
962
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
963
+	 * @param mixed|null $value
964
+	 * @return self
965
+	 */
966
+	public function whereDay($key, $operator = null, $value = null): self
967
+	{
968
+		// If two params are given then assume operator is =
969
+		if (2 == func_num_args()) {
970
+			$value    = $operator;
971
+			$operator = '=';
972
+		}
973
+		return $this->whereFunctionCallHandler($key, 'DAY', $operator, $value);
974
+	}
975
+
976
+	/**
977
+	 * @param string|Raw $key
978
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
979
+	 * @param mixed|null $value
980
+	 * @return self
981
+	 */
982
+	public function whereYear($key, $operator = null, $value = null): self
983
+	{
984
+		// If two params are given then assume operator is =
985
+		if (2 == func_num_args()) {
986
+			$value    = $operator;
987
+			$operator = '=';
988
+		}
989
+		return $this->whereFunctionCallHandler($key, 'YEAR', $operator, $value);
990
+	}
991
+
992
+	/**
993
+	 * @param string|Raw $key
994
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
995
+	 * @param mixed|null $value
996
+	 * @return self
997
+	 */
998
+	public function whereDate($key, $operator = null, $value = null): self
999
+	{
1000
+		// If two params are given then assume operator is =
1001
+		if (2 == func_num_args()) {
1002
+			$value    = $operator;
1003
+			$operator = '=';
1004
+		}
1005
+		return $this->whereFunctionCallHandler($key, 'DATE', $operator, $value);
1006
+	}
1007
+
1008
+	/**
1009
+	 * @param string|Raw $key
1010
+	 *
1011
+	 * @return static
1012
+	 */
1013
+	public function whereNull($key): self
1014
+	{
1015
+		return $this->whereNullHandler($key);
1016
+	}
1017
+
1018
+	/**
1019
+	 * @param string|Raw $key
1020
+	 *
1021
+	 * @return static
1022
+	 */
1023
+	public function whereNotNull($key): self
1024
+	{
1025
+		return $this->whereNullHandler($key, 'NOT');
1026
+	}
1027
+
1028
+	/**
1029
+	 * @param string|Raw $key
1030
+	 *
1031
+	 * @return static
1032
+	 */
1033
+	public function orWhereNull($key): self
1034
+	{
1035
+		return $this->whereNullHandler($key, '', 'or');
1036
+	}
1037
+
1038
+	/**
1039
+	 * @param string|Raw $key
1040
+	 *
1041
+	 * @return static
1042
+	 */
1043
+	public function orWhereNotNull($key): self
1044
+	{
1045
+		return $this->whereNullHandler($key, 'NOT', 'or');
1046
+	}
1047
+
1048
+	/**
1049
+	 * @param string|Raw $key
1050
+	 * @param string $prefix
1051
+	 * @param string $operator
1052
+	 *
1053
+	 * @return static
1054
+	 */
1055
+	protected function whereNullHandler($key, string $prefix = '', $operator = ''): self
1056
+	{
1057
+		$prefix = 0 === mb_strlen($prefix) ? '' : " {$prefix}";
1058
+
1059
+		if ($key instanceof Raw) {
1060
+			$key = $this->adapterInstance->parseRaw($key);
1061
+		}
1062
+
1063
+		$key = $this->adapterInstance->wrapSanitizer($this->addTablePrefix($key));
1064
+		if ($key instanceof Closure) {
1065
+			throw new Exception('Key used for whereNull condition must be a string or raw exrpession.', 1);
1066
+		}
1067
+
1068
+		return $this->{$operator . 'Where'}($this->raw("{$key} IS{$prefix} NULL"));
1069
+	}
1070
+
1071
+	/**
1072
+	 * @param string|Raw $table
1073
+	 * @param string|Raw|Closure $key
1074
+	 * @param string|null $operator
1075
+	 * @param mixed $value
1076
+	 * @param string $type
1077
+	 *
1078
+	 * @return static
1079
+	 */
1080
+	public function join($table, $key, ?string $operator = null, $value = null, $type = 'inner')
1081
+	{
1082
+		if (!$key instanceof Closure) {
1083
+			$key = function ($joinBuilder) use ($key, $operator, $value) {
1084
+				$joinBuilder->on($key, $operator, $value);
1085
+			};
1086
+		}
1087
+
1088
+		// Build a new JoinBuilder class, keep it by reference so any changes made
1089
+		// in the closure should reflect here
1090
+		$joinBuilder = $this->container->build(JoinBuilder::class, [$this->connection]);
1091
+		$joinBuilder = &$joinBuilder;
1092
+		// Call the closure with our new joinBuilder object
1093
+		$key($joinBuilder);
1094
+		$table = $this->addTablePrefix($table, false);
1095
+		// Get the criteria only query from the joinBuilder object
1096
+		$this->statements['joins'][] = compact('type', 'table', 'joinBuilder');
1097
+		return $this;
1098
+	}
1099
+
1100
+	/**
1101
+	 * Runs a transaction
1102
+	 *
1103
+	 * @param \Closure(Transaction):void $callback
1104
+	 *
1105
+	 * @return static
1106
+	 */
1107
+	public function transaction(Closure $callback): self
1108
+	{
1109
+		try {
1110
+			// Begin the transaction
1111
+			$this->dbInstance->query('START TRANSACTION');
1112
+
1113
+			// Get the Transaction class
1114
+			$transaction = $this->container->build(Transaction::class, [$this->connection]);
1115
+
1116
+			$this->handleTransactionCall($callback, $transaction);
1117
+
1118
+			// If no errors have been thrown or the transaction wasn't completed within
1119
+			$this->dbInstance->query('COMMIT');
1120
+
1121
+			return $this;
1122
+		} catch (TransactionHaltException $e) {
1123
+			// Commit or rollback behavior has been handled in the closure, so exit
1124
+			return $this;
1125
+		} catch (\Exception $e) {
1126
+			// something happened, rollback changes
1127
+			$this->dbInstance->query('ROLLBACK');
1128
+
1129
+			return $this;
1130
+		}
1131
+	}
1132
+
1133
+	/**
1134
+	 * Handles the transaction call.
1135
+	 *
1136
+	 * Catches any WP Errors (printed)
1137
+	 *
1138
+	 * @param Closure    $callback
1139
+	 * @param Transaction $transaction
1140
+	 *
1141
+	 * @return void
1142
+	 *
1143
+	 * @throws Exception
1144
+	 */
1145
+	protected function handleTransactionCall(Closure $callback, Transaction $transaction): void
1146
+	{
1147
+		try {
1148
+			ob_start();
1149
+			$callback($transaction);
1150
+			$output = ob_get_clean() ?: '';
1151
+		} catch (Throwable $th) {
1152
+			ob_end_clean();
1153
+			throw $th;
1154
+		}
1155
+
1156
+		// If we caught an error, throw an exception.
1157
+		if (0 !== mb_strlen($output)) {
1158
+			throw new Exception($output);
1159
+		}
1160
+	}
1161
+
1162
+	/**
1163
+	 * @param string|Raw $table
1164
+	 * @param string|Raw|Closure $key
1165
+	 * @param string|null $operator
1166
+	 * @param mixed $value
1167
+	 *
1168
+	 * @return static
1169
+	 */
1170
+	public function leftJoin($table, $key, $operator = null, $value = null)
1171
+	{
1172
+		return $this->join($table, $key, $operator, $value, 'left');
1173
+	}
1174
+
1175
+	/**
1176
+	 * @param string|Raw $table
1177
+	 * @param string|Raw|Closure $key
1178
+	 * @param string|null $operator
1179
+	 * @param mixed $value
1180
+	 *
1181
+	 * @return static
1182
+	 */
1183
+	public function rightJoin($table, $key, $operator = null, $value = null)
1184
+	{
1185
+		return $this->join($table, $key, $operator, $value, 'right');
1186
+	}
1187
+
1188
+	/**
1189
+	 * @param string|Raw $table
1190
+	 * @param string|Raw|Closure $key
1191
+	 * @param string|null $operator
1192
+	 * @param mixed $value
1193
+	 *
1194
+	 * @return static
1195
+	 */
1196
+	public function innerJoin($table, $key, $operator = null, $value = null)
1197
+	{
1198
+		return $this->join($table, $key, $operator, $value, 'inner');
1199
+	}
1200
+
1201
+	/**
1202
+	 * @param string|Raw $table
1203
+	 * @param string|Raw|Closure $key
1204
+	 * @param string|null $operator
1205
+	 * @param mixed $value
1206
+	 *
1207
+	 * @return static
1208
+	 */
1209
+	public function crossJoin($table, $key, $operator = null, $value = null)
1210
+	{
1211
+		return $this->join($table, $key, $operator, $value, 'cross');
1212
+	}
1213
+
1214
+	/**
1215
+	 * @param string|Raw $table
1216
+	 * @param string|Raw|Closure $key
1217
+	 * @param string|null $operator
1218
+	 * @param mixed $value
1219
+	 *
1220
+	 * @return static
1221
+	 */
1222
+	public function outerJoin($table, $key, $operator = null, $value = null)
1223
+	{
1224
+		return $this->join($table, $key, $operator, $value, 'outer');
1225
+	}
1226
+
1227
+	/**
1228
+	 * Shortcut to join 2 tables on the same key name with equals
1229
+	 *
1230
+	 * @param string $table
1231
+	 * @param string $key
1232
+	 * @param string $type
1233
+	 * @return self
1234
+	 * @throws Exception If base table is set as more than 1 or 0
1235
+	 */
1236
+	public function joinUsing(string $table, string $key, string $type = 'INNER'): self
1237
+	{
1238
+		if (!array_key_exists('tables', $this->statements) || count($this->statements['tables']) !== 1) {
1239
+			throw new Exception("JoinUsing can only be used with a single table set as the base of the query", 1);
1240
+		}
1241
+		$baseTable = end($this->statements['tables']);
1242
+
1243
+		$remoteKey = $table = $this->addTablePrefix("{$table}.{$key}", true);
1244
+		$localKey = $table = $this->addTablePrefix("{$baseTable}.{$key}", true);
1245
+		return $this->join($table, $remoteKey, '=', $localKey, $type);
1246
+	}
1247
+
1248
+	/**
1249
+	 * Add a raw query
1250
+	 *
1251
+	 * @param string|Raw $value
1252
+	 * @param mixed|mixed[] $bindings
1253
+	 *
1254
+	 * @return Raw
1255
+	 */
1256
+	public function raw($value, $bindings = []): Raw
1257
+	{
1258
+		return new Raw($value, $bindings);
1259
+	}
1260
+
1261
+	/**
1262
+	 * Return wpdb instance
1263
+	 *
1264
+	 * @return wpdb
1265
+	 */
1266
+	public function dbInstance(): wpdb
1267
+	{
1268
+		return $this->dbInstance;
1269
+	}
1270
+
1271
+	/**
1272
+	 * @param Connection $connection
1273
+	 *
1274
+	 * @return static
1275
+	 */
1276
+	public function setConnection(Connection $connection): self
1277
+	{
1278
+		$this->connection = $connection;
1279
+
1280
+		return $this;
1281
+	}
1282
+
1283
+	/**
1284
+	 * @return Connection
1285
+	 */
1286
+	public function getConnection()
1287
+	{
1288
+		return $this->connection;
1289
+	}
1290
+
1291
+	/**
1292
+	 * @param string|Raw|Closure $key
1293
+	 * @param string|null      $operator
1294
+	 * @param mixed|null       $value
1295
+	 * @param string $joiner
1296
+	 *
1297
+	 * @return static
1298
+	 */
1299
+	protected function whereHandler($key, $operator = null, $value = null, $joiner = 'AND')
1300
+	{
1301
+		$key                          = $this->addTablePrefix($key);
1302
+		$this->statements['wheres'][] = compact('key', 'operator', 'value', 'joiner');
1303
+		return $this;
1304
+	}
1305
+
1306
+	/**
1307
+	 * Add table prefix (if given) on given string.
1308
+	 *
1309
+	 * @param array<string|int, string|int|float|bool|Raw|Closure>|string|int|float|bool|Raw|Closure     $values
1310
+	 * @param bool $tableFieldMix If we have mixes of field and table names with a "."
1311
+	 *
1312
+	 * @return mixed|mixed[]
1313
+	 */
1314
+	public function addTablePrefix($values, bool $tableFieldMix = true)
1315
+	{
1316
+		if (is_null($this->tablePrefix)) {
1317
+			return $values;
1318
+		}
1319
+
1320
+		// $value will be an array and we will add prefix to all table names
1321
+
1322
+		// If supplied value is not an array then make it one
1323
+		$single = false;
1324
+		if (!is_array($values)) {
1325
+			$values = [$values];
1326
+			// We had single value, so should return a single value
1327
+			$single = true;
1328
+		}
1329
+
1330
+		$return = [];
1331
+
1332
+		foreach ($values as $key => $value) {
1333
+			// It's a raw query, just add it to our return array and continue next
1334
+			if ($value instanceof Raw || $value instanceof Closure) {
1335
+				$return[$key] = $value;
1336
+				continue;
1337
+			}
1338
+
1339
+			// If key is not integer, it is likely a alias mapping,
1340
+			// so we need to change prefix target
1341
+			$target = &$value;
1342
+			if (!is_int($key)) {
1343
+				$target = &$key;
1344
+			}
1345
+
1346
+			if (!$tableFieldMix || (is_string($target) && false !== strpos($target, '.'))) {
1347
+				$target = $this->tablePrefix . $target;
1348
+			}
1349
+
1350
+			$return[$key] = $value;
1351
+		}
1352
+
1353
+		// If we had single value then we should return a single value (end value of the array)
1354
+		return true === $single ? end($return) : $return;
1355
+	}
1356
+
1357
+	/**
1358
+	 * @param string $key
1359
+	 * @param mixed|mixed[]|bool $value
1360
+	 *
1361
+	 * @return void
1362
+	 */
1363
+	protected function addStatement($key, $value)
1364
+	{
1365
+		if (!is_array($value)) {
1366
+			$value = [$value];
1367
+		}
1368
+
1369
+		if (!array_key_exists($key, $this->statements)) {
1370
+			$this->statements[$key] = $value;
1371
+		} else {
1372
+			$this->statements[$key] = array_merge($this->statements[$key], $value);
1373
+		}
1374
+	}
1375
+
1376
+	/**
1377
+	 * @param string $event
1378
+	 * @param string|Raw $table
1379
+	 *
1380
+	 * @return callable|null
1381
+	 */
1382
+	public function getEvent(string $event, $table = ':any'): ?callable
1383
+	{
1384
+		return $this->connection->getEventHandler()->getEvent($event, $table);
1385
+	}
1386
+
1387
+	/**
1388
+	 * @param string $event
1389
+	 * @param string|Raw $table
1390
+	 * @param Closure $action
1391
+	 *
1392
+	 * @return void
1393
+	 */
1394
+	public function registerEvent($event, $table, Closure $action): void
1395
+	{
1396
+		$table = $table ?: ':any';
1397
+
1398
+		if (':any' != $table) {
1399
+			$table = $this->addTablePrefix($table, false);
1400
+		}
1401
+
1402
+		$this->connection->getEventHandler()->registerEvent($event, $table, $action);
1403
+	}
1404
+
1405
+	/**
1406
+	 * @param string $event
1407
+	 * @param string|Raw $table
1408
+	 *
1409
+	 * @return void
1410
+	 */
1411
+	public function removeEvent(string $event, $table = ':any')
1412
+	{
1413
+		if (':any' != $table) {
1414
+			$table = $this->addTablePrefix($table, false);
1415
+		}
1416
+
1417
+		$this->connection->getEventHandler()->removeEvent($event, $table);
1418
+	}
1419
+
1420
+	/**
1421
+	 * @param string $event
1422
+	 *
1423
+	 * @return mixed
1424
+	 */
1425
+	public function fireEvents(string $event)
1426
+	{
1427
+		$params = func_get_args(); // @todo Replace this with an easier to read alteratnive
1428
+		array_unshift($params, $this);
1429
+
1430
+		return call_user_func_array([$this->connection->getEventHandler(), 'fireEvents'], $params);
1431
+	}
1432
+
1433
+	/**
1434
+	 * @return array<string, mixed[]>
1435
+	 */
1436
+	public function getStatements()
1437
+	{
1438
+		return $this->statements;
1439
+	}
1440
+
1441
+	/**
1442
+	 * @return string will return WPDB Fetch mode
1443
+	 */
1444
+	public function getFetchMode()
1445
+	{
1446
+		return null !== $this->fetchMode
1447
+			? $this->fetchMode
1448
+			: \OBJECT;
1449
+	}
1450 1450
 }
Please login to merge, or discard this patch.
Spacing   +28 added lines, -28 removed lines patch added patch discarded remove patch
@@ -201,7 +201,7 @@  discard block
 block discarded – undo
201 201
         $start        = microtime(true);
202 202
         $sqlStatement = empty($bindings) ? $sql : $this->interpolateQuery($sql, $bindings);
203 203
 
204
-        if (!is_string($sqlStatement)) {
204
+        if ( ! is_string($sqlStatement)) {
205 205
             throw new Exception('Could not interpolate query', 1);
206 206
         }
207 207
 
@@ -218,7 +218,7 @@  discard block
 block discarded – undo
218 218
     public function get()
219 219
     {
220 220
         $eventResult = $this->fireEvents('before-select');
221
-        if (!is_null($eventResult)) {
221
+        if ( ! is_null($eventResult)) {
222 222
             return $eventResult;
223 223
         }
224 224
         $executionTime = 0;
@@ -243,7 +243,7 @@  discard block
 block discarded – undo
243 243
         $this->sqlStatement = null;
244 244
 
245 245
         // Ensure we have an array of results.
246
-        if (!is_array($result) && null !== $result) {
246
+        if ( ! is_array($result) && null !== $result) {
247 247
             $result = [$result];
248 248
         }
249 249
 
@@ -276,7 +276,7 @@  discard block
 block discarded – undo
276 276
      */
277 277
     protected function useHydrator(): bool
278 278
     {
279
-        return !in_array($this->getFetchMode(), [\ARRAY_A, \ARRAY_N, \OBJECT, \OBJECT_K]);
279
+        return ! in_array($this->getFetchMode(), [\ARRAY_A, \ARRAY_N, \OBJECT, \OBJECT_K]);
280 280
     }
281 281
 
282 282
     /**
@@ -366,7 +366,7 @@  discard block
 block discarded – undo
366 366
             ->select([$this->raw(sprintf('%s(%s) AS field', strtoupper($type), $field))])
367 367
             ->first();
368 368
 
369
-        return true === isset($count->field) ? (float)$count->field : 0;
369
+        return true === isset($count->field) ? (float) $count->field : 0;
370 370
     }
371 371
 
372 372
     /**
@@ -382,7 +382,7 @@  discard block
 block discarded – undo
382 382
      */
383 383
     public function count(string $field = '*'): int
384 384
     {
385
-        return (int)$this->aggregate('count', $field);
385
+        return (int) $this->aggregate('count', $field);
386 386
     }
387 387
 
388 388
     /**
@@ -460,8 +460,8 @@  discard block
 block discarded – undo
460 460
     public function getQuery(string $type = 'select', $dataToBePassed = [])
461 461
     {
462 462
         $allowedTypes = ['select', 'insert', 'insertignore', 'replace', 'delete', 'update', 'criteriaonly'];
463
-        if (!in_array(strtolower($type), $allowedTypes)) {
464
-            throw new Exception($type . ' is not a known type.', 2);
463
+        if ( ! in_array(strtolower($type), $allowedTypes)) {
464
+            throw new Exception($type.' is not a known type.', 2);
465 465
         }
466 466
 
467 467
         $queryArr = $this->adapterInstance->$type($this->statements, $dataToBePassed);
@@ -480,9 +480,9 @@  discard block
 block discarded – undo
480 480
      */
481 481
     public function subQuery(QueryBuilderHandler $queryBuilder, ?string $alias = null)
482 482
     {
483
-        $sql = '(' . $queryBuilder->getQuery()->getRawSql() . ')';
483
+        $sql = '('.$queryBuilder->getQuery()->getRawSql().')';
484 484
         if (is_string($alias) && 0 !== mb_strlen($alias)) {
485
-            $sql = $sql . ' as ' . $alias;
485
+            $sql = $sql.' as '.$alias;
486 486
         }
487 487
 
488 488
         return $queryBuilder->raw($sql);
@@ -499,12 +499,12 @@  discard block
 block discarded – undo
499 499
     private function doInsert(array $data, string $type)
500 500
     {
501 501
         $eventResult = $this->fireEvents('before-insert');
502
-        if (!is_null($eventResult)) {
502
+        if ( ! is_null($eventResult)) {
503 503
             return $eventResult;
504 504
         }
505 505
 
506 506
         // If first value is not an array () not a batch insert)
507
-        if (!is_array(current($data))) {
507
+        if ( ! is_array(current($data))) {
508 508
             $queryObject = $this->getQuery($type, $data);
509 509
 
510 510
             list($preparedQuery, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings());
@@ -574,7 +574,7 @@  discard block
 block discarded – undo
574 574
     public function update($data)
575 575
     {
576 576
         $eventResult = $this->fireEvents('before-update');
577
-        if (!is_null($eventResult)) {
577
+        if ( ! is_null($eventResult)) {
578 578
             return $eventResult;
579 579
         }
580 580
         $queryObject                         = $this->getQuery('update', $data);
@@ -620,7 +620,7 @@  discard block
 block discarded – undo
620 620
     public function delete(): int
621 621
     {
622 622
         $eventResult = $this->fireEvents('before-delete');
623
-        if (!is_null($eventResult)) {
623
+        if ( ! is_null($eventResult)) {
624 624
             return $eventResult;
625 625
         }
626 626
 
@@ -642,7 +642,7 @@  discard block
 block discarded – undo
642 642
      */
643 643
     public function table(...$tables): QueryBuilderHandler
644 644
     {
645
-        $instance =  $this->constructCurrentBuilderClass($this->connection);
645
+        $instance = $this->constructCurrentBuilderClass($this->connection);
646 646
         $this->setFetchMode($this->getFetchMode(), $this->hydratorConstructorArgs);
647 647
         $tables = $this->addTablePrefix($tables, false);
648 648
         $instance->addStatement('tables', $tables);
@@ -670,7 +670,7 @@  discard block
 block discarded – undo
670 670
      */
671 671
     public function select($fields): self
672 672
     {
673
-        if (!is_array($fields)) {
673
+        if ( ! is_array($fields)) {
674 674
             $fields = func_get_args();
675 675
         }
676 676
 
@@ -714,7 +714,7 @@  discard block
 block discarded – undo
714 714
      */
715 715
     public function orderBy($fields, string $defaultDirection = 'ASC'): self
716 716
     {
717
-        if (!is_array($fields)) {
717
+        if ( ! is_array($fields)) {
718 718
             $fields = [$fields];
719 719
         }
720 720
 
@@ -725,7 +725,7 @@  discard block
 block discarded – undo
725 725
                 $field = $value;
726 726
                 $type  = $defaultDirection;
727 727
             }
728
-            if (!$field instanceof Raw) {
728
+            if ( ! $field instanceof Raw) {
729 729
                 $field = $this->addTablePrefix($field);
730 730
             }
731 731
             $this->statements['orderBys'][] = compact('field', 'type');
@@ -1065,7 +1065,7 @@  discard block
 block discarded – undo
1065 1065
             throw new Exception('Key used for whereNull condition must be a string or raw exrpession.', 1);
1066 1066
         }
1067 1067
 
1068
-        return $this->{$operator . 'Where'}($this->raw("{$key} IS{$prefix} NULL"));
1068
+        return $this->{$operator.'Where'}($this->raw("{$key} IS{$prefix} NULL"));
1069 1069
     }
1070 1070
 
1071 1071
     /**
@@ -1079,8 +1079,8 @@  discard block
 block discarded – undo
1079 1079
      */
1080 1080
     public function join($table, $key, ?string $operator = null, $value = null, $type = 'inner')
1081 1081
     {
1082
-        if (!$key instanceof Closure) {
1083
-            $key = function ($joinBuilder) use ($key, $operator, $value) {
1082
+        if ( ! $key instanceof Closure) {
1083
+            $key = function($joinBuilder) use ($key, $operator, $value) {
1084 1084
                 $joinBuilder->on($key, $operator, $value);
1085 1085
             };
1086 1086
         }
@@ -1235,7 +1235,7 @@  discard block
 block discarded – undo
1235 1235
      */
1236 1236
     public function joinUsing(string $table, string $key, string $type = 'INNER'): self
1237 1237
     {
1238
-        if (!array_key_exists('tables', $this->statements) || count($this->statements['tables']) !== 1) {
1238
+        if ( ! array_key_exists('tables', $this->statements) || count($this->statements['tables']) !== 1) {
1239 1239
             throw new Exception("JoinUsing can only be used with a single table set as the base of the query", 1);
1240 1240
         }
1241 1241
         $baseTable = end($this->statements['tables']);
@@ -1321,7 +1321,7 @@  discard block
 block discarded – undo
1321 1321
 
1322 1322
         // If supplied value is not an array then make it one
1323 1323
         $single = false;
1324
-        if (!is_array($values)) {
1324
+        if ( ! is_array($values)) {
1325 1325
             $values = [$values];
1326 1326
             // We had single value, so should return a single value
1327 1327
             $single = true;
@@ -1339,12 +1339,12 @@  discard block
 block discarded – undo
1339 1339
             // If key is not integer, it is likely a alias mapping,
1340 1340
             // so we need to change prefix target
1341 1341
             $target = &$value;
1342
-            if (!is_int($key)) {
1342
+            if ( ! is_int($key)) {
1343 1343
                 $target = &$key;
1344 1344
             }
1345 1345
 
1346
-            if (!$tableFieldMix || (is_string($target) && false !== strpos($target, '.'))) {
1347
-                $target = $this->tablePrefix . $target;
1346
+            if ( ! $tableFieldMix || (is_string($target) && false !== strpos($target, '.'))) {
1347
+                $target = $this->tablePrefix.$target;
1348 1348
             }
1349 1349
 
1350 1350
             $return[$key] = $value;
@@ -1362,11 +1362,11 @@  discard block
 block discarded – undo
1362 1362
      */
1363 1363
     protected function addStatement($key, $value)
1364 1364
     {
1365
-        if (!is_array($value)) {
1365
+        if ( ! is_array($value)) {
1366 1366
             $value = [$value];
1367 1367
         }
1368 1368
 
1369
-        if (!array_key_exists($key, $this->statements)) {
1369
+        if ( ! array_key_exists($key, $this->statements)) {
1370 1370
             $this->statements[$key] = $value;
1371 1371
         } else {
1372 1372
             $this->statements[$key] = array_merge($this->statements[$key], $value);
Please login to merge, or discard this patch.
src/QueryBuilder/Transaction.php 1 patch
Indentation   +20 added lines, -20 removed lines patch added patch discarded remove patch
@@ -4,25 +4,25 @@
 block discarded – undo
4 4
 
5 5
 class Transaction extends QueryBuilderHandler
6 6
 {
7
-    /**
8
-     * Commit the database changes
9
-     *
10
-     * @throws TransactionHaltException
11
-     */
12
-    public function commit(): void
13
-    {
14
-        $this->dbInstance->query('COMMIT');
15
-        throw new TransactionHaltException();
16
-    }
7
+	/**
8
+	 * Commit the database changes
9
+	 *
10
+	 * @throws TransactionHaltException
11
+	 */
12
+	public function commit(): void
13
+	{
14
+		$this->dbInstance->query('COMMIT');
15
+		throw new TransactionHaltException();
16
+	}
17 17
 
18
-    /**
19
-     * Rollback the database changes
20
-     *
21
-     * @throws TransactionHaltException
22
-     */
23
-    public function rollback(): void
24
-    {
25
-        $this->dbInstance->query('ROLLBACK');
26
-        throw new TransactionHaltException();
27
-    }
18
+	/**
19
+	 * Rollback the database changes
20
+	 *
21
+	 * @throws TransactionHaltException
22
+	 */
23
+	public function rollback(): void
24
+	{
25
+		$this->dbInstance->query('ROLLBACK');
26
+		throw new TransactionHaltException();
27
+	}
28 28
 }
Please login to merge, or discard this patch.