Passed
Pull Request — master (#22)
by Wanderson
02:51
created

Repository::setTable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 4
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()
41
	{
42
		$this->pdo = Application::app()->pdo;
43
		$this->sql = new Sql($this->table);
44
		$this->pagination = new Pagination();
45
	}
46
47
	/**
48
	 * Define a tabela manualmente
49
	 * @param string $table
50
	 */
51
	public function setTable($table)
52
	{
53
		$this->table = $table;
54
		return $this;
55
	}
56
57
	public function getTable()
58
	{
59
		return $this->table;
60
	}
61
62
	/**
63
	 * Define as colunas da busca
64
	 * @param string $columns
65
	 */
66
	public function select($columns)
67
	{
68
		$this->sql->columns[] = $columns;
69
70
		return $this;
71
	}
72
73
	/**
74
	 * Retorna o primeiro resultado da busca
75
	 * @return Model
76
	 */
77
	public function one()
78
	{
79
		$this->sql->setLimit(0, 1);
80
		$query = $this->sql->select();
81
		$values = $this->sql->values();
82
		$this->flush();
83
84
		$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

84
		/** @scrutinizer ignore-call */ 
85
  $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...
85
		$stmt->execute($values);
86
		$row = $stmt->fetch();
87
88
		if ($row !== false) {
89
			return $this->mapModel($row);
90
		}
91
	}
92
93
	/**
94
	 * Retorna o primeiro resultado da busca ou redireciona para Página 404
95
	 */
96
	public function oneOr404()
97
	{
98
		$model = $this->one();
99
		if (is_null($model)) {
100
			throw new HttpException('O registro não foi encontrado no banco de dados.', 404);
101
		}
102
103
		return $model;
104
	}
105
106
	/**
107
	 * Retorna o registro pela PK
108
	 * @param int $id
109
	 */
110
	public function find($id)
111
	{
112
		return $this->if($this->pk, $id)->one();
113
	}
114
115
	/**
116
	 * Retorna o registro pela PK ou redireciona para Página 404
117
	 * @param int $id
118
	 */
119
	public function findOr404($id)
120
	{
121
		return $this->if($this->pk, $id)->oneOr404();
122
	}
123
124
	/**
125
	 * Retorna o todos os resultados da busca
126
	 */
127
	public function list()
128
	{
129
		try {
130
			$this->setLimit();
131
			$query = $this->sql->select();
132
			$values = $this->sql->values();
133
			$this->flush();
134
135
			$stmt = $this->pdo->prepare($query);
136
			$stmt->execute($values);
137
			return array_map([$this, 'mapModel'], $stmt->fetchAll());
138
		} catch (PDOException $e) {
139
			throw new DbException('Ocorreu um erro ao ler no banco de dados.', 500, $e);
140
		}
141
	}
142
143
	protected function setLimit()
144
	{
145
		$pagination = $this->pagination;
146
147
		if ($pagination->pageSize > 0) {
148
			$query = $this->sql->selectCount();
149
			$values = $this->sql->values();
150
151
			$stmt = $this->pdo->prepare($query);
152
			$stmt->execute($values);
153
			$pagination->count = $stmt->fetchColumn();
154
			$this->sql->setLimit($pagination->offset(), $pagination->pageSize);
155
		}
156
	}
157
158
	/**
159
	 * Retorna o total de resultados da busca
160
	 * @return int
161
	 */
162
	public function count()
163
	{
164
		try {
165
			$query = $this->sql->selectCount();
166
			$values = $this->sql->values();
167
			$this->flush();
168
169
			$stmt = $this->pdo->prepare($query);
170
			$stmt->execute($values);
171
			return (int) $stmt->fetchColumn();
172
		} catch (PDOException $e) {
173
			throw new DbException('Ocorreu um erro ao ler no banco de dados.', 500, $e);
174
		}
175
	}
176
177
	/**
178
	 * Define um limite das buscas com list()
179
	 * @param int $pageSize
180
	 * @param int $currentPage
181
	 */
182
	public function paginate($pageSize, $currentPage = 1)
183
	{
184
		$this->pagination->pageSize = $pageSize;
185
		$this->pagination->current = max($currentPage, 1);
186
		return $this;
187
	}
188
189
	/**
190
	 * Remove todos os registros da busca
191
	 */
192
	public function delete()
193
	{
194
		try {
195
			$query = $this->sql->delete();
196
			$values = $this->sql->values();
197
			$this->flush();
198
199
			$this->pdo->prepare($query)->execute($values);
200
		} catch (PDOException $e) {
201
			throw new DbException('Ocorreu um erro ao escrever no banco de dados.', 500, $e);
202
		}
203
	}
204
205
	/**
206
	 * Remove o registro pela PK
207
	 * @param int $id
208
	 */
209
	public function destroy($id)
210
	{
211
		$this->if($this->pk, $id)->delete();
212
	}
213
214
	/**
215
	 * Salva o registro no banco
216
	 * @param Model $model
217
	 */
218
	public function save(Model $model)
219
	{
220
		try {
221
			$this->sql->setValues($this->mapRow($model));
222
			$query = $this->querySave($model);
223
			$values = array_values($this->sql->values());
224
			$this->flush();
225
226
			$this->pdo->prepare($query)->execute($values);
227
			$model->id = $model->id ?? $this->pdo->lastInsertId();
228
			return $this;
229
		} catch (PDOException $e) {
230
			throw new DbException('Ocorreu um erro ao escrever no banco de dados.', 500, $e);
231
		}
232
	}
233
234
	/**
235
	 * @param Model $model
236
	 * @return string
237
	 */
238
	protected function querySave($model)
239
	{
240
		if ($this->exists($model->id)) {
241
			$this->if($this->pk, $model->id);
242
			return $this->sql->update();
243
		} else {
244
			return $this->sql->insert();
245
		}
246
	}
247
248
	/**
249
	 * Atualiza somente as colunas informadas
250
	 * @param mixed[] $columns
251
	 * @return int
252
	 */
253
	public function update($columns)
254
	{
255
		try {
256
			$this->sql->setValues($columns);
257
			$query = $this->sql->update();
258
			$values = array_values($this->sql->values());
259
			$this->flush();
260
261
			$stmt = $this->pdo->prepare($query);
262
			$stmt->execute($values);
263
			return $stmt ? $stmt->rowCount() : null;
264
		} catch (PDOException $e) {
265
			throw new DbException('Ocorreu um erro ao escrever no banco de dados.', 500, $e);
266
		}
267
	}
268
269
	/**
270
	 * Remove todos os filtros, ordenação, etc
271
	 */
272
	public function flush()
273
	{
274
		$this->sql->__construct($this->table);
275
276
		return $this;
277
	}
278
279
	/**
280
	 * Retorna TRUE se o model existir no banco
281
	 * @param int $id
282
	 * @return bool
283
	 */
284
	protected function exists($id)
285
	{
286
		$orm = new static($this->pagination);
0 ignored issues
show
Unused Code introduced by
The call to Win\Repositories\Databas...pository::__construct() has too many arguments starting with $this->pagination. ( Ignorable by Annotation )

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

286
		$orm = /** @scrutinizer ignore-call */ new static($this->pagination);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
287
		$orm->pdo = $this->pdo;
288
		$orm->table = $this->table;
289
		return $orm->if($this->pk, $id)->count() > 0;
290
	}
291
292
	/**
293
	 * Adiciona (inner) join
294
	 * @param string $query
295
	 * @param array $values
296
	 */
297
	public function join($query, ...$values)
298
	{
299
		$this->sql->addJoin('JOIN ' . $query, $values);
300
301
		return $this;
302
	}
303
304
	/**
305
	 * Adiciona left join
306
	 * @param string $query
307
	 * @param array $values
308
	 */
309
	public function leftJoin($query, ...$values)
310
	{
311
		$this->sql->addJoin('LEFT JOIN ' . $query, $values);
312
313
		return $this;
314
	}
315
316
	/**
317
	 * Adiciona right join
318
	 * @param string $query
319
	 * @param array $values
320
	 */
321
	public function rightJoin($query, ...$values)
322
	{
323
		$this->sql->addJoin('RIGHT JOIN ' . $query, $values);
324
325
		return $this;
326
	}
327
328
	/**
329
	 * Aplica filtros
330
	 * @param string $comparator
331
	 * @param mixed $values
332
	 * @example if('name', 'John')
333
	 * @example if('name = ? OR email = ?', 'John', 'email')
334
	 */
335
	public function if($comparator, ...$values)
336
	{
337
		$this->sql->addWhere($comparator, $values);
338
339
		return $this;
340
	}
341
342
	/**
343
	 * Aplica filtros (aceita bind params)
344
	 * @param string $query
345
	 * @param mixed[] $values
346
	 * @example filter('name = :name OR age > 0', [':name' => 'John'])
347
	 */
348
	public function filter($query, $values = [])
349
	{
350
		$this->sql->addWhere($query, $values);
351
352
		return $this;
353
	}
354
355
	/**
356
	 * Ordem por um campo
357
	 * @param string $rule
358
	 * @param int $priority
359
	 */
360
	public function sort($rule, $priority = 0)
361
	{
362
		$this->sql->addOrderBy($rule, $priority);
363
364
		return $this;
365
	}
366
367
	/**
368
	 * Exibe a query e os valores para debug
369
	 * @param string $method
370
	 * @return array<string,mixed>
371
	 */
372
	public function debug($method = 'select')
373
	{
374
		return [$this->sql->$method(), ...$this->sql->values()];
375
	}
376
}
377