Passed
Branch v1.8.0 (144fd0)
by Wanderson
09:03 queued 06:00
created

Repository::select()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Win\Repositories;
4
5
use PDO;
6
use PDOException;
7
use Win\Application;
8
use Win\Common\Pagination;
9
use Win\Common\InjectableTrait;
10
use Win\Models\Model;
11
use Win\HttpException;
12
13
/**
14
 * Base Database Repository
15
 * 
16
 * @method Model[] list()
17
 * @method Model one()
18
 * @method Model oneOr404()
19
 * @method Model find($id)
20
 * @method Model findOr404($id)
21
 */
22
abstract class Repository
23
{
24
	use InjectableTrait;
25
	public ?PDO $pdo;
26
	public Pagination $pagination;
27
28
	protected $table = '';
29
	protected $pk = 'id';
30
	protected Sql $sql;
31
32
	/**
33
	 * @param Model $model
34
	 * @return mixed[]
35
	 */
36
	abstract public static function mapRow($model);
37
38
	abstract public static function mapModel($row);
39
40
	public function __construct()
41
	{
42
		$app = Application::app();
43
		if (!$app->pdo) {
44
			$app->pdo = require BASE_PATH . '/config/database.php';
45
		}
46
		$this->pdo = $app->pdo;
47
		$this->sql = new Sql($this->table);
48
		$this->pagination = new Pagination();
49
	}
50
51
	/**
52
	 * Define a tabela manualmente
53
	 * @param string $table
54
	 */
55
	public function setTable($table)
56
	{
57
		$this->table = $table;
58
		return $this;
59
	}
60
61
	public function getTable()
62
	{
63
		return $this->table;
64
	}
65
66
	/**
67
	 * Define as colunas da busca
68
	 * @param string $columns
69
	 */
70
	public function select($columns)
71
	{
72
		$this->sql->columns[] = $columns;
73
74
		return $this;
75
	}
76
77
	/**
78
	 * Retorna o primeiro resultado da busca
79
	 * @return Model
80
	 */
81
	public function one()
82
	{
83
		try {
84
			$this->sql->setLimit(0, 1);
85
			$query = $this->sql->select();
86
			$values = $this->sql->values();
87
			$this->flush();
88
89
			$stmt = $this->pdo->prepare($query);
0 ignored issues
show
Bug introduced by
The method prepare() 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

89
			/** @scrutinizer ignore-call */ 
90
   $stmt = $this->pdo->prepare($query);

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...
90
			$stmt->execute($values);
91
			$row = $stmt->fetch();
92
93
			if ($row !== false) {
94
				return $this->mapModel($row);
95
			}
96
		} catch (PDOException $e) {
97
			throw new DbException('Ocorreu um erro ao ler no banco de dados.', 500, $e);
98
		}
99
	}
100
101
	/**
102
	 * Retorna o primeiro resultado da busca ou redireciona para Página 404
103
	 */
104
	public function oneOr404()
105
	{
106
		$model = $this->one();
107
		if (is_null($model)) {
108
			throw new HttpException('O registro não foi encontrado no banco de dados.', 404);
109
		}
110
111
		return $model;
112
	}
113
114
	/**
115
	 * Retorna o registro pela PK
116
	 * @param int $id
117
	 */
118
	public function find($id)
119
	{
120
		return $this->if($this->pk, $id)->one();
121
	}
122
123
	/**
124
	 * Retorna o registro pela PK ou redireciona para Página 404
125
	 * @param int $id
126
	 */
127
	public function findOr404($id)
128
	{
129
		return $this->if($this->pk, $id)->oneOr404();
130
	}
131
132
	/**
133
	 * Retorna o todos os resultados da busca
134
	 */
135
	public function list()
136
	{
137
		try {
138
			$this->setLimit();
139
			$query = $this->sql->select();
140
			$values = $this->sql->values();
141
			$this->flush();
142
143
			$stmt = $this->pdo->prepare($query);
144
			$stmt->execute($values);
145
			return array_map([$this, 'mapModel'], $stmt->fetchAll());
146
		} catch (PDOException $e) {
147
			throw new DbException('Ocorreu um erro ao ler no banco de dados.', 500, $e);
148
		}
149
	}
150
151
	protected function setLimit()
152
	{
153
		$pagination = $this->pagination;
154
155
		if ($pagination->pageSize > 0) {
156
			$query = $this->sql->selectCount();
157
			$values = $this->sql->values();
158
159
			$stmt = $this->pdo->prepare($query);
160
			$stmt->execute($values);
161
			$pagination->count = $stmt->fetchColumn();
162
			$this->sql->setLimit($pagination->offset(), $pagination->pageSize);
163
		}
164
	}
165
166
	/**
167
	 * Retorna o total de resultados da busca
168
	 * @return int
169
	 */
170
	public function count()
171
	{
172
		try {
173
			$query = $this->sql->selectCount();
174
			$values = $this->sql->values();
175
			$this->flush();
176
177
			$stmt = $this->pdo->prepare($query);
178
			$stmt->execute($values);
179
			return (int) $stmt->fetchColumn();
180
		} catch (PDOException $e) {
181
			throw new DbException('Ocorreu um erro ao ler no banco de dados.', 500, $e);
182
		}
183
	}
184
185
	/**
186
	 * Define um limite das buscas com list()
187
	 * @param int $pageSize
188
	 * @param int $currentPage
189
	 */
190
	public function paginate($pageSize, $currentPage = 1)
191
	{
192
		$this->pagination->pageSize = $pageSize;
193
		$this->pagination->current = max($currentPage, 1);
194
		return $this;
195
	}
196
197
	/**
198
	 * Remove todos os registros da busca
199
	 */
200
	public function delete()
201
	{
202
		try {
203
			$query = $this->sql->delete();
204
			$values = $this->sql->values();
205
			$this->flush();
206
207
			$this->pdo->prepare($query)->execute($values);
208
		} catch (PDOException $e) {
209
			throw new DbException('Ocorreu um erro ao escrever no banco de dados.', 500, $e);
210
		}
211
	}
212
213
	/**
214
	 * Remove o registro pela PK
215
	 * @param int $id
216
	 */
217
	public function destroy($id)
218
	{
219
		$this->if($this->pk, $id)->delete();
220
	}
221
222
	/**
223
	 * Salva o registro no banco
224
	 * @param Model $model
225
	 */
226
	public function save(Model $model)
227
	{
228
		try {
229
			$this->sql->setValues($this->mapRow($model));
230
			$query = $this->querySave($model);
231
			$values = array_values($this->sql->values());
232
			$this->flush();
233
234
			$this->pdo->prepare($query)->execute($values);
235
			$model->id = $model->id ?? $this->pdo->lastInsertId();
236
			return $this;
237
		} catch (PDOException $e) {
238
			throw new DbException('Ocorreu um erro ao escrever no banco de dados.', 500, $e);
239
		}
240
	}
241
242
	/**
243
	 * @param Model $model
244
	 * @return string
245
	 */
246
	protected function querySave($model)
247
	{
248
		if ($this->exists($model->id)) {
249
			$this->if($this->pk, $model->id);
250
			return $this->sql->update();
251
		} else {
252
			return $this->sql->insert();
253
		}
254
	}
255
256
	/**
257
	 * Atualiza somente as colunas informadas
258
	 * @param mixed[] $columns
259
	 * @return int
260
	 */
261
	public function update($columns)
262
	{
263
		try {
264
			$this->sql->setValues($columns);
265
			$query = $this->sql->update();
266
			$values = array_values($this->sql->values());
267
			$this->flush();
268
269
			$stmt = $this->pdo->prepare($query);
270
			$stmt->execute($values);
271
			return $stmt ? $stmt->rowCount() : null;
272
		} catch (PDOException $e) {
273
			throw new DbException('Ocorreu um erro ao escrever no banco de dados.', 500, $e);
274
		}
275
	}
276
277
	/**
278
	 * Remove todos os filtros, ordenação, etc
279
	 */
280
	public function flush()
281
	{
282
		$this->sql->__construct($this->table);
283
284
		return $this;
285
	}
286
287
	/**
288
	 * Retorna TRUE se o model existir no banco
289
	 * @param int $id
290
	 * @return bool
291
	 */
292
	protected function exists($id)
293
	{
294
		$orm = new static();
295
		$orm->pdo = $this->pdo;
296
		$orm->table = $this->table;
297
		return $orm->if($this->pk, $id)->count() > 0;
298
	}
299
300
	/**
301
	 * Adiciona (inner) join
302
	 * @param string $query
303
	 * @param array $values
304
	 */
305
	public function join($query, ...$values)
306
	{
307
		$this->sql->addJoin('JOIN ' . $query, $values);
308
309
		return $this;
310
	}
311
312
	/**
313
	 * Adiciona left join
314
	 * @param string $query
315
	 * @param array $values
316
	 */
317
	public function leftJoin($query, ...$values)
318
	{
319
		$this->sql->addJoin('LEFT JOIN ' . $query, $values);
320
321
		return $this;
322
	}
323
324
	/**
325
	 * Adiciona right join
326
	 * @param string $query
327
	 * @param array $values
328
	 */
329
	public function rightJoin($query, ...$values)
330
	{
331
		$this->sql->addJoin('RIGHT JOIN ' . $query, $values);
332
333
		return $this;
334
	}
335
336
	/**
337
	 * Aplica filtros
338
	 * @param string $comparator
339
	 * @param mixed $values
340
	 * @example if('name', 'John')
341
	 * @example if('name = ? OR email = ?', 'John', 'email')
342
	 */
343
	public function if($comparator, ...$values)
344
	{
345
		$this->sql->addWhere($comparator, $values);
346
347
		return $this;
348
	}
349
350
	/**
351
	 * Aplica filtros (aceita bind params)
352
	 * @param string $query
353
	 * @param mixed[] $values
354
	 * @example filter('name = :name OR age > 0', [':name' => 'John'])
355
	 */
356
	public function filter($query, $values = [])
357
	{
358
		$this->sql->addWhere($query, $values);
359
360
		return $this;
361
	}
362
363
	/**
364
	 * Ordem por um campo
365
	 * @param string $rule
366
	 * @param int $priority
367
	 */
368
	public function sort($rule, $priority = 0)
369
	{
370
		$this->sql->addOrderBy($rule, $priority);
371
372
		return $this;
373
	}
374
375
	/**
376
	 * Exibe a query e os valores para debug
377
	 * @param string $method
378
	 * @return array<string,mixed>
379
	 */
380
	public function debug($method = 'select')
381
	{
382
		return [$this->sql->$method(), ...$this->sql->values()];
383
	}
384
}
385