Passed
Branch v1.6.0 (c77ef3)
by Wanderson
01:54
created

Repository::exists()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 5
rs 10
1
<?php
2
3
namespace Win\Repositories\Database;
4
5
use PDO;
6
use PDOException;
7
use Win\Application;
8
use Win\Common\Pagination;
9
use Win\Common\Traits\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(Pagination $pagination)
41
	{
42
		$this->pdo = Application::app()->pdo;
43
		$this->sql = new Sql($this->table);
44
		$this->pagination = $pagination;
45
	}
46
47
	/**
48
	 * Define a tabela manualmente
49
	 * @param string $table
50
	 */
51
	public function from($table)
52
	{
53
		$this->table = $table;
54
		return $this;
55
	}
56
57
	/**
58
	 * Retorna o primeiro resultado da busca
59
	 * @return Model
60
	 */
61
	public function one()
62
	{
63
		$this->sql->setLimit(0, 1);
64
		$query = $this->sql->select();
65
		$values = $this->sql->values();
66
		$this->flush();
67
68
		$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

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