Passed
Branch v1.5.1 (efd65f)
by Wanderson
02:00
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
	/** @var string */
16
	const TABLE = '';
17
18
	/** @var string */
19
	const PK = 'id';
20
21
	/** @var Connection */
22
	public $conn;
23
24
	/** @var Pagination */
25
	public $pagination;
26
27
	/** @var Sql */
28
	protected $sql;
29
30
	/**
31
	 * @param Model $model
32
	 * @return mixed[]
33
	 */
34
	abstract public static function mapRow($model);
35
36
	abstract public static function mapModel($row);
37
38
	/**
39
	 * @param Connection $connection
40
	 */
41
	public function __construct($connection = null)
42
	{
43
		$this->conn = $connection ?: Application::app()->conn;
44
		$this->sql = new Sql(static::TABLE);
45
	}
46
47
	/**
48
	 * Executa SQL
49
	 * @param string $query
50
	 * @param mixed[]
51
	 */
52
	protected function execute($query, ...$values)
53
	{
54
		return $this->conn->execute($query, $values);
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
65
		$query = $this->sql->select();
66
		$values = $this->sql->values();
67
		$row = $this->conn->fetch($query, $values);
68
		$this->flush();
69
70
		return $this->mapModel($row);
71
	}
72
73
	/**
74
	 * Retorna o primeiro resultado da busca ou redireciona para Página 404
75
	 */
76
	public function oneOr404()
77
	{
78
		$model = $this->one();
79
		if (is_null($model->id)) {
80
			throw new HttpException('Model not found', 404);
81
		}
82
83
		return $model;
84
	}
85
86
	/**
87
	 * Retorna o registro pela PK
88
	 * @param int $id
89
	 */
90
	public function find($id)
91
	{
92
		return $this->filter(static::PK, $id)->one();
93
	}
94
95
	/**
96
	 * Retorna o registro pela PK ou redireciona para Página 404
97
	 * @param int $id
98
	 */
99
	public function findOr404($id)
100
	{
101
		return $this->filter(static::PK, $id)->oneOr404();
102
	}
103
104
	/**
105
	 * Retorna o todos os resultados da busca
106
	 */
107
	public function list()
108
	{
109
		if ($this->pagination) {
110
			$this->setLimit();
111
		}
112
113
		$query = $this->sql->select();
114
		$values = $this->sql->values();
115
		$rows = $this->conn->fetchAll($query, $values);
116
		$this->flush();
117
118
		return array_map([$this, 'mapModel'], $rows);
119
	}
120
121
	/**
122
	 * Retorna o total de resultados da busca
123
	 * @return int
124
	 */
125
	public function count()
126
	{
127
		$query = $this->sql->selectCount();
128
		$values = $this->sql->values();
129
130
		$count = $this->conn->fetchCount($query, $values);
131
		$this->flush();
132
133
		return $count;
134
	}
135
136
	/**
137
	 * Remove todos os registros da busca
138
	 */
139
	public function delete()
140
	{
141
		$query = $this->sql->delete();
142
		$values = $this->sql->values();
143
		$this->conn->execute($query, $values);
144
		$this->flush();
145
	}
146
147
	/**
148
	 * Remove o registro pela PK
149
	 * @param int $id
150
	 */
151
	public function destroy($id)
152
	{
153
		$this->filter(static::PK, $id);
154
155
		$query = $this->sql->delete();
156
		$values = $this->sql->values();
157
		$this->conn->execute($query, $values);
158
		$this->flush();
159
	}
160
161
	/**
162
	 * Salva o registro no banco
163
	 * @param Model $model
164
	 */
165
	public function save(Model $model)
166
	{
167
		$model->validate();
168
		$this->sql = new Sql(static::TABLE, $this->mapRow($model));
169
170
		if ($this->exists($model->id)) {
171
			$this->filter(static::PK, $model->id);
172
			$query = $this->sql->update();
173
		} else {
174
			$query = $this->sql->insert();
175
		}
176
		$values = array_values($this->sql->values());
177
		$this->conn->execute($query, $values);
178
		$model->id = $model->id ?? $this->conn->lastInsertId();
179
180
		$this->flush();
181
		return $model;
182
	}
183
184
	/**
185
	 * Atualiza somente as colunas informadas
186
	 * @param mixed[] $columns
187
	 * @return int
188
	 */
189
	public function update($columns)
190
	{
191
		$this->sql->setValues($columns);
192
		$query = $this->sql->update();
193
		$values = array_values($this->sql->values());
194
		$stmt = $this->conn->stmt($query, $values);
195
		$this->flush();
196
197
		return $stmt->rowCount();
198
	}
199
200
	/**
201
	 * Remove todos os filtros e ordenação
202
	 */
203
	public function flush()
204
	{
205
		$this->sql = new Sql(static::TABLE);
206
207
		return $this;
208
	}
209
210
	/**
211
	 * Retorna TRUE se o model existir no banco
212
	 * @param int $id
213
	 * @return bool
214
	 */
215
	protected function exists($id)
216
	{
217
		$orm = new static($this->conn);
218
		return $orm->filter(static::PK, $id)->count() > 0;
219
	}
220
221
	/**
222
	 * Define as colunas da busca
223
	 * @param string $columns
224
	 */
225
	public function select(...$columns)
226
	{
227
		$this->sql->columns = $columns;
228
229
		return $this;
230
	}
231
232
	/**
233
	 * Adiciona (inner) join
234
	 * @param string $query
235
	 */
236
	public function join($query)
237
	{
238
		$this->sql->addJoin('JOIN ' . $query);
239
240
		return $this;
241
	}
242
243
	/**
244
	 * Adiciona left join
245
	 * @param string $query
246
	 */
247
	public function leftJoin($query)
248
	{
249
		$this->sql->addJoin('LEFT JOIN ' . $query);
250
251
		return $this;
252
	}
253
254
	/**
255
	 * Adiciona right join
256
	 * @param string $query
257
	 */
258
	public function rightJoin($query)
259
	{
260
		$this->sql->addJoin('RIGHT JOIN ' . $query);
261
262
		return $this;
263
	}
264
265
	/**
266
	 * Aplica filtros
267
	 * @param string $comparator
268
	 * @param mixed $values
269
	 * @example filter('name', 'John')
270
	 * @example filter('name = ? OR email = ?', 'John', 'email')
271
	 */
272
	public function filter($comparator, ...$values)
273
	{
274
		$this->sql->addWhere($comparator, $values);
275
276
		return $this;
277
	}
278
279
	/**
280
	 * Aplica filtros (aceita bind params)
281
	 * @param string $query
282
	 * @param mixed[] $values
283
	 * @example filterBy('name = :name OR age > 0', [':name' => 'John'])
284
	 */
285
	public function filterBy($query, $values = [])
286
	{
287
		$this->sql->addWhere($query, $values);
288
289
		return $this;
290
	}
291
292
	/**
293
	 * Ordem por um campo
294
	 * @param string $rule
295
	 * @param int $priority
296
	 */
297
	public function sort($rule, $priority = 0)
298
	{
299
		$this->sql->addOrderBy($rule, $priority);
300
301
		return $this;
302
	}
303
304
	/**
305
	 * Define um limite das buscas com list()
306
	 * @param int $pageSize
307
	 * @param int $currentPage
308
	 */
309
	public function paginate($pageSize, $currentPage = 1)
310
	{
311
		$this->pagination = new Pagination($pageSize, $currentPage);
312
		return $this;
313
	}
314
315
	/**
316
	 * Define o limit com base na paginação
317
	 */
318
	private function setLimit()
319
	{
320
		$query = $this->sql->selectCount();
321
		$values = $this->sql->values();
322
		$count = $this->conn->fetchCount($query, $values);
323
324
		if ($count) {
325
			$this->pagination->setCount($count);
326
			$this->sql->setLimit($this->pagination->offset, $this->pagination->pageSize);
327
		}
328
	}
329
330
	/**
331
	 * Exibe a query e os valores para debug
332
	 * @param string $method
333
	 * @return array<string,mixed>
334
	 */
335
	public function debug($method = 'select')
336
	{
337
		return [$this->sql->$method(), $this->sql->values()];
338
	}
339
}
340