Passed
Pull Request — master (#21)
by Wanderson
03:27
created

Repository::update()   A

Complexity

Conditions 3
Paths 9

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 10
c 1
b 0
f 0
nc 9
nop 1
dl 0
loc 13
rs 9.9332
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
	 * Define as colunas da busca
59
	 * @param string $columns
60
	 */
61
	public function select($columns)
62
	{
63
		$this->sql->columns[] = $columns;
64
65
		return $this;
66
	}
67
68
	/**
69
	 * Retorna o primeiro resultado da busca
70
	 * @return Model
71
	 */
72
	public function one()
73
	{
74
		$this->sql->setLimit(0, 1);
75
		$query = $this->sql->select();
76
		$values = $this->sql->values();
77
		$this->flush();
78
79
		$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

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