Passed
Branch v1.6.0 (6d31de)
by Wanderson
02:23
created

Orm::join()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Win\Repositories\Database;
4
5
use Win\Application;
6
use Win\Common\Pagination;
7
use Win\Models\Model;
8
use Win\HttpException;
9
10
/**
11
 * Object Relational Mapping
12
 */
13
abstract class Orm
14
{
15
	const TABLE = '';
16
	const PK = 'id';
17
18
	public ?Connection $conn;
19
	public Pagination $pagination;
20
	protected Sql $sql;
21
22
	/**
23
	 * @param Model $model
24
	 * @return mixed[]
25
	 */
26
	abstract public static function mapRow($model);
27
28
	abstract public static function mapModel($row);
29
30
	public function __construct(Pagination $pagination)
31
	{
32
		$this->conn = Application::app()->conn;
33
		$this->sql = new Sql(static::TABLE);
34
		$this->pagination = $pagination;
35
	}
36
37
	/**
38
	 * Executa SQL
39
	 * @param string $query
40
	 * @param mixed[]
41
	 */
42
	protected function execute($query, ...$values)
43
	{
44
		return $this->conn->execute($query, $values);
0 ignored issues
show
Bug introduced by
The method execute() does not exist on null. ( Ignorable by Annotation )

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

44
		return $this->conn->/** @scrutinizer ignore-call */ execute($query, $values);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
45
	}
46
47
	/**
48
	 * Retorna o primeiro resultado da busca
49
	 * @return Model
50
	 */
51
	public function one()
52
	{
53
		$this->sql->setLimit(0, 1);
54
55
		$query = $this->sql->select();
56
		$values = $this->sql->values();
57
		$row = $this->conn->fetch($query, $values);
58
		$this->flush();
59
60
		if ($row !== false) {
61
			return $this->mapModel($row);
62
		}
63
	}
64
65
	/**
66
	 * Retorna o primeiro resultado da busca ou redireciona para Página 404
67
	 */
68
	public function oneOr404()
69
	{
70
		$model = $this->one();
71
		if (is_null($model)) {
72
			throw new HttpException('Model not found', 404);
73
		}
74
75
		return $model;
76
	}
77
78
	/**
79
	 * Retorna o registro pela PK
80
	 * @param int $id
81
	 */
82
	public function find($id)
83
	{
84
		return $this->filter(static::PK, $id)->one();
85
	}
86
87
	/**
88
	 * Retorna o registro pela PK ou redireciona para Página 404
89
	 * @param int $id
90
	 */
91
	public function findOr404($id)
92
	{
93
		return $this->filter(static::PK, $id)->oneOr404();
94
	}
95
96
	/**
97
	 * Retorna o todos os resultados da busca
98
	 */
99
	public function list()
100
	{
101
		$values = $this->sql->values();
102
		$pagination = $this->pagination;
103
104
		if ($pagination->pageSize) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pagination->pageSize of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
105
			$query = $this->sql->selectCount();
106
			$pagination->count = $this->conn->fetchCount($query, $values);
107
			$this->sql->setLimit($pagination->offset(), $pagination->pageSize);
108
		}
109
110
		$query = $this->sql->select();
111
		$rows = $this->conn->fetchAll($query, $values);
112
		$this->flush();
113
114
		return array_map([$this, 'mapModel'], $rows);
115
	}
116
117
	/**
118
	 * Retorna o total de resultados da busca
119
	 * @return int
120
	 */
121
	public function count()
122
	{
123
		$query = $this->sql->selectCount();
124
		$values = $this->sql->values();
125
		$count = $this->conn->fetchCount($query, $values);
126
		$this->flush();
127
128
		return $count;
129
	}
130
131
	/**
132
	 * Define um limite das buscas com list()
133
	 * @param int $pageSize
134
	 * @param int $currentPage
135
	 */
136
	public function paginate($pageSize, $currentPage = 1)
137
	{
138
		$this->pagination->pageSize = $pageSize;
139
		$this->pagination->current = max($currentPage, 1);
140
		return $this;
141
	}
142
143
	/**
144
	 * Remove todos os registros da busca
145
	 */
146
	public function delete()
147
	{
148
		$query = $this->sql->delete();
149
		$values = $this->sql->values();
150
		$this->conn->execute($query, $values);
151
		$this->flush();
152
	}
153
154
	/**
155
	 * Remove o registro pela PK
156
	 * @param int $id
157
	 */
158
	public function destroy($id)
159
	{
160
		$this->filter(static::PK, $id);
161
162
		$query = $this->sql->delete();
163
		$values = $this->sql->values();
164
		$this->conn->execute($query, $values);
165
		$this->flush();
166
	}
167
168
	/**
169
	 * Salva o registro no banco
170
	 * @param Model $model
171
	 */
172
	public function save(Model $model)
173
	{
174
		$model->validate();
175
		$this->sql->setValues($this->mapRow($model));
176
177
		if ($this->exists($model->id)) {
178
			$this->filter(static::PK, $model->id);
179
			$query = $this->sql->update();
180
		} else {
181
			$query = $this->sql->insert();
182
		}
183
		$values = array_values($this->sql->values());
184
		$this->conn->execute($query, $values);
185
		$model->id = $model->id ?? $this->conn->lastInsertId();
186
		$this->flush();
187
188
		return $model;
189
	}
190
191
	/**
192
	 * Atualiza somente as colunas informadas
193
	 * @param mixed[] $columns
194
	 * @return int
195
	 */
196
	public function update($columns)
197
	{
198
		$this->sql->setValues($columns);
199
		$query = $this->sql->update();
200
		$values = array_values($this->sql->values());
201
		$stmt = $this->conn->stmt($query, $values);
202
		$this->flush();
203
204
		return $stmt->rowCount();
205
	}
206
207
	/**
208
	 * Remove todos os filtros e ordenação
209
	 */
210
	public function flush()
211
	{
212
		$this->sql->__construct(static::TABLE);
213
214
		return $this;
215
	}
216
217
	/**
218
	 * Retorna TRUE se o model existir no banco
219
	 * @param int $id
220
	 * @return bool
221
	 */
222
	protected function exists($id)
223
	{
224
		$orm = new static($this->pagination);
225
		$orm->conn = $this->conn;
226
		return $orm->filter(static::PK, $id)->count() > 0;
227
	}
228
229
	/**
230
	 * Define as colunas da busca
231
	 * @param string $columns
232
	 */
233
	public function select(...$columns)
234
	{
235
		$this->sql->columns = $columns;
236
237
		return $this;
238
	}
239
240
	/**
241
	 * Adiciona (inner) join
242
	 * @param string $query
243
	 */
244
	public function join($query)
245
	{
246
		$this->sql->addJoin('JOIN ' . $query);
247
248
		return $this;
249
	}
250
251
	/**
252
	 * Adiciona left join
253
	 * @param string $query
254
	 */
255
	public function leftJoin($query)
256
	{
257
		$this->sql->addJoin('LEFT JOIN ' . $query);
258
259
		return $this;
260
	}
261
262
	/**
263
	 * Adiciona right join
264
	 * @param string $query
265
	 */
266
	public function rightJoin($query)
267
	{
268
		$this->sql->addJoin('RIGHT JOIN ' . $query);
269
270
		return $this;
271
	}
272
273
	/**
274
	 * Aplica filtros
275
	 * @param string $comparator
276
	 * @param mixed $values
277
	 * @example filter('name', 'John')
278
	 * @example filter('name = ? OR email = ?', 'John', 'email')
279
	 */
280
	public function filter($comparator, ...$values)
281
	{
282
		$this->sql->addWhere($comparator, $values);
283
284
		return $this;
285
	}
286
287
	/**
288
	 * Aplica filtros (aceita bind params)
289
	 * @param string $query
290
	 * @param mixed[] $values
291
	 * @example filterBy('name = :name OR age > 0', [':name' => 'John'])
292
	 */
293
	public function filterBy($query, $values = [])
294
	{
295
		$this->sql->addWhere($query, $values);
296
297
		return $this;
298
	}
299
300
	/**
301
	 * Ordem por um campo
302
	 * @param string $rule
303
	 * @param int $priority
304
	 */
305
	public function sort($rule, $priority = 0)
306
	{
307
		$this->sql->addOrderBy($rule, $priority);
308
309
		return $this;
310
	}
311
312
	/**
313
	 * Exibe a query e os valores para debug
314
	 * @param string $method
315
	 * @return array<string,mixed>
316
	 */
317
	public function debug($method = 'select')
318
	{
319
		return [$this->sql->$method(), $this->sql->values()];
320
	}
321
}
322