您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

308 行
7.1KB

  1. <?php
  2. namespace Framework\Database\QueryBuilder;
  3. use Framework\Database\Connection\Connection;
  4. use Framework\Database\Exception\QueryException;
  5. use Pdo;
  6. use PdoStatement;
  7. abstract class QueryBuilder
  8. {
  9. protected string $type;
  10. protected array $columns;
  11. protected string $table;
  12. protected int $limit;
  13. protected int $offset;
  14. protected array $values;
  15. protected array $wheres = [];
  16. /**
  17. * Fetch all rows matching the current query
  18. */
  19. public function all(): array
  20. {
  21. if (!isset($this->type)) {
  22. $this->select();
  23. }
  24. $statement = $this->prepare();
  25. $statement->execute($this->getWhereValues());
  26. return $statement->fetchAll(Pdo::FETCH_ASSOC);
  27. }
  28. /**
  29. * Get the values for the where clause placeholders
  30. */
  31. protected function getWhereValues(): array
  32. {
  33. $values = [];
  34. if (count($this->wheres) === 0) {
  35. return $values;
  36. }
  37. foreach ($this->wheres as $where) {
  38. if (is_bool($where[2])) {
  39. $values[$where[0]] = (int) $where[2];
  40. continue;
  41. }
  42. $values[$where[0]] = $where[2];
  43. }
  44. return $values;
  45. }
  46. /**
  47. * Prepare a query against a particular connection
  48. */
  49. public function prepare(): PdoStatement
  50. {
  51. $query = '';
  52. if ($this->type === 'select') {
  53. $query = $this->compileSelect($query);
  54. $query = $this->compileWheres($query);
  55. $query = $this->compileLimit($query);
  56. }
  57. if ($this->type === 'insert') {
  58. $query = $this->compileInsert($query);
  59. }
  60. if ($this->type === 'update') {
  61. $query = $this->compileUpdate($query);
  62. $query = $this->compileWheres($query);
  63. }
  64. if ($this->type === 'delete') {
  65. $query = $this->compileDelete($query);
  66. $query = $this->compileWheres($query);
  67. }
  68. if (empty($query)) {
  69. throw new QueryException('Unrecognised query type');
  70. }
  71. return $this->connection->pdo()->prepare($query);
  72. }
  73. /**
  74. * Add select clause to the query
  75. */
  76. protected function compileSelect(string $query): string
  77. {
  78. $joinedColumns = join(', ', $this->columns);
  79. $query .= " SELECT {$joinedColumns} FROM {$this->table}";
  80. return $query;
  81. }
  82. /**
  83. * Add limit and offset clauses to the query
  84. */
  85. protected function compileLimit(string $query): string
  86. {
  87. if (isset($this->limit)) {
  88. $query .= " LIMIT {$this->limit}";
  89. }
  90. if (isset($this->offset)) {
  91. $query .= " OFFSET {$this->offset}";
  92. }
  93. return $query;
  94. }
  95. /**
  96. * Add where clauses to the query
  97. */
  98. protected function compileWheres(string $query): string
  99. {
  100. if (count($this->wheres) === 0) {
  101. return $query;
  102. }
  103. $query .= ' WHERE';
  104. foreach ($this->wheres as $i => $where) {
  105. if ($i > 0) {
  106. $query .= ' AND ';
  107. }
  108. [$column, $comparator, $value] = $where;
  109. $query .= " {$column} {$comparator} :{$column}";
  110. }
  111. return $query;
  112. }
  113. /**
  114. * Add insert clause to the query
  115. */
  116. protected function compileInsert(string $query): string
  117. {
  118. $joinedColumns = join(', ', $this->columns);
  119. $joinedPlaceholders = join(', ', array_map(fn($column) => ":{$column}", $this->columns));
  120. $query .= " INSERT INTO {$this->table} ({$joinedColumns}) VALUES ({$joinedPlaceholders})";
  121. return $query;
  122. }
  123. /**
  124. * Add update clause to the query
  125. */
  126. protected function compileUpdate(string $query): string
  127. {
  128. $joinedColumns = '';
  129. foreach ($this->columns as $i => $column) {
  130. if ($i > 0) {
  131. $joinedColumns .= ', ';
  132. }
  133. $joinedColumns = " {$column} = :{$column}";
  134. }
  135. $query .= " UPDATE {$this->table} SET {$joinedColumns}";
  136. return $query;
  137. }
  138. /**
  139. * Add delete clause to the query
  140. */
  141. protected function compileDelete(string $query): string
  142. {
  143. $query .= " DELETE FROM {$this->table}";
  144. return $query;
  145. }
  146. /**
  147. * Fetch the first row matching the current query
  148. */
  149. public function first(): ?array
  150. {
  151. if (!isset($this->type)) {
  152. $this->select();
  153. }
  154. $statement = $this->take(1)->prepare();
  155. $statement->execute($this->getWhereValues());
  156. $result = $statement->fetchAll(Pdo::FETCH_ASSOC);
  157. if (count($result) === 1) {
  158. return $result[0];
  159. }
  160. return null;
  161. }
  162. /**
  163. * Limit a set of query results so that it's possible
  164. * to fetch a single or limited batch of rows
  165. */
  166. public function take(int $limit, int $offset = 0): static
  167. {
  168. $this->limit = $limit;
  169. $this->offset = $offset;
  170. return $this;
  171. }
  172. /**
  173. * Indicate which table the query is targetting
  174. */
  175. public function from(string $table): static
  176. {
  177. $this->table = $table;
  178. return $this;
  179. }
  180. /**
  181. * Indicate the query type is a "select" and remember
  182. * which fields should be returned by the query
  183. */
  184. public function select(mixed $columns = '*'): static
  185. {
  186. if (is_string($columns)) {
  187. $columns = [$columns];
  188. }
  189. $this->type = 'select';
  190. $this->columns = $columns;
  191. return $this;
  192. }
  193. /**
  194. * Insert a row of data into the table specified in the query
  195. * and return the number of affected rows
  196. */
  197. public function insert(array $columns, array $values): int
  198. {
  199. $this->type = 'insert';
  200. $this->columns = $columns;
  201. $this->values = $values;
  202. $statement = $this->prepare();
  203. return $statement->execute($values);
  204. }
  205. /**
  206. * Store where clause data for later queries
  207. */
  208. public function where(string $column, mixed $comparator, mixed $value = null): static
  209. {
  210. if (is_null($value) && !is_null($comparator)) {
  211. array_push($this->wheres, [$column, '=', $comparator]);
  212. } else {
  213. array_push($this->wheres, [$column, $comparator, $value]);
  214. }
  215. return $this;
  216. }
  217. /**
  218. * Insert a row of data into the table specified in the query
  219. * and return the number of affected rows
  220. */
  221. public function update(array $columns, array $values): int
  222. {
  223. $this->type = 'update';
  224. $this->columns = $columns;
  225. $this->values = $values;
  226. $statement = $this->prepare();
  227. return $statement->execute($this->getWhereValues() + $values);
  228. }
  229. /**
  230. * Get the ID of the last row that was inserted
  231. */
  232. public function getLastInsertId(): string
  233. {
  234. return $this->connection->pdo()->lastInsertId();
  235. }
  236. /**
  237. * Delete a row from the database
  238. */
  239. public function delete(): int
  240. {
  241. $this->type = 'delete';
  242. $statement = $this->prepare();
  243. return $statement->execute($this->getWhereValues());
  244. }
  245. }

Powered by TurnKey Linux.